example - Spring Boot OAuth 2.0 UserDetails usuario no encontrado
spring boot oauth2 jdbc (2)
Enfrenté el mismo problema y pasé horas investigando el caso. Como solución alternativa, si está utilizando Spring Boot versión 1.1.8.RELEASE, descárguelo a 1.0.2.RELEASE. Las cosas salieron bien de esa manera, pero aún no investigué las razones del problema de compatibilidad con Spring Boot versión 1.1.8.RELEASE.
Soy nuevo en Spring Boot, y estoy tratando de configurar OAuth 2.0. El problema que tengo en este momento es que sigo recibiendo el siguiente mensaje cuando intento solicitar un token de acceso:
{"error": "invalid_grant", "error_description": "credenciales incorrectas"}
El mensaje de error en la consola Spring Boot indica que no se puede encontrar al usuario.
: Intento de autenticación utilizando org.springframework.security.authentication.dao.DaoAuthenticationProvider: User ''stromero'' no encontrado: devolución de instancia en caché de singleton bean ''authenticationAuditListener''
Implementé un usuario personalizado que ya se ha guardado en una base de datos usando JPA, no entiendo por qué Spring Security no puede encontrar a este usuario, puede ser un problema con mi lógica o configuración. Si alguien con más experiencia puede mirar mi código y quizás guiarme en la dirección correcta, eso sería muy apreciado.
Esta es la solicitud HTTP:
POST / oauth / token HTTP / 1.1 Host: localhost: 8181 Autorización: Basic YnJvd3NlcjpzZWNyZXQ = Cache-Control: no-cache Content-Type: application / x-www-form-urlencoded username = stromero & password = password & client_id = browser & client_secret = secret & grant_type = contraseña
Estas son las clases que utilicé para implementar mi usuario personalizado y OAuth 2.0
@Repository
public interface UserRepository extends CrudRepository<CustomUser, String> {
public CustomUser findByUsername(String name);
}
Debajo está el usuario personalizado que he creado
@Entity
@Table (name = "custom_user")
public class CustomUser {
@Id
@Column(name = "id", nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "username", unique=true, nullable = false)
private String username;
@Column(name = "password", nullable = false)
private String password;
@ElementCollection
private List<String> roles = new ArrayList<>();
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
A continuación se muestra un servicio personalizado que lee la información del usuario de la base de datos y la devuelve como un objeto UserDetails.
@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
CustomUser customUser = userRepository.findByUsername(s);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
customUser .getUsername(),
customUser .getPassword().toLowerCase(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(customUser.getRoles()));
}
public Collection<? extends GrantedAuthority> getAuthorities(List<String> roles) {
List<GrantedAuthority> authList = getGrantedAuthorities(roles);
return authList;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
La siguiente clase es una estructura de datos que contiene los servicios UserDetailsService y ClientDetailsService
public class ClientAndUserDetailsService implements UserDetailsService,
ClientDetailsService {
private final ClientDetailsService clients;
private final UserDetailsService users;
private final ClientDetailsUserDetailsService clientDetailsWrapper;
public ClientAndUserDetailsService(ClientDetailsService clients,
UserDetailsService users) {
super();
this.clients = clients;
this.users = users;
clientDetailsWrapper = new ClientDetailsUserDetailsService(this.clients);
}
@Override
public ClientDetails loadClientByClientId(String clientId)
throws ClientRegistrationException {
return clients.loadClientByClientId(clientId);
}
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserDetails user = null;
try{
user = users.loadUserByUsername(username);
}catch(UsernameNotFoundException e){
user = clientDetailsWrapper.loadUserByUsername(username);
}
return user;
}
}
La clase a continuación es mi configuración para OAuth 2.0 usando Spring Boot
@Configuration
public class OAuth2SecurityConfiguration {
@Configuration
@EnableWebSecurity
protected static class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
protected void registerAuthentication(
final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
@Configuration
@EnableResourceServer
protected static class ResourceServer extends
ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/oauth/token").anonymous();
// Require all GET requests to have client "read" scope
http.authorizeRequests().antMatchers(HttpMethod.GET, "/**")
.access("#oauth2.hasScope(''read'')");
// Require all POST requests to have client "write" scope
http.authorizeRequests().antMatchers(HttpMethod.POST,"/**")
.access("#oauth2.hasScope(''write'')");
}
}
@Configuration
@EnableAuthorizationServer
@Order(Ordered.LOWEST_PRECEDENCE - 100)
protected static class AuthorizationServer extends
AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
private ClientAndUserDetailsService combinedService;
public AuthorizationServer() throws Exception {
ClientDetailsService clientDetailsService = new InMemoryClientDetailsServiceBuilder()
.withClient("browser")
.secret("secret")
.authorizedGrantTypes("password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read","write")
.resourceIds("message")
.accessTokenValiditySeconds(7200)
.and()
.build();
// Create a series of hard-coded users.
UserDetailsService userDetailsService = new CustomUserDetailsService();
combinedService = new ClientAndUserDetailsService(clientDetailsService, userDetailsService);
}
@Bean
public ClientDetailsService clientDetailsService() throws Exception {
return combinedService;
}
@Bean
public UserDetailsService userDetailsService() {
return combinedService;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.withClientDetails(clientDetailsService());
}
}
}
A continuación está mi archivo pom.xml
<properties>
<tomcat.version>8.0.8</tomcat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Postgres JDBC Driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.2-1002-jdbc4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Hibernate validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
</dependencies>
Sí, tenía el mismo problema ... quería usar UserDetailsService
de JPA, pero el mismo problema: no se pudo encontrar al usuario ... finalmente lo resolvió, gracias a las muestras OAuth2 de Dave Syer en GitHub.
El problema parece ser que la instancia de authenticationManager está autocandida en la clase @EnableAuthorizationServer AuthorizationServer
. AuthenticationManager está autoconectado allí y parece inicializarse con DAOAuthenticationProvider
predeterminado, y por alguna razón no utiliza JPA UserDetailsService
personalizado inicializando authenticationManager en WebSecurityConfiguration
.
En Dave Syer, los ejemplos de authenticationManager están expuestos como un bean en WebSecurityConfiguration
:
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
luego, en AuthorizationServer
iniciamos AutoWire authenticationManager de la siguiente manera:
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
Una vez que lo hice, finalmente logré que mi usuario se autenticara en mi repositorio de usuarios de JPA.