spring session mysql_spring-session用mysql实现session共享实践

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

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

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

RedisOperationsSessionRepository

868edd82cc5e016b1b42a1b1954be50c.png

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 TABLESPRING_SESSION (

PRIMARY_IDCHAR(36) NOT NULL,

SESSION_IDCHAR(36) NOT NULL,

CREATION_TIMEBIGINT NOT NULL,

LAST_ACCESS_TIMEBIGINT NOT NULL,

MAX_INACTIVE_INTERVALINT NOT NULL,

EXPIRY_TIMEBIGINT NOT NULL,

PRINCIPAL_NAMEVARCHAR(100),CONSTRAINT SPRING_SESSION_PK PRIMARY KEY(PRIMARY_ID)

) ENGINE=INNODB ROW_FORMAT=DYNAMIC;CREATE UNIQUE INDEX SPRING_SESSION_IX1 ONSPRING_SESSION (SESSION_ID);CREATE INDEX SPRING_SESSION_IX2 ONSPRING_SESSION (EXPIRY_TIME);CREATE INDEX SPRING_SESSION_IX3 ONSPRING_SESSION (PRINCIPAL_NAME);CREATE TABLESPRING_SESSION_ATTRIBUTES (

SESSION_PRIMARY_IDCHAR(36) NOT NULL,

ATTRIBUTE_NAMEVARCHAR(200) NOT NULL,

ATTRIBUTE_BYTES BLOBNOT 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关联,执行结果如下:

5551b276e224b00d07c99064fbc5b802.png

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

2774fdac8fed0248e0f545e4dd0b2ea7.png

2.2 添加spring-session-jdbc依赖

org.springframework.session

spring-session-jdbc

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.Driver

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

serverTimezone选项

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

2.4 创建测试controller

packagecom.example.demo;importjavax.servlet.http.HttpSession;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping("/jdbc")public classTestController {

@RequestMapping("/welcome")publicString welcome(HttpSession httpSession) {

httpSession.setAttribute("hello", "world");return "hello world !";

}

}

2.5 测试

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

ffb0755acbd2bc68e6934b5fb6493e2a.png

查看数据库数据信息

SPRING_SESSION表信息

e083825764e55ee5ec32bd5402464b77.png

SPRING_SESSION_ATTRIBUTES表信息

5cd715930ecd56a42f5aa263fca3162c.png

2.6 Q&A

2.6.1 session_id是如何产生的?

断点调试进入

d815907b81886fb84d15e016a81c3124.png

此时的cookie和请求的一致。

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

@Overridepublic voidsetAttribute(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 = newMapSession();this.isNew = true;this.primaryKey =UUID.randomUUID().toString();

}

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

/*** Creates a new instance with a secure randomly generated identifier.*/

publicMapSession() {this(generateId());

}

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

private staticString generateId() {returnUUID.randomUUID().toString();

}

2.6.2 为何和cookie的不一致?

response中返回的cookie值为:NDM1Mjg1YjAtMmVjMi00MmQ0LWJjY2UtYzFjY2U4NzQzNDNm

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

为什么会这样呢?

@Overridepublic ListreadCookieValues(HttpServletRequest request) {

Cookie[] cookies=request.getCookies();

List 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);

}

}

}returnmatchingCookieValues;

}

从代码中我们可以知道,中间查了一个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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值