Spring Security 5:没有为id"null"映射PasswordEncoder

Jim*_*mmy 37 java spring spring-security spring-boot spring-security-oauth2

我正在从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();
}
Run Code Online (Sandbox Code Playgroud)

但它给了我以下错误:

编码密码看起来不像BCrypt

所以我按照Spring Security 5文档更新编码器:

@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Run Code Online (Sandbox Code Playgroud)

现在如果我可以在数据库中看到密码,那么它就是存储的

{bcrypt}$2a$10$LoV/3z36G86x6Gn101aekuz3q9d7yfBp3jFn7dzNN/AL5630FyUQ
Run Code Online (Sandbox Code Playgroud)

随着第一个错误消失,现在当我尝试进行身份验证时,我收到以下错误:

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);
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请指导我这个问题.我花了好几个小时来解决这个问题,但无法修复.

Edw*_*dez 55

在配置时ClientDetailsServiceConfigurer,您还必须将新密码存储格式应用于客户端密钥.

.secret("{noop}secret")
Run Code Online (Sandbox Code Playgroud)

  • 还有: auth.inMemoryAuthentication() .withUser("admin").roles("ADMIN").password("{noop}password"); (4认同)
  • 如果我不使用秘密怎么办? (2认同)

Sai*_*oni 39

添加.password("{noop}password")到安全配置文件。

例如 :

auth.inMemoryAuthentication()
        .withUser("admin").roles("ADMIN").password("{noop}password");
Run Code Online (Sandbox Code Playgroud)

  • 只是想使用简单的密码...!就像没有对其执行任何操作一样!我想是这样!;) (3认同)

roc*_*ady 8

对于任何面临同样问题并且不需要安全解决方案的人 - 主要用于测试和调试 - 在内存中仍然可以配置用户.

这只是为了玩游戏 - 没有现实世界的场景.

以下使用的方法已弃用.

这是我得到它的地方:


在您WebSecurityConfigurerAdapter添加以下内容:

@SuppressWarnings("deprecation")
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
Run Code Online (Sandbox Code Playgroud)

显然,密码是经过哈希处理的,但仍可在内存中使用.


当然,您也可以使用真正的PasswordEncoder类似BCryptPasswordEncoder密码并使用正确的id作为前缀:

// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
Run Code Online (Sandbox Code Playgroud)

  • 嗨,实际上我们不能使用NoOpPasswordEncoder.因为它在新的spring-security版本中被弃用了.根据Spring安全文档(https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-encoding),即使我们使用NoOpPasswordEncoder,我们也必须将{noop}添加为密码和密码的id.我相信这不能证明我的问题.所有解决方案的主要问题是他们没有提到您需要为您的秘密添加ID. (2认同)

小智 5

不知道这是否会对任何人有帮助。我的工作 WebSecurityConfigurer 和 OAuth2Config 代码如下:

OAuth2配置文件:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

网络安全配置器:

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

这是该项目的链接: springboot-authorization-server-oauth2


Con*_*ty8 5

您可以在 Spring Security 官方文档中阅读,DelegatingPasswordEncoder密码的一般格式为:{id}encodedPassword

这样 id 是用于查找应使用哪个 PasswordEncoder 的标识符,encodedPassword 是所选 PasswordEncoder 的原始编码密码。id 必须位于密码的开头,以 { 开头并以 } 结尾。如果找不到 id,则 id 将为 null。例如,以下可能是使用不同 id 编码的密码列表。所有的原始密码都是“password”。

ID 示例为:

{ bcrypt }$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG {noop}password { pbkdf2 }5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc { scrypt }$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{ sha256 }97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0


Pie*_*e C 5

从 Spring Security 4 升级到 5 时会出现该java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"错误消息。请参阅这篇Baeldung 文章以获取完整的说明和可能的解决方案。