前段时间,写了篇<spring-session-data-redis解决session共享的问题>文章,介绍了spring-session使用redis存储实现session共享的内部机制,

遇到很多人提问到jdbc的实现或者为什么只有理论没有实践?索性就实践一下jdbc方式。

1.spring-session实现了使用不同存储介质保存共享的session的api

spring-session实现session共享的实现有三种:

jdbc:使用数据库做介质,支持多种数据,如oracle,sqlserver,mysql等,对应的实现类

RedisOperationsSessionRepository

                  <图一 支持的数据库类型及脚本>

redis:使用redis做存储介质,对应的实现类为:

JdbcOperationsSessionRepository

Hazelcast:使用内存做存储介质,对应的实现类为:

HazelcastSessionRepository

Hazelcast作为一个高度可扩展的数据分发和集群平台,提供了高效的、可扩展的分布式数据存储、数据缓存。Hazelcast是开源的,在分布式技术方面,Hazelcast提供了十分友好的接口供开发者选择,如Map,Queue,ExecutorService, Lock和Jcache。
Hazelcast的稳定性很高,分布式应用可以使用Hazelcast进行存储数据、同步数据、发布订阅消息等。Hazelcast是基于Java开发的,其客户端有Java, C/C++, .NET以及REST。Hazelcast同时也支持memcache协议。它很好的支持了Hibernate,可以很容易的在当今流行的数据库系统中应用。

2.spring-session-jdbc使用mysql保存共享session的实现实践

2.0 准备工作

  如<图一 支持的数据库类型及脚本>所示,找到schema-mysql.sql脚本,放入sql-client客户端执行,如sqlyog,红色部分是我自己添加的,为了可以重复执行的。

DROP TABLE IF EXISTS SPRING_SESSION_ATTRIBUTES;
DROP TABLE IF EXISTS SPRING_SESSION;
CREATE TABLE SPRING_SESSION (PRIMARY_ID CHAR(36) NOT NULL,SESSION_ID CHAR(36) NOT NULL,CREATION_TIME BIGINT NOT NULL,LAST_ACCESS_TIME BIGINT NOT NULL,MAX_INACTIVE_INTERVAL INT NOT NULL,EXPIRY_TIME BIGINT NOT NULL,PRINCIPAL_NAME VARCHAR(100),CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=INNODB ROW_FORMAT=DYNAMIC;CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);CREATE TABLE SPRING_SESSION_ATTRIBUTES (SESSION_PRIMARY_ID CHAR(36) NOT NULL,ATTRIBUTE_NAME VARCHAR(200) NOT NULL,ATTRIBUTE_BYTES BLOB NOT NULL,CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=INNODB ROW_FORMAT=DYNAMIC;

此时创建了两张表

  SPRING_SESSION

  SPRING_SESSION_ATTRIBUTES

 通过PRIMARY_ID关联,执行结果如下:

2.1 使用sts创建spring-boot项目,项目名称session-jdbc,使用jdbc,mysql,web(方便测试)的start,创建完项目如下:

2.2 添加spring-session-jdbc依赖

        <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-jdbc</artifactId></dependency>

2.3 配置jdbc属性application.properties

spring.session.store-type=JDBC
spring.datasource.url=jdbc:mysql://localhost:3306/www?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wangwei456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.session.jdbc.cleanup-cron=0 * * * * *

注意,新版mysql的url需要增加

serverTimezone选项

而且,driver-class-name也变为com.mysql.cj.jdbc.Driver,否则有报警。

2.4 创建测试controller

package com.example.demo;import javax.servlet.http.HttpSession;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/jdbc")
public class TestController {@RequestMapping("/welcome")public String welcome(HttpSession httpSession) {httpSession.setAttribute("hello", "world");return "hello world !";}
}

2.5 测试

启动spring-boot项目session-jdbc,访问http://localhost:8080/jdbc/welcome/,返回"hello world".

查看数据库数据信息

SPRING_SESSION表信息

SPRING_SESSION_ATTRIBUTES表信息

2.6 Q&A

 2.6.1 session_id是如何产生的?

断点调试进入

此时的cookie和请求的一致。

进入HttpSessionAdapter的setAttribute属性方法,再调用JdbcOperationsSessionRepository的setAttribute方法

@Overridepublic void setAttribute(String attributeName, Object attributeValue) {boolean attributeExists = (this.delegate.getAttribute(attributeName) != null);boolean attributeRemoved = (attributeValue == null);if (!attributeExists && attributeRemoved) {return;}if (attributeExists) {if (attributeRemoved) {this.delta.merge(attributeName, DeltaValue.REMOVED, (oldDeltaValue,deltaValue) -> (oldDeltaValue == DeltaValue.ADDED) ? null: deltaValue);}else {this.delta.merge(attributeName, DeltaValue.UPDATED,(oldDeltaValue,deltaValue) -> (oldDeltaValue == DeltaValue.ADDED)? oldDeltaValue: deltaValue);}}else {this.delta.merge(attributeName, DeltaValue.ADDED,(oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED)? oldDeltaValue: DeltaValue.UPDATED);}this.delegate.setAttribute(attributeName, value(attributeValue));if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||SPRING_SECURITY_CONTEXT.equals(attributeName)) {this.changed = true;}}

调用内部类JdbcSession的构造方法

        JdbcSession() {this.delegate = new MapSession();this.isNew = true;this.primaryKey = UUID.randomUUID().toString();}

其中primarykey的产生,是随机产生的,而jdbcSession的一个成员变量delegate是MapSession的实例

    /*** Creates a new instance with a secure randomly generated identifier.*/public MapSession() {this(generateId());}

而generateId()方法,调用了随机uuid的产生

    private static String generateId() {return UUID.randomUUID().toString();}

2.6.2 为何和cookie的不一致?

response中返回的cookie值为:NDM1Mjg1YjAtMmVjMi00MmQ0LWJjY2UtYzFjY2U4NzQzNDNm

数据库中sessionId为435285b0-2ec2-42d4-bcce-c1cce874343f

为什么会这样呢?

    @Overridepublic List<String> readCookieValues(HttpServletRequest request) {Cookie[] cookies = request.getCookies();List<String> matchingCookieValues = new ArrayList<>();if (cookies != null) {for (Cookie cookie : cookies) {if (this.cookieName.equals(cookie.getName())) {String sessionId = (this.useBase64Encoding? base64Decode(cookie.getValue()): cookie.getValue());if (sessionId == null) {continue;}if (this.jvmRoute != null && sessionId.endsWith(this.jvmRoute)) {sessionId = sessionId.substring(0,sessionId.length() - this.jvmRoute.length());}matchingCookieValues.add(sessionId);}}}return matchingCookieValues;}

从代码中我们可以知道,中间查了一个base64

435285b0-2ec2-42d4-bcce-c1cce874343f base64加密后即为:NDM1Mjg1YjAtMmVjMi00MmQ0LWJjY2UtYzFjY2U4NzQzNDNm

3.总结

1.浏览器第一次请求的时候,服务端创建一个session,并且将名为SESSION的属性作为响应cookie返回浏览器

2. 浏览器保存名称为SESSION的cookie作为唯一标识,下次访问服务端时带上此cookie

3.服务端session中的名为SESSION的属性和浏览器保存的cookie是不一致的,为保密原因,进行了base64加密。

4.spring-session使用了简单的配置,可以实现session持久化到数据库。

参考文献:

【1】https://angelbill3.iteye.com/blog/2342989

转载于:https://www.cnblogs.com/davidwang456/p/10361550.html

spring-session用mysql实现session共享实践相关推荐

  1. Spring Session + Redis实现分布式Session共享

    2019独角兽企业重金招聘Python工程师标准>>> 通常情况下,Tomcat.Jetty等Servlet容器,会默认将Session保存在内存中.如果是单个服务器实例的应用,将S ...

  2. Spring boot - 使用redis实现session共享

    在分布式系统架构的系统中,我们如何保证session的一致性,其中之一的解决方式就是session共享形式,在SpringBoot框架中如何使用session达成共享呢,我们可以借助指定Redis实现 ...

  3. Spring Session - Cookie VS Session VS Token 以及 Session不一致问题的N种解决方案

    文章目录 Cookie VS Session VS Token History Cookie Session Token Session不一致问题 Session不一致解决方案 nginx sessi ...

  4. [Java][web]利用Spring随时随地获得Request和Session

    利用Spring随时随地获得Request和Session 一.准备工作: 在web.xml中加入 <listener> <listener-class> org.spring ...

  5. spring+redis自主实现分布式session(非spring-session方式)

    为什么80%的码农都做不了架构师?>>>    背景:最近对一个老项目进行改造,使其支持多机部署,其中最关键的一点就是实现多机session共享.项目有多老呢,jdk版本是1.6,s ...

  6. 基于Spring Session实现JIM分布式Session

    基于Spring Session实现JIM分布式Session 前引 在实际项目中,应用程序经常会以集群方式部署线上,一般来说无状态的应用程序是理想的部署方式,一旦应用程序拥有状态(比如Session ...

  7. 集群共享session;shiro实现session共享;springboot实现redis共享session;

    shiro实现共享session;springboot集成redis共享session;集群环境下shiro共享session 一.实现session共享 1. 聊聊session共享 2. shir ...

  8. 将tomcat的session信息通过memcached实现共享

    为什么80%的码农都做不了架构师?>>>    1.先学大拿来点介绍 MSM(memcached-session-manager)支持tomcat6和tomcat7 ,利用Value ...

  9. spring session 退出登录 清理session

    2019独角兽企业重金招聘Python工程师标准>>> spring session 退出登录 清理session 博客分类: spring /*** Allows creating ...

  10. 使用MySql保存session

    2019独角兽企业重金招聘Python工程师标准>>> 本文来源于:http://www.lai18.com/content/433951.html 本文实例讲述了php使用MySQ ...

最新文章

  1. Ajax实现无刷新树
  2. 单端信号和差分信号的区别
  3. Android-2D绘图
  4. 【Socket网络编程】5.单播、多播(组播)、广播
  5. 实车采集的数据重建场景_SIGGRAPH | 多机器人协同三维场景重建
  6. inotify 机制
  7. JavaScriptBreak 语句 continue 语句
  8. 数据库技术:数据存储和查询知识笔记
  9. Linux工作笔记032---Centos7.3/8.2 下安装mysql_不局限于MySql版本
  10. 如何解决Greenplum中无法通过标准命令修复的元数据错误
  11. 国企招聘 | NLP、语音和CV等算法岗位招聘实习生和应届全职生
  12. xd使用技巧_真香!3个技巧,帮你获得面试机率提升300%
  13. 软件开发版本号命名规则
  14. 三调业务摘要201709
  15. 2021Java实现关注公众号登陆网站
  16. 中信证券:降准并非货币宽松 缺口或达9000亿
  17. 序贯蒙特卡洛的粒子简并性问题
  18. 数据传输 -- 字符串报文
  19. word里边的表格怎么弄到ppt里
  20. [Git] 配置Github Gitee reference加速

热门文章

  1. 卡写入速度_看清商家买相机送SD卡的套路,一文教你掌握存储卡选购秘诀
  2. html 中写样式,css样式中黑体怎么写
  3. 找出重复最多的字符php,javascript获取重复次数最多的字符_javascript技巧
  4. sql left join 去重_混入了一些奇怪的东西?SQL小技巧之数据去重
  5. 诚毅学院全国计算机考试,集美大学2017年9月全国计算机等级考试报名时间
  6. java9.0.1教学,零基础Java基础教程【9天入门】
  7. sklearn支持gpu_Keras Sklearn随机搜索GPU OOM
  8. 三目运算法求一个大值,以及指定位数的应用,以及函数的声明,以及函数的嵌套,以及函数的递归,以及用递归法求阶乘
  9. 读计算机平面设计要什么文化好,浅谈计算机平面设计的有关论文
  10. echarts 弹出放大_echarts 如何让饼图hover放大效果一直显示