Spring Boot中permitAll接口调用成功却记录未授权错误的原因?
问题分析与解决方案
问题原因
当调用标记permitAll()的/api/v1/teams/**端点时,日志出现未授权错误但请求能正常返回的核心原因在于Spring Security过滤器链的执行逻辑:
- 自定义的
AuthTokenFilter在AnonymousAuthenticationFilter之前执行,当请求无JWT时,不会向SecurityContext中写入任何认证信息。 - 后续的
FilterSecurityInterceptor检查权限时,发现SecurityContext中无认证对象,抛出InsufficientAuthenticationException。 ExceptionTranslationFilter捕获该异常后,因当前认证状态为null(而非标准匿名认证),会触发AuthEntryPointJwt记录错误日志。- 由于
HttpSecurity中配置了permitAll(),请求最终仍会被允许通过并返回正常响应,但错误日志已被记录。
解决方案
方式一:让AuthTokenFilter跳过无需认证的路径
修改AuthTokenFilter,重写shouldNotFilter方法,指定无需过滤的路径:
public class AuthTokenFilter extends OncePerRequestFilter { // ... 原有代码 @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getServletPath(); return path.startsWith("/api/v1/teams/"); } }
此方式下,/api/v1/teams/**的请求会跳过AuthTokenFilter,后续的AnonymousAuthenticationFilter会自动创建标准匿名认证,FilterSecurityInterceptor检查时不会抛出异常,避免错误日志。
方式二:在AuthTokenFilter中手动设置匿名认证
若不想跳过过滤,可在无JWT的请求中手动写入匿名认证信息到SecurityContext:
public class AuthTokenFilter extends OncePerRequestFilter { private static final String ANONYMOUS_USER = "anonymousUser"; private static final List<GrantedAuthority> ANONYMOUS_AUTHORITIES = Collections.singletonList(new SimpleGrantedAuthority("ROLE_ANONYMOUS")); @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain ) throws ServletException, IOException { try { String jwt = parseJwt(request); if (jwt != null && jwtUtils.validateJwtToken(jwt)) { // 原有设置认证的逻辑 String username = jwtUtils.getUserNameFromJwtToken(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } else { // 手动设置匿名认证 UsernamePasswordAuthenticationToken anonymousAuth = new UsernamePasswordAuthenticationToken( ANONYMOUS_USER, null, ANONYMOUS_AUTHORITIES ); anonymousAuth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(anonymousAuth); } } catch (Exception e) { logger.error("Cannot set user authentication: {}", e); } filterChain.doFilter(request, response); } // ... 原有代码 }
这样FilterSecurityInterceptor检查时会识别到标准匿名认证,结合permitAll()配置,不会触发异常日志。
内容的提问来源于stack exchange,提问作者OD1995
相关产品推荐
相关产品推荐

