Spring Security中http.authorizeRequests().anyRequest().authenticated()行为异常技术问询
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

