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

Spring Security Web 6.1.5中AuthenticationManager的正确配置方式

Spring Security 6.1.5 配置AuthenticationManager的正确姿势

在Spring Security 6+版本里,AuthenticationManagerBuilder配置方式已经被弃用,现在改用自定义UserDetailsService + SecurityFilterChain的组合来实现,同时可以通过@Bean暴露AuthenticationManager供登录、注册这类接口调用。结合你的项目场景(独立的app_users表、JWT+Redis认证),具体配置步骤如下:

1. 实现自定义UserDetailsService

因为你的用户信息存在独立的app_users表,所以需要自己实现UserDetailsService,从这个表加载用户的账号、密码和权限信息:

@Service
public class AppUserDetailsService implements UserDetailsService {

    private final AppUserRepository appUserRepository; // 操作app_users表的JPA仓库

    public AppUserDetailsService(AppUserRepository appUserRepository) {
        this.appUserRepository = appUserRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AppUser appUser = appUserRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("找不到用户: " + username));
        
        // 转成Spring Security认可的UserDetails对象,注意权限要加ROLE_前缀(如果数据库存的是ADMIN,这里转成ROLE_ADMIN)
        return User.withUsername(appUser.getUsername())
                .password(appUser.getPassword()) // 数据库里的密码必须是加密后的,注册时要用PasswordEncoder加密
                .authorities(appUser.getRoles().stream()
                        .map(role -> "ROLE_" + role)
                        .collect(Collectors.toList()))
                .build();
    }
}

2. 配置PasswordEncoder

必须配置密码加密器,推荐用BCrypt:

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

3. 配置AuthenticationManager和SecurityFilterChain

现在通过AuthenticationConfiguration来获取AuthenticationManager,同时在SecurityFilterChain里配置认证规则和自定义认证提供者:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final AppUserDetailsService appUserDetailsService;
    private final PasswordEncoder passwordEncoder;

    public SecurityConfig(AppUserDetailsService appUserDetailsService, PasswordEncoder passwordEncoder) {
        this.appUserDetailsService = appUserDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    // 暴露AuthenticationManager,供登录接口调用
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    // 配置安全过滤链,定义哪些接口放行、哪些需要权限
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // JWT认证不需要CSRF
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/v1/signup", "/api/v1/login").permitAll() // 注册、登录接口直接放行
                        .requestMatchers("/api/v1/admin/**").hasRole("ADMIN") // 管理员接口需要ADMIN权限
                        .anyRequest().authenticated()
                )
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // JWT是无状态的,禁用session
                .authenticationProvider(authenticationProvider()); // 关联自定义的认证提供者

        return http.build();
    }

    // 自定义认证提供者,把UserDetailsService和PasswordEncoder绑定
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(appUserDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder);
        return authProvider;
    }
}

4. 在登录接口中使用AuthenticationManager

比如你的JWT登录接口,注入AuthenticationManager来完成用户身份校验:

@RestController
@RequestMapping("/api/v1")
public class AuthController {

    private final AuthenticationManager authenticationManager;
    private final JwtTokenProvider jwtTokenProvider; // 自己写的JWT生成工具类
    private final RedisTemplate<String, String> redisTemplate; // 存JWT用的Redis模板

    public AuthController(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider, RedisTemplate<String, String> redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.jwtTokenProvider = jwtTokenProvider;
        this.redisTemplate = redisTemplate;
    }

    @PostMapping("/login")
    public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest loginRequest) {
        // 用AuthenticationManager校验用户名密码
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );

        // 生成JWT令牌
        String token = jwtTokenProvider.generateToken(authentication);

        // 把令牌存到Redis,设置过期时间和JWT有效期一致
        redisTemplate.opsForValue().set("JWT:" + loginRequest.getUsername(), token, jwtTokenProvider.getExpirationMinutes(), TimeUnit.MINUTES);

        return ResponseEntity.ok(new AuthResponse(token));
    }
}

额外提示

  • 初始admin用户:可以用Flyway脚本往app_users表插数据,密码要提前用BCrypt加密(比如在线生成或者写个小工具生成)。
  • JWT校验:还得加个自定义过滤器,拦截所有请求,从请求头里拿JWT,去Redis校验有效性,校验通过后把用户信息放到SecurityContext里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:57:50