You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security报错:无映射id为null的PasswordEncoder,求解决方案

解决Spring Security中"There is no PasswordEncoder mapped for the id 'null'"错误

问题描述

报错信息:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

相关实现代码如下:

SecurityConfig配置类

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    private final UserDetailsService userDetailsService;
    private final PersistentTokenRepository persistentTokenRepository;
    
    // 用于Spring Data JPA SPeL
    @Bean
    public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
        return new SecurityEvaluationContextExtension();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        http
                .authorizeRequests(authorize -> {
                    authorize
                            .antMatchers("/", "/webjars/**", "/login", "/resources/**").permitAll();
                } )
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin(loginConfigurer -> {
                    loginConfigurer
                            .loginProcessingUrl("/login")
                            .loginPage("/").permitAll()
                            .successForwardUrl("/")
                            .defaultSuccessUrl("/")
                            .failureUrl("/?error");
                })
                .logout(logoutConfigurer -> {
                    logoutConfigurer
                            .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
                            .logoutSuccessUrl("/?logout")
                            .permitAll();
                })
                .httpBasic()
                .and().rememberMe()
                .tokenRepository(persistentTokenRepository)
                .userDetailsService(userDetailsService);
        http.headers().frameOptions().sameOrigin();
    }
    
    @Bean
    PasswordEncoder passwordEncoder(){
        return SfgPasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

Token创建配置

@Configuration
public class SecurityBeans {
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }
}

自定义PasswordEncoder工厂类

public class SfgPasswordEncoderFactories {

    public static PasswordEncoder createDelegatingPasswordEncoder() {
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put(encodingId, new BCryptPasswordEncoder());
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
        encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
        encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());

        return new DelegatingPasswordEncoder(encodingId, encoders);
    }

    // 禁止实例化
    private SfgPasswordEncoderFactories() {
    }
}

用户说明:数据库中已插入bcrypt格式的密码,错误出在return new DelegatingPasswordEncoder(encodingId, encoders);行,但认为代码正确。

问题原因

DelegatingPasswordEncoder默认要求密码字符串以{编码ID}作为前缀(比如{bcrypt}$2a$10$...),用来识别使用哪种编码器校验密码。如果数据库中的密码没有这个前缀,DelegatingPasswordEncoder会无法识别编码ID,导致抛出"id 'null'"的错误。

虽然你在工厂类中设置了默认编码ID为bcrypt,但DelegatingPasswordEncoder的默认逻辑是必须通过前缀指定编码ID,不会自动使用默认编码器处理无前缀的密码。

解决方案

方案1:修改数据库密码,添加前缀

将数据库中存储的bcrypt密码前面加上{bcrypt}前缀,例如:
原密码:$2a$10$EixZaY3s7vjR0kS6Rq7M/.tG8fL93K5y8eQbX8y8eQbX8y8eQbX8y
修改后:{bcrypt}$2a$10$EixZaY3s7vjR0kS6Rq7M/.tG8fL93K5y8eQbX8y8eQbX8y8eQbX8y

方案2:配置DelegatingPasswordEncoder使用默认编码器处理无前缀密码

修改SfgPasswordEncoderFactories的createDelegatingPasswordEncoder方法,创建DelegatingPasswordEncoder后,设置默认编码器(当无前缀时使用bcrypt):

public static PasswordEncoder createDelegatingPasswordEncoder() {
    String encodingId = "bcrypt";
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    encoders.put(encodingId, new BCryptPasswordEncoder());
    encoders.put("bcrypt", new BCryptPasswordEncoder());
    encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
    encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
    encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());

    DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders);
    // 设置默认编码器,处理无前缀的密码
    delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());
    return delegatingPasswordEncoder;
}

方案3:直接使用BCryptPasswordEncoder(如果仅需bcrypt编码)

如果你的系统只需要bcrypt一种密码编码方式,可以简化PasswordEncoder的Bean定义,直接返回BCryptPasswordEncoder,无需使用DelegatingPasswordEncoder:

@Bean
PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

这样数据库中的密码无需添加前缀,直接存储bcrypt加密后的字符串即可。

验证建议

修改后重新启动应用,测试登录功能,确认错误消失。同时确保UserDetailsService实现中返回的User对象的密码与数据库存储的格式一致。

内容的提问来源于stack exchange,提问作者Javier Figueiras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 16:35:23