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

Spring Security配置BcryptEncoder登录提示Bad Credentials问题排查

问题描述

开发Spring Security支撑的REST服务时,已按指引创建配置类定义PasswordEncoder Bean,项目可正常编译,但登录操作始终返回*Bad Credentials(无效凭证)*错误。已确认输入的账号密码完全正确,数据库中存储的Bcrypt格式密码带有{bcrypt}前缀,初步判定为密码编码器配置错误。

现有配置

PasswordEncoderConfig 代码

@Configuration
public class PasswordEncoderConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

SpringSecurityConfiguration 代码

@EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    private static final String ADMIN = "ROLE_ADMIN";
    private static final String WORKER = "ROLE_WORKER";
    
    private final DataSource dataSource;
    private PasswordEncoder bcryptencoder;
    
    public SecurityConfiguration(DataSource dataSource,  PasswordEncoder bcryptencoder) {
        this.dataSource = dataSource;
        this.bcryptencoder = bcryptencoder;
    }
    
    /*@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }*/
    
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
            .usersByUsernameQuery("select voornaam as username, password as password, true as enabled from gebruikers where voornaam = ?")
            .passwordEncoder(bcryptencoder)
            .authoritiesByUsernameQuery("select voornaam as username, role as authorities from gebruikers where voornaam = ?");
        
            
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
        .mvcMatchers("/images/**")
        .mvcMatchers("/css/**")
        .mvcMatchers("/js/**");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        /*remove after postman, @cross origin*/
        http.formLogin();
        http.authorizeHttpRequests(requests -> requests
            .mvcMatchers("/**").hasAnyAuthority(ADMIN, WORKER)
            .mvcMatchers("/gebruikers/**").hasAnyAuthority(ADMIN, WORKER));
        http.logout();
        
    }
}
故障根因

核心问题出在密码编码器的注入匹配逻辑与密码前缀适配规则不匹配:

  • PasswordEncoderConfig中定义的DelegatingPasswordEncoder本身支持识别{bcrypt}前缀,会自动提取前缀后调用BCrypt算法完成密码匹配,逻辑本身没有问题,该Bean默认注册名称为passwordEncoder。
  • SecurityConfiguration构造器注入PasswordEncoder时,参数名写为bcryptencoder。如果项目中残留了其他名为bcryptencoder的PasswordEncoder Bean(比如注释掉的BCryptPasswordEncoder定义未清理、其他配置类声明了同名Bean),Spring会按参数名优先注入BCryptPasswordEncoder实例。
  • BCryptPasswordEncoder不识别{bcrypt}前缀,会把包含前缀的完整字符串作为BCrypt密文与输入明文匹配,必然匹配失败,抛出Bad Credentials错误。

另外权限配置顺序存在隐患:/**的全路径匹配规则写在/gebruikers/**前面,会导致后续更具体的路径规则永远不会触发,不过该问题不会影响登录阶段的凭证校验。

修复方案

二选一即可:

  • 方案1(推荐,兼容多密码算法格式):保留DelegatingPasswordEncoder
    1. 全局搜索项目中所有PasswordEncoder类型的Bean定义,删除多余的BCryptPasswordEncoder实例声明,确保上下文里只有PasswordEncoderConfig中定义的DelegatingPasswordEncoder。
    2. 将SecurityConfiguration中注入的密码编码器参数名改为passwordEncoder,避免按名称匹配注入错误的Bean,同步修改jdbcAuthentication配置中的编码器引用即可。
  • 方案2:直接使用BCryptPasswordEncoder(无需{bcrypt}前缀)
    1. 删除PasswordEncoderConfig类,放开SecurityConfiguration中被注释的BCryptPasswordEncoder Bean定义。
    2. 批量删除数据库中所有存储密码前的{bcrypt}前缀,BCryptPasswordEncoder不识别该前缀,前缀存在会导致密文格式非法、匹配失败。

额外优化

调整HttpSecurity中的权限匹配规则顺序,把范围更小的路径匹配放在前面,避免规则被覆盖:

http.authorizeHttpRequests(requests -> requests
    .mvcMatchers("/gebruikers/**").hasAnyAuthority(ADMIN, WORKER)
    .mvcMatchers("/**").hasAnyAuthority(ADMIN, WORKER));

内容的提问来源于stack exchange,提问作者Yannick Mussche

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:06:28