![](/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.