Ich migriere von Spring Boot 1.4.9 zu Spring Boot 2.0 und auch zu Spring Security 5 und versuche, mich über OAuth 2 zu authentifizieren. Es wird jedoch folgender Fehler angezeigt:
Java.lang.IllegalArgumentException: Für die ID "null" ist kein PasswordEncoder zugeordnet
Aus der Dokumentation von Spring Security 5 erfahre ich, dass das Speicherformat für das Passwort geändert wurde.
In meinem aktuellen Code habe ich mein Passwort Encoder Bean erstellt als:
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Es gab mir jedoch folgenden Fehler:
Das verschlüsselte Passwort sieht nicht aus wie BCrypt
Also aktualisiere ich den Encoder gemäß dem Spring Security 5 Dokument zu:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Nun, wenn ich das Passwort in der Datenbank sehen kann, wird es gespeichert als
{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ
Wenn der erste Fehler behoben ist und ich versuche, die Authentifizierung durchzuführen, wird der folgende Fehler angezeigt:
Java.lang.IllegalArgumentException: Für die ID "null" ist kein PasswordEncoder zugeordnet
Um dieses Problem zu lösen, habe ich alle folgenden Fragen von Stackoverflow ausprobiert:
Hier ist eine Frage, die meiner ähnlich ist, aber nicht beantwortet wird:
HINWEIS: Ich speichere bereits ein verschlüsseltes Passwort in der Datenbank, sodass ich es nicht erneut in UserDetailsService
verschlüsseln muss.
In der Dokumentation Spring security 5 wurde vorgeschlagen, dass Sie diese Ausnahme wie folgt behandeln können:
DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches (PasswordEncoder)
Wenn dies die Lösung ist, wo soll ich sie dann hinstellen? Ich habe versucht, es wie folgt in PasswordEncoder
Bean zu schreiben, aber es hat nicht funktioniert:
DelegatingPasswordEncoder def = new DelegatingPasswordEncoder(idForEncode, encoders);
def.setDefaultPasswordEncoderForMatches(passwordEncoder);
MyWebSecurity-Klasse
@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();
}
}
MyOauth2-Konfiguration
@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);
}
}
Bitte führen Sie mich mit diesem Problem. Ich habe Stunden damit verbracht, dieses Problem zu beheben, kann es aber nicht beheben.
Wenn Sie ClientDetailsServiceConfigurer
konfigurieren, müssen Sie auch das neue Kennwortspeicherformat auf das Clientgeheimnis anwenden.
.secret("{noop}secret")
Für alle, die mit demselben Problem konfrontiert sind und keine sichere Lösung benötigen - hauptsächlich zum Testen und Debuggen - können Benutzer im Arbeitsspeicher weiterhin konfiguriert werden.
Dies ist nur zum Herumspielen gedacht - kein reales Szenario.
Der unten verwendete Ansatz ist veraltet.
Hier habe ich es bekommen:
Fügen Sie in Ihrem WebSecurityConfigurerAdapter
Folgendes hinzu:
@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
Hier werden offensichtlich Passwörter gehasht, sind aber immer noch im Speicher verfügbar.
Natürlich können Sie auch ein echtes PasswordEncoder
wie BCryptPasswordEncoder
verwenden und dem Passwort die richtige ID voranstellen:
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
.password("{noop}password")
zur Sicherheitskonfigurationsdatei hinzufügen.
Beispielsweise :
auth.inMemoryAuthentication()
.withUser("admin").roles("ADMIN").password("{noop}password");
Immer wenn Spring das Passwort speichert, fügt es den verschlüsselten Passwörtern wie bcrypt, scrypt, pbkdf2 usw. ein Präfix von encoder hinzu, sodass es zum Decodieren des Passworts einen geeigneten Encoder verwenden kann. Wenn das verschlüsselte Kennwort kein Präfix enthält, wird defaultPasswordEncoderForMatches verwendet. Sie können die Übereinstimmungsmethode von DelegatingPasswordEncoder.class anzeigen, um zu sehen, wie sie funktioniert. Daher müssen wir defaultPasswordEncoderForMatches in den folgenden Zeilen festlegen.
@Bean(name="myPasswordEncoder")
public PasswordEncoder getPasswordEncoder() {
DelegatingPasswordEncoder delPasswordEncoder= (DelegatingPasswordEncoder)PasswordEncoderFactories.createDelegatingPasswordEncoder();
BCryptPasswordEncoder bcryptPasswordEncoder =new BCryptPasswordEncoder();
delPasswordEncoder.setDefaultPasswordEncoderForMatches(bcryptPasswordEncoder);
return delPasswordEncoder;
}
Jetzt müssen Sie diesen Encoder möglicherweise auch Ihrem Authentifizierungsanbieter mit DefaultPasswordEncoderForMatches zur Verfügung stellen. Ich habe das mit den folgenden Zeilen in meinen Konfigurationsklassen gemacht.
@Bean
@Autowired
public DaoAuthenticationProvider getDaoAuthenticationProvider(@Qualifier("myPasswordEncoder") PasswordEncoder passwordEncoder, UserDetailsService userDetailsServiceJDBC) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsServiceJDBC);
return daoAuthenticationProvider;
}
Hinsichtlich
Das verschlüsselte Passwort sieht nicht wie BCrypt aus
In meinem Fall gab es eine Inkongruenz in der BCryptPasswordEncoder-Stärke, die vom Standardkonstruktor (10) verwendet wurde, da pwd-Hash mit Stärke 4 generiert wurde. Ich habe die Stärke also explizit festgelegt.
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
auch meine Spring Security Version ist 5.1.6 und funktioniert perfekt mit BCryptPasswordEncoder