如何通过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
相关产品推荐
相关产品推荐

