http是无状态协议。
浏览器请求Tomcat,如果Cookie中sessionId为空,tomcat创建sessionId并存入JVM中session容器对象,之后返回sessionId给浏览器。
浏览器再请求时,Cookie中包含sessionId给Tomcat,Tomcat获取并查看session容器对象中是否存在。以判断该sessionId是否有效。
Nginx做负载均衡时,会自动分配给多个Tomcat。如果sessionId存在第一个Tomcat中,再多次请求时,分配给第二个Tomcat,会导致第二个sessionId找不到。导致session丢失。
解决上面问题,就把session不再存在JVM的session容器中,改存到session对象中间件中,如数据库、Redis。
Spring session提供一组API和实现。他把servlet容器实现的httpSession替换为spring-session.用于解决session管理问题,session信息存储于Redis。
1,创建session不共享示例
编写一个get/set 的servlet,启动两次,分别为8080、8081.
8080设置,从8081读取,此时8081读取不到。
1)创建一个maven-web项目
2)添加依赖
<!-- servlet依赖的jar包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!-- jsp依赖 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> <!-- jstl依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
3)写get/set
get
/** * @author senrsl * @ClassName: TestGetServlet * @Package: dc.test * @CreateTime: 2022/6/30 17:07 */ @WebServlet(urlPatterns = "/get") public class TestGetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //super.doPost(req, resp); String testSessionKey = String.valueOf(req.getSession().getAttribute("testSessionKey")); resp.getWriter().println("读到的session is " + testSessionKey); } }
set
/** * @author senrsl * @ClassName: TestSetServlet * @Package: dc.test * @CreateTime: 2022/6/30 17:00 */ @WebServlet(urlPatterns = "/set") public class TestSetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //super.doPost(req, resp); req.getSession().setAttribute("testSessionKey", "sessionValueTest"); resp.getWriter().println("set session OK !"); } }
4)配置tomcat
配置Tomcat路径,端口8080
配图1
配置项目部署方式
配图2
5)启动
http://localhost:8080/TestSpringSession_war_exploded/get
访问,获取get/set 可以读取到手动设置的session
6)再启动一个Tomcat
复制,端口改成8081、10991
访问
http://localhost:8081/TestSpringSession_war_exploded/get
2,配置Spring session
添加依赖
<!--Spring session redis --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.1.RELEASE</version> </dependency> <!-- spring web--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.16.RELEASE</version> </dependency>
web.xml添加过滤器与监听器
<!-- spring session 过滤器 --> <filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- spring配置文件,如果是SpringMVC,不需要启动监听器 -->? <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
applicationContext.xml引入springsession.xml
<import resource="springSession.xml"/>
springSession.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 启动注解支持 --> <context:annotation-config /> <!-- 定义SpringSession Redis配置Bean --> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration" id="redisHttpSessionConfiguration"> </bean> <!-- jedis,用于连接redis --> <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" id="jedisConnectionFactory"> <property name="hostName" value="${redis.hostName}" /> <property name="port" value="${redis.port}" /> <!-- <property name="password" value="${redis.password}" />--> <!-- <property name="usePool" value="${redis.usePool}" />--> <!-- <property name="timeout" value="${redis.timeout}" />--> </bean> <!-- 读取Redis配置文件 --> <context:property-placeholder location="classpath:redis.properties" /> </beans>
3)启动
Redis一定要注意连接的是外部机器IP,且不是slave,不然写不进去。
READONLY You can't write against a read only replica.
不知道为什么,Redis设置为master后明明已经存进去了,然后Tomcat突然崩了一下,回去看Redis又变成slave了。。。。
貌似是因为开了哨兵。。。。
关了哨兵就好了。。。。
http://localhost:8080/TestSpringSession_war_exploded/set
http://localhost:8081/TestSpringSession_war_exploded/get
3,redis存储的内容
192.168.0.121:6380> keys *
1)
"spring:session:sessions:expires:f4d1fbf0-4591-4d07-9d23-dfad13afeeb2"
2) "spring:session:expirations:1656585540000"
3) "spring:session:sessions:f4d1fbf0-4591-4d07-9d23-dfad13afeeb2"
192.168.0.121:6380>
4,解决同域名不同项目path
如 localhost/ProjectA/、localhost/ProjectB/、会分别生成自己的Cookie,存储在 /projectA/、/projectB/
也就是Cookie path不同。
<!-- 定义SpringSession Redis配置Bean --> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration" id="redisHttpSessionConfiguration"> <!-- session最大生命周期,单位秒 --> <property name="maxInactiveIntervalInSeconds" value="1800" /> <!-- 注入Cookie序列化规则对象 --> <property name="cookieSerializer" ref="defaultCookieSerializer" /> </bean> <!-- 改变Cookie存放规则 --> <bean class="org.springframework.session.web.http.DefaultCookieSerializer" id="defaultCookieSerializer"> <!--指定Cookie的sessionId存放在域名根路径,实现同域名不同项目session共享 --> <!-- 解决 localhost/projectA/、localhost/projectB/ session共享问题 --> <property name="cookiePath" value="/" /> </bean>
5,解决不同二级域名
如 bj.58.com、tj.58.com
也就是Cookie domain不同。修改存放到根域名下。
<!-- 改变Cookie存放规则 --> <bean class="org.springframework.session.web.http.DefaultCookieSerializer" id="defaultCookieSerializer"> <!--指定Cookie的sessionId存放在域名根路径,实现同域名不同项目session共享 --> <!-- 解决 localhost/projectA/、localhost/projectB/ session共享问题 --> <property name="cookiePath" value="/" /> <!-- 解决不同二级域名生成不同Cookie问题,存放在根域名下 --> <!-- 如 bj.58.com、tj.58.com --> <property name="domainName" value="test.com" /> </bean>
6,单点登录不同域名
如 tmail.com 和 taobao.com 共享session
Single Sign On SSO
Spring Session不支持。
7,Springboot集成
依赖
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-core</artifactId> </dependency> <!-- springboot 集成 Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- spring session 到 Redis --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
配置
spring.redis.host=192.168.0.121 spring.redis.port=6380 #springsession 30分钟 默认30分钟 #server.servlet.session.timeout=30m # 同域名不同项目 #server.servlet.session.cookie.path=/ # 同根不同子域名 #server.servlet.session.cookie.domain=test.com
请求
@RestController public class TestSpringSessionController { @RequestMapping("/set") public Object set(HttpSession session) { session.setAttribute("TestSessionKey", "sessionValue"); return "session设置完成"; } @RequestMapping("/get") public Object get(HttpSession session) { String testSessionKey = (String) session.getAttribute("TestSessionKey"); return "session get is " + testSessionKey; } }
启动
请求。。。。
http://localhost:8080/get
http://localhost:8081/get
没有对比就没有伤害。。。。
简单到吓人。。。。
senRsl
2022年06月30日16:12:00
没有评论 :
发表评论