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

Spring Security:无需认证接口因过期JWT Cookie被拦截问题排查

问题原因分析及解决方案

核心原因

你的JwtAuthenticationFilter会对所有请求执行JWT验证逻辑,包括已被permitAll放行的/api/v1/auth/authenticate接口。当请求携带过期的token Cookie时,JWT解析或验证过程会抛出异常(如ExpiredJwtException),且该异常未被过滤器捕获处理,导致请求提前终止,无法到达AuthController的authenticate方法。

具体逻辑拆解

  1. 过滤器执行顺序:JwtAuthenticationFilter被添加在UsernamePasswordAuthenticationFilter之前,会优先于权限校验逻辑执行。即使/api/v1/auth/**被配置为permitAll,过滤器依然会先处理请求。
  2. 过期Token的触发逻辑:
    • 当请求携带名为token的Cookie时,过滤器会调用jwtService.extractUsername(jwt)和jwtService.isTokenValid(jwt, userDetails)尝试解析验证Token。
    • 若Token已过期,这两个方法通常会抛出JWT相关运行时异常。
    • 过滤器未捕获这类异常,异常会直接向上传播,触发Spring Security的默认异常处理,返回错误响应,请求无法继续执行到Controller。
  3. 无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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:05:13