Spring Session 整合 JWT Token

Spring Session 整合 JWT Token依赖库 pom xmldependenc springframew session groupIdartif session data redis artifactIdve 3 1 RELEASE version

参考文章:http://blog.csdn.net//article/details/

依赖库 pom.xml

 <dependency> <groupId>org.springframework.session 
   groupId> <artifactId>spring-session-data-redis 
   artifactId> <version>1.3.1.RELEASE 
   version> <type>pom 
   type>  
   dependency> <dependency> <groupId>org.springframework 
   groupId> <artifactId>spring-web 
   artifactId> <version>4.3.4.RELEASE 
   version>  
   dependency>

Spring Session 默认的会话策略类是 CookieHttpSessionStrategy,其行为方式与 Tomcat 会话管理大同小异(但是存储方式不同,一个是redis远程存储,一个是堆内存),我们可以通过创建自己的会话策略类来干涉 Spring Session 的会话管理(很遗憾,目前Spring Session不支持自定义sessionId,为了让 jwt token 享受到会话管理,需要手动将两者关联起来)

/ * 自定义的 Spring Session 会话策略 */ public class MyHttpSessionStrategy implements HttpSessionStrategy { 
    //框架默认的会话策略,之所以选择组合的方式,是因为该类是 final,无法继承 private static final CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy(); // 
   
     ,应改成 redis 远程存储管理,此处仅用于演示 
    private static final Map 
  
    token2SessionId = 
   new ConcurrentHashMap<>(); 
   private 
   static 
   final String TOKEN_NAME = 
   "x-jwt-token"; 
   //当获取会话时回调此方法,返回的sessionid用于从redis取得对应的会话对象 
   @Override 
   public String 
   getRequestedSessionId(HttpServletRequest request) { 
   //从HTTP头部或参数获取 jwt token String token = getJwtToken(request); 
   if ( 
   null != token && !token.equals( 
   "")) { 
   //寻找token对应的sessioid String sessionId = token2SessionId.get(token); 
   if ( 
   null != sessionId) { System.out.println( 
   "发现 JWT(" + token + 
   "),其关联会话: " + sessionId); 
   return sessionId; } } 
   //框架默认的会话策略,从Cookie获取sessionid 
   return strategy.getRequestedSessionId(request); } 
   //当新建会话时回调此方法 
   @Override 
   public 
   void 
   onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) { String token = getJwtToken(request); 
   if ( 
   null != token && !token.equals( 
   "")) { String old = token2SessionId.get(token); 
   if ( 
   null != old) { 
   //会话已存在 } 
   else { 
   //将Token关联新会话,至于JWT的校验流程可由后续处理 token2SessionId.put(token, session.getId()); } } 
   //框架默认的会话策略,将sessionid写入cookie strategy.onNewSession(session, request, response); } 
   //当会话过期时回调此方法 
   @Override 
   public 
   void 
   onInvalidateSession(HttpServletRequest request, HttpServletResponse response) { String token = getJwtToken(request); 
   if ( 
   null != token && !token.equals( 
   "")) { token2SessionId.remove(token); } strategy.onInvalidateSession(request, response); } 
   private String 
   getJwtToken(HttpServletRequest request) { 
   return 
   null != request.getHeader(TOKEN_NAME) ? request.getHeader(TOKEN_NAME) : request.getParameter(TOKEN_NAME); } 
   / * * @param token * @param sessionId * @return true表示新旧会话一致; false表示新旧会话不一样,既然旧会话是有效的,呢么新会话就是多余的,可回收 */ 
   public 
   static 
   boolean 
   checkAndSetSessionId(String token, String sessionId) { String old = token2SessionId.get(token); 
   if ( 
   null != old) { 
   //会话已存在 System.out.println( 
   "Token(" + token + 
   ") 会话已存在:" + sessionId + 
   "; 选择无视新会话"); 
   //判断新旧会话是否相同 
   return sessionId.equals(old); } 
   else { 
   //将 JWT Token 与 sessionId 关联起来 token2SessionId.put(token, sessionId); System.out.println( 
   "将计算得到的 Token(" + token + 
   ") 关联 会话id(" + sessionId + 
   ")"); } 
   return 
   true; } } 
  

配置Spring Sessio最核心的一个组件,springSessionRepositoryFilter,其位于过滤链最前端,用于重写会话相关的方法;Spring应用只需要如下代码,其他的例如集成Spring Boot、Spring Security可参考官方示例

public class Initializer extends AbstractHttpSessionApplicationInitializer { 
    public Initializer() { super(Config.class); } }

配置MyHttpSessionStrategy,以覆盖Spring容器中默认的会话策略(CookieHttpSessionStrategy)

@EnableRedisHttpSession public class Config { 
    @Bean public JedisConnectionFactory connectionFactory() { JedisConnectionFactory connection = new JedisConnectionFactory(); connection.setPort(6379); connection.setHostName("127.0.0.1"); // connection.setPassword("password"); connection.setDatabase(8); return connection; } @Bean public HttpSessionStrategy httpSessionStrategy() { return new MyHttpSessionStrategy(); } }

定义一个登录Servlet,用于测试

@WebServlet("/login") public class LoginServlet extends HttpServlet { 
    private static final long serialVersionUID = L; @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String sessionId = req.getSession().getId(); System.out.println("当前会话id为:" + sessionId); String username = req.getParameter("username"); String password = req.getParameter("password"); if (null != username && username.equals("username") && null != password && password.equals("password")){ //模拟 JWT 计算得到 token String token = "HelloWorld"; if (!MyHttpSessionStrategy.checkAndSetSessionId(token, sessionId)){ //表示新旧会话不同,既然旧会话是有效的,呢么新会话就是多余的,可回收 System.out.println("JWT Token已存在会话,可重复使用,当前新会话是多余的"); req.getSession().invalidate(); } } } }

测试开始,先普通地访问Servlet,不传任何参数,输出为

当前会话id为:75f67e86-cf3d-4672-be6c-8fb5c7be80ea 当前会话id为:75f67e86-cf3d-4672-be6c-8fb5c7be80ea 当前会话id为:75f67e86-cf3d-4672-be6c-8fb5c7be80ea 当前会话id为:75f67e86-cf3d-4672-be6c-8fb5c7be80ea 当前会话id为:75f67e86-cf3d-4672-be6c-8fb5c7be80ea

可以看到,与普通的浏览器会话管理一样,只要浏览器不关闭,会话能保持30分钟;

传参 username 和 password,尝试第一次登录

当前会话id为:41aa06d2-13d5-4d13-8251-c8be9a74d687 将计算得到的 Token(HelloWorld) 关联 会话id(41aa06d2-13d5-4d13-8251-c8be9a74d687)

不关闭浏览器,继续尝试第二、第三次重复登录

当前会话id为:41aa06d2-13d5-4d13-8251-c8be9a74d687 Token(HelloWorld) 会话已存在:41aa06d2-13d5-4d13-8251-c8be9a74d687; 选择无视新会话 当前会话id为:41aa06d2-13d5-4d13-8251-c8be9a74d687 Token(HelloWorld) 会话已存在:41aa06d2-13d5-4d13-8251-c8be9a74d687; 选择无视新会话

关闭浏览器,重新启动以模拟不同的客户端,尝试第四、第五次重复登录

当前会话id为:b-7553-4de7-9125-a893b840d16b Token(HelloWorld) 会话已存在:b-7553-4de7-9125-a893b840d16b; 选择无视新会话 JWT Token已存在会话,可重复使用,当前新会话是多余的 当前会话id为:45a66d45-c328-4c1a-a774-f1612c2cd727 Token(HelloWorld) 会话已存在:45a66d45-c328-4c1a-a774-f1612c2cd727; 选择无视新会话 JWT Token已存在会话,可重复使用,当前新会话是多余的

可以看到,每一个 JWT token 都能唯一关联固定的会话,实现了不同客户端共享同一个会话(如果需要其他逻辑,可自定义实现)

最后,传参 x-jwt-token:HelloWorld,实现 token 认证访问

发现 JWT(HelloWorld),其关联会话: 41aa06d2-13d5-4d13-8251-c8be9a74d687 当前会话id为:41aa06d2-13d5-4d13-8251-c8be9a74d687 发现 JWT(HelloWorld),其关联会话: 41aa06d2-13d5-4d13-8251-c8be9a74d687 发现 JWT(HelloWorld),其关联会话: 41aa06d2-13d5-4d13-8251-c8be9a74d687 当前会话id为:41aa06d2-13d5-4d13-8251-c8be9a74d687 发现 JWT(HelloWorld),其关联会话: 41aa06d2-13d5-4d13-8251-c8be9a74d687
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215646.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午1:45
下一篇 2026年3月18日 下午1:46


相关推荐

  • IDEA炫酷主题推荐!非常好看![通俗易懂]

    IDEA炫酷主题推荐!非常好看![通俗易懂]IntelliJIDEA介绍IDEA,全称IntelliJIDEA,是Java语言的集成开发环境,IDEA在业界被公认为是最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、创新的GUI设计等方面的功能可以说是超常的。*IDEA的官网下载地址:IDEA下载地址平时开发中由…

    2022年5月31日
    93
  • 解决Execution failed for task ‘:app:checkDebugDuplicateClasses‘.

    解决Execution failed for task ‘:app:checkDebugDuplicateClasses‘.

    2021年10月2日
    56
  • VS2013 产品密钥 – 所有版本

    VS2013 产品密钥 – 所有版本 VisualStudioUltimate2013KEY(密钥):BWG7X-J98B3-W34RT-33B3R-JVYW9VisualStudioPremium2013KEY(密钥):FBJVC-3CMTX-D8DVP-RTQCT-92494VisualStudioProfessional2013 KEY(密钥): XD…

    2022年4月3日
    7.5K
  • 阿里云申请免费ssl证书及安装_阿里云服务器升级配置

    阿里云申请免费ssl证书及安装_阿里云服务器升级配置阿里云购买免费SSL证书1.操作环境2.购买免费SSL证书3.证书申请,绑定域名《下一篇:Nginx+SSL证书,配置https》1.操作环境阿里云(已实名的企业账号)2.购买免费SSL证书2.1.如图,阿里云官网登陆后,搜索框内输入SSL,点击证书控制台,进入后点击购买证书2.2.如图,选中单域名–DVSSL–免费版,点击立即购买2.3.如图,勾选阅读,点击支付,在支付成功界面,点击证书控制台3.证书申请,绑定域名3.1.如图,在证书控制台,点击证书申请

    2022年10月3日
    6
  • 什么是udp攻击?udp攻击的基本原理是什么

    什么是udp攻击?udp攻击的基本原理是什么UDPFlood关联TCP类服务防范UDP是无连接的协议,因此无法通过源认证的方法防御UDPFlood攻击。如果UDP业务流量需要通过TCP业务流量认证或控制,则当UDP业务受到攻击时,对关联的TCP业务强制启动防御,用此TCP防御产生的白名单决定同一源的UDP报文是丢弃还是转发。  比如,有些服务例如游戏类服务,是先通过TCP协议对用户进行认证,认证通过后使用UDP协议传输…

    2022年10月2日
    4
  • ComponentName的用法

    ComponentName的用法在android应用中打开别的应用我当时第一眼看到的时候是懵逼的,这是个什么玩意开启界面不都是startActivity(intent)的吗?后来才反应过来不是要起自己界面中的Activity

    2022年7月1日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号