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

Spring Security中http.authorizeRequests().anyRequest().authenticated()行为异常技术问询

解决Spring Security anyRequest().authenticated()不生效的问题

嘿,我之前也踩过这个Spring Security的坑,咱们来一步步拆解你的问题:你配置了anyRequest().authenticated(),但所有请求都能直接通过没被拦截,只有加上带hasAnyAuthority的规则才正常工作。这大概率是两个原因之一——授权规则的配置方式不对,或者自定义过滤器的逻辑有漏洞。

1. 先修正授权规则的链式写法

你现在的代码多次调用http.authorizeRequests(),这其实会创建多个独立的授权配置块,Spring Security处理这些配置时很容易出现匹配逻辑混乱。正确的做法是把所有授权规则链式写在同一个authorizeRequests()块里,这样规则的匹配顺序才会完全符合预期:

@Override
protected void configure(HttpSecurity http) throws Exception {
    CustomAuthFilter customAuthFilter = new CustomAuthFilter(authenticationManagerBean());
    customAuthFilter.setFilterProcessesUrl("/api/login");
    
    http.csrf().disable()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
            // 放行登录和注册路径
            .antMatchers("/api/login/**", "/register/**").permitAll()
            // 取消注释后可以保留这些权限规则
            //.antMatchers(HttpMethod.GET,"/api/users/").hasAnyAuthority("ROLE_USER")
            //.antMatchers(HttpMethod.POST,"/api/user/save/**").hasAnyAuthority("ROLE_ADMIN")
            // 剩下的所有请求都要求认证
            .anyRequest().authenticated()
        .and()
        .addFilter(customAuthFilter)
        .addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}

为什么要这么改?每次调用authorizeRequests()都会生成一个新的ExpressionUrlAuthorizationConfigurer实例,多个实例的规则可能会互相干扰;而链式写法能确保所有规则都在同一个配置块里,按顺序匹配:先匹配permitAll的路径,剩下的所有请求都强制要求认证。

2. 检查自定义授权过滤器CustomAuthorizationFilter的逻辑

如果上面的修改后问题还存在,那十有八九是你的CustomAuthorizationFilter没有正确处理未认证的请求。比如,过滤器可能只在有有效令牌时设置认证信息,但在没有令牌或者令牌无效时,直接让请求继续走下去,没触发Spring Security的拦截逻辑。

给你一个正确的过滤器示例参考:

public class CustomAuthorizationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 直接放行登录和注册请求,不用走认证逻辑
        String servletPath = request.getServletPath();
        if (servletPath.startsWith("/api/login") || servletPath.startsWith("/register")) {
            filterChain.doFilter(request, response);
            return;
        }
        
        String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            try {
                String token = authorizationHeader.substring(7);
                // 这里替换成你实际的JWT解析逻辑
                Algorithm algorithm = Algorithm.HMAC256("your-secret-key".getBytes());
                JWTVerifier verifier = JWT.require(algorithm).build();
                DecodedJWT decodedJWT = verifier.verify(token);
                
                String username = decodedJWT.getSubject();
                String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
                
                Collection<GrantedAuthority> authorities = new ArrayList<>();
                Arrays.stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
                
                // 将认证信息存入SecurityContext
                UsernamePasswordAuthenticationToken authToken = 
                    new UsernamePasswordAuthenticationToken(username, null, authorities);
                SecurityContextHolder.getContext().setAuthentication(authToken);
                
                filterChain.doFilter(request, response);
            } catch (Exception e) {
                // 令牌无效时,返回401并终止过滤器链
                response.setHeader("error", e.getMessage());
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                
                Map<String, String> errorResponse = new HashMap<>();
                errorResponse.put("error_message", e.getMessage());
                new ObjectMapper().writeValue(response.getOutputStream(), errorResponse);
                return; // 这里一定要return,不能继续执行后续过滤器
            }
        } else {
            // 没有提供令牌,直接返回401
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            
            Map<String, String> errorResponse = new HashMap<>();
            errorResponse.put("error_message", "No valid authentication token provided");
            new ObjectMapper().writeValue(response.getOutputStream(), errorResponse);
            return;
        }
    }
}

这里的核心点:

  • 当没有令牌或者令牌无效时,必须返回401状态码并终止过滤器链,不能让请求继续走到Spring Security的核心授权过滤器FilterSecurityInterceptor。如果你的过滤器在未认证时直接调用filterChain.doFilter(),Spring Security会认为请求是匿名的,但如果配置逻辑有问题,就会出现请求直接通过的情况。
  • 登录和注册请求要在过滤器里直接放行,避免被不必要的认证逻辑拦截。

3. 验证过滤器执行顺序

另外,确保你的自定义过滤器没有跳过Spring Security的核心授权逻辑。你当前用addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)是没问题的,因为FilterSecurityInterceptor在过滤器链的更后面,会在你的授权过滤器之后执行,确保anyRequest().authenticated()的规则被应用。

总结

先尝试把授权规则改成链式写法,这能解决大部分因多配置块导致的规则匹配问题;如果还没解决,就重点检查CustomAuthorizationFilter的逻辑,确保未认证请求被正确拦截并返回401,而不是让请求继续通过过滤器链。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 11:07:41