升级Spring Security至6.1.0:AuthenticationManager实现问题求助
Spring Security 6.1.0 适配问题:AuthenticationManager 获取方案
因旧版本存在严重漏洞,我正在将两年前的代码升级至使用Spring Security 6.1.0的新项目,希望保留原有代码结构,但适配框架时遇到了AuthenticationManager的获取问题。核心逻辑为:从每个请求的Authorization头提取JWT令牌,验证用户授权。
旧版代码(基于WebSecurityConfigurerAdapter)
@EnableWebSecurity public class SecurityConfigurer extends WebSecurityConfigurerAdapter { @Autowired private AuthenticateServiceImpl authenticateService; @Autowired private JwtRequestFilter jwtRequestFilter; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(authenticateService); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() .antMatchers("/users/createUser").permitAll()//todo fix in the future .antMatchers("/users/getProfile").permitAll()//todo fix in the future .antMatchers("/users/**").hasRole( "ADMIN") .antMatchers("/contacts/**").hasRole( "ADMIN") .anyRequest().authenticated() .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
新版代码(Spring Security 6.1.0)
WebSecurityConfigurerAdapter已被移除,无法通过原方式重写authenticationManagerBean获取AuthenticationManager,当前代码如下:
@Configuration @EnableWebSecurity public class SecurityConfigurer{ @Autowired private JwtRequestFilter jwtRequestFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests((requests) -> requests .requestMatchers("/auth/**").permitAll() .requestMatchers("/users/createUser").permitAll()//todo fix in the future .requestMatchers("/users/getProfile").permitAll()//todo fix in the future .requestMatchers("/users/**").hasRole("ADMIN") .requestMatchers("/contacts/**").hasRole("ADMIN") .anyRequest().authenticated() ); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } /*@Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }*/ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
依赖AuthenticationManager的AuthController
登录接口需要使用AuthenticationManager完成用户名密码验证,代码如下:
@RestController @RequestMapping("/auth") public class AuthController { @Autowired private AuthenticateServiceImpl authenticateService; @Autowired private Definitions definitions; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserRepository userRepository; @Autowired private JwtUtil jwtUtil; @Autowired private UserServiceImpl userService; @PostMapping(value = "/login") public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) throws UnauthorizedException { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) ); } catch (BadCredentialsException e) { throw new UnauthorizedException(definitions.INVALID_CREDENTIAL); } final UserDetails userDetails = authenticateService .loadUserByUsername(authenticationRequest.getUsername()); User user = userRepository.findUserByUsername(authenticationRequest.getUsername()); return ResponseEntity.ok() .body(new AuthenticationResponse(jwtUtil.generateToken(userDetails), userService.convertToDtoRestricted(user))); } }
JwtRequestFilter参考代码
@Component @Slf4j public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private AuthenticateServiceImpl authenticateService; @Autowired private JwtUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authorizationHeader != null) { if (authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); try { username = jwtUtil.extractUsername(jwt); } catch (SignatureException e) { if(log.isInfoEnabled()) { log.info("~JWT: Invalid Token ~ Access Denied!"); } } catch (ExpiredJwtException e) { if(log.isInfoEnabled()) { log.info("~JWT: Expired Token ~ Access Denied!"); } } } } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.authenticateService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } filterChain.doFilter(request, response); } }
解决方案:获取AuthenticationManager的两种方式
方式1:通过AuthenticationConfiguration获取
直接利用Spring提供的AuthenticationConfiguration获取已组装好的AuthenticationManager,无需手动配置Provider:
@Configuration @EnableWebSecurity public class SecurityConfigurer{ @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired private AuthenticateServiceImpl authenticateService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests((requests) -> requests .requestMatchers("/auth/**").permitAll() .requestMatchers("/users/createUser").permitAll() .requestMatchers("/users/getProfile").permitAll() .requestMatchers("/users/**").hasRole("ADMIN") .requestMatchers("/contacts/**").hasRole("ADMIN") .anyRequest().authenticated() ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));// 保持无状态会话,和旧代码一致 http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
方式2:手动构建ProviderManager
如果需要自定义认证逻辑,可手动创建DaoAuthenticationProvider并组装成ProviderManager:
@Bean public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(passwordEncoder); return new ProviderManager(authProvider); }
注意事项
- 确保
AuthenticateServiceImpl已正确实现UserDetailsService接口,并添加@Service注解被Spring管理; - 必须在SecurityFilterChain中配置
sessionManagement为STATELESS,保持和旧代码一致的无状态会话模式。
内容的提问来源于stack exchange,提问作者Itzik.B
相关产品推荐
相关产品推荐

