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

Spring Security下permitAll()为何触发AuthenticationManagerResolver

问题原因

这不是Spring Security的bug,本质是对permitAll()的语义理解存在偏差,结合过滤器的固定执行顺序,就会出现你观测到的现象。

Spring Security的过滤器链按优先级顺序执行:你配置oauth2ResourceServer()后注册的BearerTokenAuthenticationFilter,执行顺序远早于处理authorizeRequests路径匹配规则的AuthorizationFilter。

  • BearerTokenAuthenticationFilter的逻辑是:只要检测到请求携带Bearer令牌,不管后续路径配置的权限规则是什么,都会先调用你配置的AuthenticationManagerResolver获取认证管理器,完成令牌校验、填充SecurityContext的操作;如果令牌校验失败,会直接抛出认证异常,触发authenticationEntryPoint返回401,根本不会走到后续的路径权限匹配步骤。
  • permitAll()本质是授权规则,不是「跳过认证流程」的配置,它的实际语义是:当请求走到授权判断环节时,无论当前请求的认证状态是匿名、已认证还是其他状态,都直接放行。它无权干预前置过滤器的认证逻辑。

你观测到的「访问permitAll端点时AuthenticationManagerResolver被触发」完全是默认逻辑下的正常表现:

  • 请求没带令牌:BearerTokenAuthenticationFilter直接放行,走到授权环节匹配permitAll规则,正常访问
  • 请求带有效令牌:过滤器正常完成认证,走到授权环节匹配permitAll规则,正常访问
  • 请求带无效令牌:过滤器在前置校验阶段就抛出异常,直接返回401,走不到permitAll的判断逻辑,这就是你觉得不符合预期的场景
解决方案

根据业务场景选其中一种即可:

方案1:自定义令牌解析器,对公开路径跳过令牌校验

这种方式不会把路径排除在安全过滤器链外,cors、csrf等安全配置仍然生效,是更推荐的做法。

你可以自定义BearerTokenResolver,对配置了permitAll的路径直接返回null,告诉BearerTokenAuthenticationFilter当前请求不需要解析令牌,就不会触发AuthenticationManagerResolver的调用:

@Bean
public BearerTokenResolver bearerTokenResolver() {
    DefaultBearerTokenResolver defaultResolver = new DefaultBearerTokenResolver();
    return request -> {
        String servletPath = request.getServletPath();
        // 匹配所有配置permitAll的路径
        if (servletPath.startsWith("/endpoint/")
                || servletPath.equals("/error")
                || servletPath.equals("/info")
                || servletPath.equals("/health")) {
            return null;
        }
        return defaultResolver.resolve(request);
    };
}

然后在你的HttpSecurity配置中注入这个解析器:

http
    .cors().configurationSource(corsConfigurationSource)
    .and()
    .csrf()
    .ignoringAntMatchers("/")
    .and()
    .authorizeRequests(authorizeRequests -> authorizeRequests
        .mvcMatchers(GET, "/endpoint").hasAuthority("SCOPE_" + (Scope.READ))
        .mvcMatchers(GET, "/endpoint/{reference}").permitAll()
        .mvcMatchers(GET, "/error").permitAll()
        .mvcMatchers(GET, "/info").permitAll()
        .mvcMatchers(GET, "/health").permitAll()
        .anyRequest().denyAll())
    .oauth2ResourceServer()
    // 注入自定义的令牌解析器
    .bearerTokenResolver(bearerTokenResolver())
    .authenticationManagerResolver(authenticationManager())
    .accessDeniedHandler(accessDeniedHandler)
    .authenticationEntryPoint(authenticationEntryPoint);

方案2:将公开路径直接排除出安全过滤器链

如果你的公开端点完全不需要任何安全逻辑(比如纯静态资源、健康检查端点),可以通过WebSecurityCustomizer直接把这些路径从整个安全过滤器链中忽略,不会走任何安全相关的过滤器:

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    return web -> web.ignoring().mvcMatchers(
            GET,
            "/endpoint/{reference}",
            "/error",
            "/info",
            "/health"
    );
}

注意:这种方式下,这些路径的cors、csrf、安全响应头等配置都会失效,如果需要这些能力要单独在全局配置里处理,灵活性不如方案1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 13:27:25