Spring Security:无需认证接口因过期JWT Cookie被拦截问题排查
问题原因分析及解决方案
核心原因
你的JwtAuthenticationFilter会对所有请求执行JWT验证逻辑,包括已被permitAll放行的/api/v1/auth/authenticate接口。当请求携带过期的token Cookie时,JWT解析或验证过程会抛出异常(如ExpiredJwtException),且该异常未被过滤器捕获处理,导致请求提前终止,无法到达AuthController的authenticate方法。
具体逻辑拆解
- 过滤器执行顺序:
JwtAuthenticationFilter被添加在UsernamePasswordAuthenticationFilter之前,会优先于权限校验逻辑执行。即使/api/v1/auth/**被配置为permitAll,过滤器依然会先处理请求。 - 过期Token的触发逻辑:
- 当请求携带名为
token的Cookie时,过滤器会调用jwtService.extractUsername(jwt)和jwtService.isTokenValid(jwt, userDetails)尝试解析验证Token。 - 若Token已过期,这两个方法通常会抛出JWT相关运行时异常。
- 过滤器未捕获这类异常,异常会直接向上传播,触发Spring Security的默认异常处理,返回错误响应,请求无法继续执行到Controller。
- 当请求携带名为
- 无Cookie/修改Cookie名称的情况:此时过滤器会直接执行
filterChain.doFilter(request, response),跳过JWT验证逻辑,请求能正常到达authenticate接口,完成登录并返回新Token。
解决方案
方案1:跳过登录接口的JWT验证
在doFilterInternal方法开头增加路径判断,直接放行登录请求:
@Override protected void doFilterInternal( @NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain ) throws ServletException, IOException { // 跳过登录接口的JWT验证 if (request.getRequestURI().contains("/api/v1/auth/authenticate")) { filterChain.doFilter(request, response); return; } // 原有JWT验证逻辑... }
方案2:捕获JWT异常并放行
如果需要保留对登录接口的Token检查,但允许过期Token继续走到Controller处理,可捕获JWT相关异常:
@Override protected void doFilterInternal( @NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain ) throws ServletException, IOException { final String userEmail; final String jwt; final Cookie jwtCookie = request.getCookies() != null ? Arrays.stream(request.getCookies()).filter(cookie -> cookie.getName().equals("token")) .findFirst() .orElse(null): null; if (jwtCookie == null) { filterChain.doFilter(request, response); return; } jwt = jwtCookie.getValue(); try { userEmail = jwtService.extractUsername(jwt); if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } } catch (ExpiredJwtException | JwtException e) { // Token过期或无效,不设置Authentication,直接放行到Controller } filterChain.doFilter(request, response); }
内容的提问来源于stack exchange,提问作者Davido
相关产品推荐
相关产品推荐

