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
相关产品推荐
相关产品推荐

