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

如何通过Session ID从Spring Data Redis获取Session并验证有效性

问题

我有一个服务方法会将用户数据作为Session数据存入Spring Data Redis,随后生成包含Session ID的JWT。当调用另一个微服务时,该微服务的JWT过滤器需要通过提取的Session ID从Redis获取Session并验证其有效性,以下是当前的JWT过滤器代码:

public class RequestValidationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token  = request.getHeader("AUTHORIZATION");
        if(!token.isEmpty()){
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey("superdupersecretkey")
                        .parseClaimsJws(token).getBody();
                String username = String.valueOf(claims.get("username"));
                String authorities = (String) claims.get("authorities");
                String sessionId = (String) claims.get("sessionId");
               
                    Authentication auth = new UsernamePasswordAuthenticationToken(username,null,
                            AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
                    SecurityContextHolder.getContext().setAuthentication(auth);
            

            }catch (Exception e) {
                throw new BadCredentialsException("Invalid Token received!");
            }
        }
        filterChain.doFilter(request, response);
    }
}

请问如何实现Session的获取与有效性验证?能否从Redis缓存中获取Session对象和负载?

解决方案

一、Session获取与有效性验证实现步骤

1. 注入Redis操作组件

在过滤器中注入Spring Data Redis提供的StringRedisTemplate(存储JSON字符串时用)或RedisTemplate(存储序列化对象时用),用于操作Redis缓存:

@Autowired
private StringRedisTemplate redisTemplate;

// 若存储自定义Session对象,可使用泛型RedisTemplate
// @Autowired
// private RedisTemplate<String, UserSession> redisTemplate;

2. 新增Session验证逻辑

解析JWT得到sessionId后,以此为键从Redis查询Session数据,完成有效性校验:

  • 若查询结果为空,说明Session已过期或不存在,抛出认证异常
  • 可选:校验Session内的关键信息(如用户名)与JWT中的数据一致,防止Session被冒用

修改后的核心代码如下:

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {

    String token = request.getHeader("AUTHORIZATION");
    if (token != null && !token.isEmpty()) {
        try {
            // 处理JWT的Bearer前缀(如果存在)
            if (token.startsWith("Bearer ")) {
                token = token.substring(7);
            }

            Claims claims = Jwts.parser()
                    .setSigningKey("superdupersecretkey")
                    .parseClaimsJws(token)
                    .getBody();
            String username = String.valueOf(claims.get("username"));
            String authorities = (String) claims.get("authorities");
            String sessionId = (String) claims.get("sessionId");

            // 从Redis获取Session数据
            String sessionJson = redisTemplate.opsForValue().get(sessionId);
            // 若存储的是自定义对象,直接获取:UserSession session = redisTemplate.opsForValue().get(sessionId);

            // 验证Session有效性
            if (sessionJson == null) {
                throw new BadCredentialsException("Session expired or does not exist");
            }

            // 可选:校验Session与JWT的用户名一致性
            // ObjectMapper objectMapper = new ObjectMapper();
            // UserSession session = objectMapper.readValue(sessionJson, UserSession.class);
            // if (!session.getUsername().equals(username)) {
            //     throw new BadCredentialsException("Session does not match token user");
            // }

            // 设置安全上下文认证信息
            Authentication auth = new UsernamePasswordAuthenticationToken(
                    username, null,
                    AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)
            );
            SecurityContextHolder.getContext().setAuthentication(auth);

        } catch (JwtException | IllegalArgumentException e) {
            throw new BadCredentialsException("Invalid Token received!");
        }
    }
    filterChain.doFilter(request, response);
}

3. 优化异常处理(可选)

可以自定义SessionExpiredException异常类,更精准地标识Session失效场景:

public class SessionExpiredException extends RuntimeException {
    public SessionExpiredException(String message) {
        super(message);
    }
}

二、从Redis获取Session对象和负载的可行性

完全可以实现,分两种存储场景处理:

  • JSON字符串存储:如果存入Redis时将Session对象序列化为JSON字符串,取出后可通过ObjectMapper反序列化为对应的Java对象(如自定义的UserSession类),从而获取完整的Session负载。
  • 序列化对象存储:若使用RedisTemplate直接存储Java对象(默认JDK序列化),取出时可直接得到Session对象,但这种方式可读性差、跨语言兼容性弱,建议优先使用JSON序列化存储。

注意:存入Redis时需设置与Session有效期匹配的过期时间,让Redis自动清理过期Session,避免无效数据堆积。

内容的提问来源于stack exchange,提问作者D.Anush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 05:15:33