私はSpring Boot 1.4.9からSpring Boot 2.0に移行し、Spring Security 5にも移行しています。OAuth 2を介して認証しようとしていますが、このエラーが発生しています:
Java.lang.IllegalArgumentException:id "nullにマップされたPasswordEncoderはありません
Spring Security 5 のドキュメントから、パスワードの保存形式が変更されたことがわかりました。
現在のコードでは、パスワードエンコーダーBeanを次のように作成しています。
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
しかし、それは私に以下のエラーを与えていました:
エンコードされたパスワードはBCryptのように見えません
Spring Security 5 ドキュメントに従ってエンコーダーを更新します:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
データベースにパスワードが表示されたら、次のように保存されています
{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ
その最初のエラーがなくなり、認証を行おうとすると、以下のエラーが発生します:
Java.lang.IllegalArgumentException:id "nullにマップされたPasswordEncoderはありません
この問題を解決するために、Stackoverflowから以下の質問をすべて試しました。
ここに私のものに似ているが答えられていない質問があります:
注:既に暗号化されたパスワードをデータベースに保存しているため、UserDetailsService
で再度エンコードする必要はありません。
Spring security 5 ドキュメンテーションでは、以下を使用してこの例外を処理できると提案しました。
DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)
これが修正である場合、どこに置くべきですか?私はそれを以下のようなPasswordEncoder
Beanに入れようとしましたが、うまくいきませんでした:
DelegatingPasswordEncoder def = new DelegatingPasswordEncoder(idForEncode, encoders);
def.setDefaultPasswordEncoderForMatches(passwordEncoder);
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();
}
}
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);
}
}
この問題について教えてください。これを修正するのに何時間も費やしましたが、修正できません。
ClientDetailsServiceConfigurer
を構成するときは、新しい パスワードストレージ形式 もクライアントシークレットに適用する必要があります。
.secret("{noop}secret")
同じ問題に直面し、安全なソリューションを必要としない人のために-主にテストとデバッグのために-メモリ内のユーザーはまだ設定できます。
これはただ遊んでいるだけです-現実世界のシナリオはありません。
以下で使用されるアプローチは非推奨です。
これは私がそれを手に入れた場所です:
WebSecurityConfigurerAdapter
内に次を追加します。
@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
ここでは、明らかに、パスワードはハッシュされますが、メモリ内で利用可能です。
もちろん、PasswordEncoder
のような実際のBCryptPasswordEncoder
を使用して、正しいIDをパスワードの前に付けることもできます。
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
.password("{noop}password")
をセキュリティ設定ファイルに追加します。
例えば :
auth.inMemoryAuthentication()
.withUser("admin").roles("ADMIN").password("{noop}password");
Springはパスワードを保存するたびに、bcrypt、scrypt、pbkdf2などのエンコードされたパスワードにエンコーダーのプレフィックスを挿入します。これにより、パスワードをデコードするときに、適切なエンコーダーを使用してデコードできます。エンコードされたパスワードにプレフィックスがない場合、defaultPasswordEncoderForMatchesを使用します。 DelegatingPasswordEncoder.classのmatchesメソッドを表示して、その動作を確認できます。したがって、基本的に次の行でdefaultPasswordEncoderForMatchesを設定する必要があります。
@Bean(name="myPasswordEncoder")
public PasswordEncoder getPasswordEncoder() {
DelegatingPasswordEncoder delPasswordEncoder= (DelegatingPasswordEncoder)PasswordEncoderFactories.createDelegatingPasswordEncoder();
BCryptPasswordEncoder bcryptPasswordEncoder =new BCryptPasswordEncoder();
delPasswordEncoder.setDefaultPasswordEncoderForMatches(bcryptPasswordEncoder);
return delPasswordEncoder;
}
さらに、このエンコーダーにDefaultPasswordEncoderForMatchesを認証プロバイダーにも提供する必要がある場合があります。私は私の設定クラスの以下の行でそれをしました。
@Bean
@Autowired
public DaoAuthenticationProvider getDaoAuthenticationProvider(@Qualifier("myPasswordEncoder") PasswordEncoder passwordEncoder, UserDetailsService userDetailsServiceJDBC) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsServiceJDBC);
return daoAuthenticationProvider;
}
について
エンコードされたパスワードはBCryptのように見えません
私の場合、pwdハッシュは強度4で生成されたため、デフォルトコンストラクター(10)で使用されるBCryptPasswordEncoder強度に不一致がありました。そのため、強度を明示的に設定しました。
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
また、私のSpring Securityバージョンは5.1.6であり、BCryptPasswordEncoderで完全に動作します