東川印記

一本東川,笑看爭龍斗虎;寰茫兦者,度橫佰昧人生。

简单学习SpringSession

2022年6月30日星期四



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

没有评论 :

发表评论