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

Spring Boot如何在每次请求时为JWT Token添加自定义验证?

实现JWT Token的IssuedAt与数据库SessionID前置验证

核心思路

要通过@PreAuthorize实现自定义验证且避免重复解析JWT,关键是在认证阶段一次性解析Token并缓存关键信息,后续验证逻辑直接复用缓存数据,无需二次解析Token。

步骤1:改造JWT认证过滤器,缓存IssuedAt信息

在JWT认证过滤器中,解析Token后将issuedAt存入Authentication对象的details字段,后续验证直接从这里读取:

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtTokenProvider jwtTokenProvider;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = extractTokenFromRequest(request);
        if (token != null && jwtTokenProvider.validateToken(token)) {
            // 解析Token获取用户信息和issuedAt
            String username = jwtTokenProvider.getUsernameFromToken(token);
            String tokenIssuedAt = jwtTokenProvider.getIssuedAtDateFromToken(token);
            
            // 加载用户详情
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            
            // 构造Authentication,将issuedAt存入details
            UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new JwtTokenDetails(tokenIssuedAt));
            
            // 存入SecurityContext供后续验证使用
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }

    // 辅助类:存储JWT额外信息
    private static class JwtTokenDetails {
        private final String issuedAt;

        public JwtTokenDetails(String issuedAt) {
            this.issuedAt = issuedAt;
        }

        public String getIssuedAt() {
            return issuedAt;
        }
    }

    // 从请求头/参数中提取Token的方法,根据你的实际实现调整
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

步骤2:自定义Security验证方法

创建组件类,编写验证逻辑,从SecurityContext中取出缓存的issuedAt,与数据库中的sessionID对比:

@Component("customSecurity")
public class CustomSecurityExpressions {
    private final UserSessionService userSessionService; // 负责从数据库查询用户最新sessionID

    public CustomSecurityExpressions(UserSessionService userSessionService) {
        this.userSessionService = userSessionService;
    }

    public boolean isValidTokenIssuedAt() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !(authentication.getDetails() instanceof JwtTokenDetails)) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Invalid authentication");
        }

        JwtTokenDetails tokenDetails = (JwtTokenDetails) authentication.getDetails();
        String tokenIssuedAt = tokenDetails.getIssuedAt();
        String username = authentication.getName();
        
        // 从数据库获取用户最新的sessionID
        String sessionID = userSessionService.getLatestSessionIdByUsername(username);

        if (!tokenIssuedAt.equals(sessionID)) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Session not up-to-date!!");
        }
        return true;
    }
}

步骤3:在接口上应用自定义验证

修改原接口的@PreAuthorize注解,叠加权限校验与自定义验证逻辑:

@PostMapping("/add")
@PreAuthorize("hasAuthority('user_add') && @customSecurity.isValidTokenIssuedAt()")
public void addUser(@RequestBody User user, HttpServletRequest request) {
    userService.addNewUser(user, request);
}

关键说明

  • 避免重复解析:Token仅在过滤器中解析一次,issuedAt被缓存到SecurityContext,后续验证直接读取,无需重复调用解析方法。
  • 双重校验:@PreAuthorize同时保留原有权限校验hasAuthority('user_add')和自定义的isValidTokenIssuedAt()验证,两者都通过才允许访问接口。
  • 异常处理:验证不通过时直接抛出ResponseStatusException,Spring会自动转换为HTTP 403响应。

内容的提问来源于stack exchange,提问作者Ahmed Salah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:09:16