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

SpringBoot中UserDetailsManager返回非空但AuthenticationManager认证失败

问题分析与解决方案

核心问题

1. 密码未加密存储

你创建用户时直接存入明文密码,但配置类里的UserDetailsManager是基于加密逻辑构建的(默认使用DelegatingPasswordEncoder),系统期望存储的是加密后的密码。明文密码和加密后的密码无法匹配,导致认证失败。

2. 认证Token构造错误

调用authenticationManager.authenticate()时,你提前传入了权限列表grantedAuthorityList。正确逻辑是:认证阶段的UsernamePasswordAuthenticationToken只需传入用户名和密码,权限由UserDetailsManager加载并返回,提前指定权限会干扰Spring Security的认证流程,触发异常。

修复步骤

步骤1:统一密码加密逻辑

首先在配置类中显式声明PasswordEncoder Bean,确保创建用户和认证时使用同一加密规则:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

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

    @Bean
    public UserDetailsManager userDetailsManager(PasswordEncoder passwordEncoder) {
        UserDetails user =
                User.withUsername("user")
                        .password(passwordEncoder.encode("password"))
                        .roles("USER")
                        .build();

        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}

然后修改用户创建代码,用PasswordEncoder加密密码后再存储:

// 在AuthenticationController中注入PasswordEncoder
private final PasswordEncoder passwordEncoder;

// 构造函数注入依赖
public AuthenticationController(UserDetailsManager userDetailsManager, AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder) {
    this.userDetailsManager = userDetailsManager;
    this.authenticationManager = authenticationManager;
    this.passwordEncoder = passwordEncoder;
}

// 创建用户时加密密码
userDetailsManager.createUser(User.withUsername(form.username())
        .password(passwordEncoder.encode(form.password()))
        .roles("USER")
        .build());

步骤2:修正认证Token构造

修改login方法中的authenticate调用,仅传入用户名和密码:

@PostMapping("/login")
ResponseEntity<AuthenticationResponse> login(@RequestBody AuthenticationRequest form) {
    try {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(form.username(), form.password())
        );
        // 认证通过后的逻辑,比如获取用户信息、生成响应
        var user = userDetailsManager.loadUserByUsername(form.username());
        return ResponseEntity.ok(new AuthenticationResponse(...));
    } catch (AuthenticationException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
}

补充提示

  • 生产环境不要使用withDefaultPasswordEncoder(),建议使用BCryptPasswordEncoder这类更安全的加密实现。
  • 认证失败返回HttpStatus.UNAUTHORIZED(401)比BAD_REQUEST(400)更符合HTTP语义,明确表示是权限认证问题。

内容的提问来源于stack exchange,提问作者Haohan Yang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 06:37:19