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

Spring Security自定义认证过滤器与方法安全执行顺序问题

问题分析与解决方案

你的核心问题是方法安全拦截器(处理@PreAuthorize)在自定义认证过滤器之前执行,导致SecurityContext未填充用户身份,最终返回403。下面从配置问题、过滤器选型、修正步骤三个维度给出具体解决方案:


一、核心原因:过滤器链顺序配置错误

Spring Security的方法安全拦截器(MethodSecurityInterceptor)是在Spring MVC控制器方法执行前触发的,而你的自定义AbstractAuthenticationProcessingFilter没有被放置到过滤器链的前置位置,导致认证流程晚于权限检查。

另外,AbstractAuthenticationProcessingFilter本身是继承OncePerRequestFilter的,二者核心差异在于:

  • AbstractAuthenticationProcessingFilter默认只拦截特定路径(通过setFilterProcessesUrl指定),适合表单登录这类定向认证场景;
  • OncePerRequestFilter会对每个请求执行一次,更适合令牌认证这种全局请求校验的场景。

二、具体修正步骤

1. 调整自定义过滤器的位置(关键)

无论使用哪种过滤器,都必须将其添加到Spring Security过滤器链的前端,确保在方法安全检查前完成认证。

示例1:使用AbstractAuthenticationProcessingFilter的正确配置

@Configuration
@Order(2) // 受保护路径配置,优先级低于白名单
public class ProtectedSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomTokenAuthFilter customTokenAuthFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/**") // 处理除白名单外的所有请求
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            // 将自定义过滤器放到UsernamePasswordAuthenticationFilter之前
            .addFilterBefore(customTokenAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 令牌认证建议无状态
    }

    // 自定义过滤器实例化,需指定拦截路径
    @Bean
    public CustomTokenAuthFilter customTokenAuthFilter() throws Exception {
        CustomTokenAuthFilter filter = new CustomTokenAuthFilter("/**"); // 拦截所有路径
        filter.setAuthenticationManager(authenticationManagerBean());
        // 设置认证成功/失败处理器
        filter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
        filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
        return filter;
    }
}

示例2:改用OncePerRequestFilter(更适合令牌认证)

如果你的认证逻辑不需要针对特定路径触发,推荐使用OncePerRequestFilter,它能确保每个请求都先经过认证:

@Component
public class CustomTokenAuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头/参数中提取令牌
        String token = request.getHeader("Authorization");
        if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 验证令牌,生成Authentication对象
            Authentication auth = validateTokenAndGetAuth(token);
            if (auth != null) {
                // 将认证信息存入SecurityContext
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        filterChain.doFilter(request, response);
    }

    // 实现令牌验证逻辑,返回合法的Authentication对象
    private Authentication validateTokenAndGetAuth(String token) {
        // 省略校验逻辑,示例返回带权限的认证对象
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_SOMETHING");
        return new UsernamePasswordAuthenticationToken("username", null, authorities);
    }
}

然后在受保护路径配置中添加该过滤器:

@Configuration
@Order(2)
public class ProtectedSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomTokenAuthFilter customTokenAuthFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/**")
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .addFilterBefore(customTokenAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

2. 确保白名单配置的独立性

@Order(1)的白名单配置要严格限制路径范围,避免干扰受保护路径的过滤器链:

@Configuration
@Order(1)
public class WhitelistSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/public/**") // 仅处理白名单路径
            .authorizeRequests()
                .anyRequest().permitAll()
                .and()
            .csrf().disable();
    }
}

3. 校验方法安全的角色层级配置

确保全局方法安全配置正确,角色层级生效:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setRoleHierarchy(roleHierarchy());
        return handler;
    }

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_SOMETHING"); // 示例层级
        return hierarchy;
    }
}

三、关键注意事项

  • 确保认证成功后,Authentication对象被正确存入SecurityContextHolder(基于ThreadLocal,请求线程内有效);
  • 令牌认证场景建议启用SessionCreationPolicy.STATELESS,避免会话干扰;
  • 检查用户的权限集合中是否确实包含ROLE_SOMETHING(注意Spring Security默认会给角色添加ROLE_前缀,避免权限名称不匹配)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:15:33