java - secure - Spring Security 5: No hay un PasswordEncoder asignado para el ID "null"
spring security oauth2 example mkyong (2)
Estoy migrando de Spring Boot 1.4.9 a Spring Boot 2.0 y también a Spring Security 5 y estoy tratando de autenticarme a través de OAuth 2. Pero recibo este error:
java.lang.IllegalArgumentException: No hay un PasswordEncoder asignado para el id "null
De la documentación de Spring Security 5 , llego a saber que el formato de almacenamiento para la contraseña ha cambiado.
En mi código actual, he creado mi codificador de contraseña como:
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Sin embargo me estaba dando por debajo del error:
La contraseña codificada no se parece a BCrypt
Así que actualizo el codificador según el documento Spring Security 5 para:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Ahora si puedo ver la contraseña en la base de datos se está almacenando como
{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ
Con ese primer error desaparecido y ahora cuando intento realizar la autenticación, obtengo un error inferior al siguiente:
java.lang.IllegalArgumentException: No hay un PasswordEncoder asignado para el id "null
Para resolver este problema, probé todas las siguientes preguntas de Stackoverflow:
Aquí hay una pregunta similar a la mía pero no contestada:
NOTA: Ya estoy almacenando la contraseña cifrada en la base de datos, por lo que no es necesario codificar nuevamente en UserDetailsService
.
En la documentación de seguridad de Spring 5 sugirieron que puedes manejar esta excepción usando:
DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches (PasswordEncoder)
Si esta es la solución, ¿dónde debería ponerla? He intentado ponerlo en el bean PasswordEncoder
como abajo, pero no funcionó:
DelegatingPasswordEncoder def = new DelegatingPasswordEncoder(idForEncode, encoders);
def.setDefaultPasswordEncoderForMatches(passwordEncoder);
Clase MyWebSecurity
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(HttpMethod.OPTIONS)
.antMatchers("/api/user/add");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Configuración de MyOauth2
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("test")
.scopes("read", "write")
.authorities(Roles.ADMIN.name(), Roles.USER.name())
.authorizedGrantTypes("password", "refresh_token")
.secret("secret")
.accessTokenValiditySeconds(1800);
}
}
Por favor guíame con este tema. He pasado horas para arreglar esto pero no puedo arreglarlo.
Cuando está configurando ClientDetailsServiceConfigurer
, también debe aplicar el nuevo formato de almacenamiento de contraseña al secreto del cliente.
.secret("{noop}secret")
Para cualquier persona que enfrente el mismo problema y no necesite una solución segura, principalmente para pruebas y depuración, los usuarios de la memoria aún pueden configurarse.
Esto es solo para jugar - no hay un escenario del mundo real.
El enfoque utilizado a continuación está en desuso.
Aquí es donde lo obtuve de:
Dentro de su WebSecurityConfigurerAdapter
agregue lo siguiente:
@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
Aquí, obviamente, las contraseñas están hasheadas, pero aún están disponibles en la memoria.
Por supuesto, también podría usar un PasswordEncoder
real como BCryptPasswordEncoder
y prefijar la contraseña con el id correcto:
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));