![](/img/trans.png)
[英]There is no PasswordEncoder mapped for the id “null” in Spring Security
[英]Spring Security 5 : There is no PasswordEncoder mapped for the id "null"
I am migrating from Spring Boot 1.4.9 to Spring Boot 2.0 and also to Spring Security 5 and I am trying to do authenticate via OAuth 2. But I am getting this error:
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 安全 5文檔中,他們建議您可以使用以下方法處理此異常:
DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)
如果這是修復,那么我應該把它放在哪里? 我試圖把它放在PasswordEncoder
bean 中,如下所示,但它不起作用:
DelegatingPasswordEncoder def = new DelegatingPasswordEncoder(idForEncode, encoders);
def.setDefaultPasswordEncoderForMatches(passwordEncoder);
MyWebSecurity class
@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")
將.password("{noop}password")
到安全配置文件。
例如:
auth.inMemoryAuthentication()
.withUser("admin").roles("ADMIN").password("{noop}password");
對於面臨相同問題且不需要安全解決方案的任何人 - 主要用於測試和調試 - 仍然可以配置內存用戶。
這只是為了玩 - 沒有真實世界的場景。
下面使用的方法已被棄用。
這是我從哪里得到的:
在您的WebSecurityConfigurerAdapter
添加以下內容:
@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
顯然,這里的密碼是經過哈希處理的,但仍然可以在內存中使用。
當然,你也可以使用像BCryptPasswordEncoder
這樣的真正的PasswordEncoder
並在密碼前加上正確的 id:
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
不知道這是否會幫助任何人。 我的工作 WebSecurityConfigurer 和 OAuth2Config 代碼如下:
OAuth2Config 文件:
package com.crown.AuthenticationServer.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("crown")
.secret("{noop}thisissecret")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
網絡安全配置器:
package com.crown.AuthenticationServer.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
final User.UserBuilder userBuilder = User.builder().passwordEncoder(encoder::encode);
UserDetails user = userBuilder
.username("john.carnell")
.password("password")
.roles("USER")
.build();
UserDetails admin = userBuilder
.username("william.woodward")
.password("password")
.roles("USER","ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
每當 Spring 存儲密碼時,它都會在編碼后的密碼(如 bcrypt、scrypt、pbkdf2 等)中放置一個編碼器前綴,以便在需要解碼密碼時,可以使用合適的編碼器進行解碼。 如果編碼密碼中沒有前綴,則它使用 defaultPasswordEncoderForMatches。 您可以查看 DelegatingPasswordEncoder.class 的匹配方法以了解其工作原理。 所以基本上我們需要通過以下幾行設置 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;
}
如果您從數據庫中獲取用戶名和密碼,您可以使用以下代碼添加 NoOpPassword 實例。
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(adm).passwordEncoder(NoOpPasswordEncoder.getInstance());
}
其中adm是我的項目的自定義用戶對象,它具有 getPassword() 和 getUsername() 方法。
還要記住,要創建自定義用戶 POJO,您必須實現 UserDetails 接口並實現它的所有方法。
希望這會有所幫助。
您可以在官方 Spring Security 文檔中閱讀,對於DelegatingPasswordEncoder
,密碼的一般格式為: {id}encodedPassword
這樣 id 是用於查找應該使用哪個 PasswordEncoder 的標識符,encodedPassword 是所選 PasswordEncoder 的原始編碼密碼。 id 必須在密碼的開頭,以 { 開頭,以 } 結尾。 如果找不到 id,則 id 將為 null 。 例如,以下可能是使用不同 id 編碼的密碼列表。 所有原始密碼都是“密碼”。
ID 示例是:
{bcrypt} $ 2A $ 10 $ dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM / BG {空操作}密碼{PBKDF2} 5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc {scrypt} $ $ e0801 + 8bWJaSu2IKSn9Z9kM TPXfOc / 9bdYSrN1oD9qfVThWEwdRTnO7re7Ei + fUZRJ68k9lTyuTeUp4of4g24hHnazw == $ OAOec05 + bXxvuu / 1qZ6NUR + xQYvYv7BeL1QxwRpY5Pc =
{ sha256 }97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
關於
編碼密碼看起來不像 BCrypt
在我的例子中,默認構造函數 (10) 使用的 BCryptPasswordEncoder 強度不匹配,因為 pwd 哈希是用強度 4 生成的。所以我已經明確設置了強度。
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
我的 Spring Security 版本也是 5.1.6,它與 BCryptPasswordEncoder 完美配合
從 Spring Security 4 升級到 5 時,出現java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
錯誤消息。請參閱此Baeldung 文章以獲取完整說明和可能的解決方案。
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private UserDetailsService userDetailsService;
@Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
return provider;
}
}
添加以下兩個注釋可以解決該問題
@Configuration @EnableWebSecurity
發生此錯誤可能是您錯誤地忘記對業務邏輯使用注釋“@Bean”。
@Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
return provider;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.