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

Spring Security 6中自定义AuthenticationManager未生效问题排查

自定义AuthenticationProvider未被调用,返回403状态码的问题

在Spring Boot 3.1.x/Spring Security 6.1.0应用中,我需要支持多种自定义认证方式,于是创建了包含两个自定义AuthenticationProvider的AuthenticationManager,但这些AuthenticationProvider并未被调用,请求直接返回403状态码。

我的安全配置代码

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        var builder = http.getSharedObject(AuthenticationManagerBuilder.class);
        // 两个未被调用的AuthenticationProvider实现类
        builder.authenticationProvider(new AuthProvider1());
        builder.authenticationProvider(new AuthProvider2());
        var authManager = builder.build();

        return http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
                .authenticationManager(authManager) 
                .build();
    }
}

请求debug日志输出

13:31:51.540 [http-nio-8080-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
13:31:51.540 [http-nio-8080-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
13:31:51.540 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Detected StandardServletMultipartResolver
13:31:51.540 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Detected AcceptHeaderLocaleResolver
13:31:51.540 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Detected FixedThemeResolver
13:31:51.541 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@3b1247f7
13:31:51.541 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.support.SessionFlashMapManager@6cd3032
13:31:51.541 [http-nio-8080-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
13:31:51.541 [http-nio-8080-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
13:31:51.547 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - Securing GET /products
13:31:51.550 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
13:31:51.552 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
13:31:51.562 [http-nio-8080-exec-1] DEBUG o.s.s.w.s.HttpSessionRequestCache - Saved request http://localhost:8080/products to session
13:31:51.562 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.Http403ForbiddenEntryPoint - Pre-authenticated entry point called. Rejecting access
13:31:51.563 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
13:31:51.564 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - Did not store empty SecurityContext
13:31:51.564 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request
13:31:51.566 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - Securing GET /error
13:31:51.566 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
13:31:51.566 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
13:31:51.566 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - Secured GET /error
13:31:51.572 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - Did not store anonymous SecurityContext
13:31:51.573 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - Did not store anonymous SecurityContext
13:31:51.573 [http-nio-8080-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request

我想知道为什么自定义AuthenticationManager和AuthenticationProvider没被使用?我知道有其他替代方案(比如自定义安全过滤器),但希望用这种简洁的多认证策略方案。


问题原因

从日志能看到请求走到了Http403ForbiddenEntryPoint,核心问题是没有配置任何认证过滤器触发AuthenticationManager的调用。

在Spring Security 6.x中,仅配置AuthenticationManager并绑定到HttpSecurity是不够的——AuthenticationManager需要被某个认证过滤器(比如UsernamePasswordAuthenticationFilter、自定义Token过滤器)调用,才能执行认证逻辑。你的配置只要求所有请求必须认证,但没告诉Spring Security如何获取认证信息、触发认证流程,所以直接判定为未认证返回403。

另外,你通过http.getSharedObject(AuthenticationManagerBuilder.class)构建的AuthenticationManager,因为没有对应过滤器触发,根本不会被执行。

解决方案

要让自定义AuthenticationProvider生效,必须添加对应的认证过滤器或配置认证入口触发AuthenticationManager,以下是两种符合需求的简洁方案:

方案1:配置HttpBasic认证(快速验证)

如果你的自定义AuthenticationProvider支持UsernamePasswordAuthenticationToken,可以配置HttpBasic认证触发流程:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        AuthenticationManager authManager = http.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(new AuthProvider1())
                .authenticationProvider(new AuthProvider2())
                .build();

        return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
                .authenticationManager(authManager)
                .httpBasic(withDefaults()) // 添加HttpBasic过滤器,触发AuthenticationManager调用
                .build();
    }
}

当请求携带Basic Auth头时,HttpBasicAuthenticationFilter会调用你的AuthenticationManager,进而触发两个自定义Provider的认证逻辑。

方案2:自定义认证过滤器(灵活适配自定义认证)

如果是自定义Token等非HttpBasic的认证方式,编写自定义过滤器获取认证信息并触发认证:

@Component
public class CustomAuthFilter extends OncePerRequestFilter {

    private final AuthenticationManager authenticationManager;

    public CustomAuthFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求中获取自定义认证信息,比如请求头、参数
        String authToken = request.getHeader("X-Auth-Token");
        if (authToken != null) {
            // 构建自定义Authentication对象,需与你的Provider的supports方法匹配
            Authentication authRequest = new CustomAuthenticationToken(authToken);
            // 调用AuthenticationManager执行认证
            Authentication authResult = authenticationManager.authenticate(authRequest);
            // 将认证结果存入SecurityContext
            SecurityContextHolder.getContext().setAuthentication(authResult);
        }
        filterChain.doFilter(request, response);
    }
}

然后在SecurityConfig中注册过滤器:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomAuthFilter customAuthFilter;

    public SecurityConfig(CustomAuthFilter customAuthFilter) {
        this.customAuthFilter = customAuthFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        AuthenticationManager authManager = http.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationProvider(new AuthProvider1())
                .authenticationProvider(new AuthProvider2())
                .build();

        return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
                .authenticationManager(authManager)
                .addFilterBefore(customAuthFilter, UsernamePasswordAuthenticationFilter.class) // 添加自定义过滤器
                .build();
    }
}

额外注意事项

  • 每个AuthenticationProvider的supports(Class<?> authentication)方法必须返回true,才能被AuthenticationManager调用,请确保你的AuthProvider1和AuthProvider2的该方法正确实现,匹配传入的Authentication类型。
  • 如果是无状态认证(比如基于Token),可以禁用匿名认证和会话管理,避免默认匿名上下文干扰:
http.anonymous(AbstractHttpConfigurer::disable)
    .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:27:08