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

升级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);
}

注意事项

  1. 确保AuthenticateServiceImpl已正确实现UserDetailsService接口,并添加@Service注解被Spring管理;
  2. 必须在SecurityFilterChain中配置sessionManagement为STATELESS,保持和旧代码一致的无状态会话模式。

内容的提问来源于stack exchange,提问作者Itzik.B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 11:22:27