Java -- SpringSession实现session共享

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java -- SpringSession实现session共享相关的知识,希望对你有一定的参考价值。

在集群系统中,经常需要将 Session 进行共享。不然会出问题:用户在系统A上登陆以后,假如后续的一些操作被负载均衡到系统B上面,系统B发现本机上没有这个用户的 Session ,会强制让用户重新登陆。

如在同域名,同项目中,端口号不同;8081 set session

8081 get session 

8082 get session  是 null 

Cookie与Session

HTTP 协议是一种无连接的协议,当客户端发出一个请求时,它们之间就会建立一个连接,等服务器响应了这个请求,这个连接就会被断开,这时候服务器再也不记得先前与客户端的那次亲密接触,一些用户信息当然也就消失了。

Cookie的诞生是为了解决HTTP无状态的特性无法满足交互式Web,它可以把用户的信息储存起来。主要用于会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息);个性化设置(如用户自定义设置、主题等);浏览器行为跟踪(如跟踪分析用户行为等)。服务器把用户登录的信息保存到客户端的 Cookie 中,这样用户感觉这个网站已经记着了自己。但是 Cookie 有它的缺点不宜存储过长的数据。

Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。

Cookie 是以文件的形式保存在客户端的磁盘上,所以一 些重要数据很容易被修改,比如用户购买一些东西之后,修改自己的余额,然后提交给服务器,这种行为是一定不能允许的。

Session 就能保证数据的安 全,因为它是保存在服务器上的,服务器通过一个唯一的 SessionID 来区别不同的用户。这个 SessionID 就保存在客户端的 Cookie中(默认)或者重定向到URL里。

如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表即可。

Cookie与Session使用场景

Cookie技术可以将信息存储在不同的浏览器中,并且可以实现多次请求下的数据共享,分为临时Cookie和长久Cookie。如果一个Cookie没有设置有效期,那么浏览器在关闭时就会删除这个Cookie,这种Cookie叫做临时Cookie;如果Cookie设置了有效期,那么浏览器会一直保存这个Cookie,直到有效期为止,这种Cookie叫做长久Cookie。

Session是一种建立在Cookie之上的通信状态保留机制,可以实现在服务端存储某个用户的一些信息。服务器创建Session后,将Session的id以Cookie的形式返回给浏览器,只要浏览器不关,再去访问服务器时,就会携带着Session的id,服务器发现浏览器带Session的id过来,就会使用内存中与之对应的Session为之服务。

分布式 Session共享方案

1、使用容器扩展插件来实现,比如基于Tomcat的tomcat-redis-session-manager插件,基于Jetty的jetty-session-redis插件、memcached-session-manager插件;这个方案的好处是对项目来说是透明的,无需改动代码,但是由于过于依赖容器,一旦容器升级或者更换意味着又得重新配置

其实底层是,复制session到其它服务器,所以会有一定的延迟,也不能部署太多的服务器

2、使用 nginx 中的 IP 绑定策略(Ip_Hash),同一个 IP 只能在指定的同一个机器访问(单台机器的负载可能很高,水平添加机器后,请求可能会被重新定位到一台机器上还是会导致 Session 不能顺利共享)

3、使用 Token 代替 Session(也是比较推荐的方案,但不是本文的重点)

4、本文推荐使用 Spring-Session 集成好的解决方案,将Session存放在Redis中进行共享

首先,创建一个maven web工程

1、添加依赖如下:

<dependencies>

    <!-- servlet依赖的jar包 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>

    <!-- jsp依赖jar包 -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.1</version>
    </dependency>

    <!--jstl标签依赖的jar包 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- taglibs 标签库依赖-->
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

    <!-- 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>
    <!-- Spring Session相关的依赖 -->

  </dependencies>

2、在web.xml文件中配置springSessionRepositoryFilter过滤器以及 spring配置文件

<!--配置springSessionRepositoryFilter过滤器 -->
    <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配置文件(可选) -->
    <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>

3、spring主配置文件

<import resource="classpath:springsession.xml"/>

SpringSession配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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">

    <!--
        启动Spring的注解支持,SpringSession中使用到了Spring的相关注解(可省略)
        如果使用 <context:component-scan base-package="com"/>来进行包扫描,这个标签中的功能就包含了
        <context:annotation-config/>的功能
     -->
    <context:annotation-config />

    <!--
       定义一个用于专门配置SpringSession的bean标签配置
           只配置RedisHttpSessionConfiguration 的Bean 就可以实现同域名同项目的Session共享
     -->
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <!--配置Session的最大生命周期 单位 秒 默认值为1800 表示30分钟 -->
<!--        <property name="maxInactiveIntervalInSeconds" value="1800"/>-->

        <!--注入一个Cookie的序列化规则对象 -->
        <property name="cookieSerializer" ref="defaultCookieSerializer"/>
    </bean>

    <!--配置一个Cookie序列化规则对象 用于改变Cookie的存放规则 -->
    <bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
        <!--指定SpringSession的SessionId存放在域名的根路径下,用于实现同域名不同项目的Session共享 -->
        <property name="cookiePath" value="/"/>
        <!--指定SpringSession的SessionId存放在根域名下,用于实现同根域名不同二级子域名下的Session共享
            适合应用在Nginx的虚拟主机的多城市站点部署
         -->
        <property name="domainName" value="myweb.com"/>
    </bean>

    <!--配置Redis(可选);如果当前工程已经配置过了Redis,则可省略 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1"/>
        <property name="port" value="6379"/>
<!--        <property name="password" value="123456"/>-->
    </bean>

</beans>

4、测试类

@WebServlet("/set")
public class SetServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        this.doPost(req, resp);
    

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        req.getSession().setAttribute("myKey","My Session Data !");
        resp.getWriter().println("Set Session OK !");
    




@WebServlet("/get")
public class GetServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        this.doPost(req, resp);
    

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
        String data = (String) req.getSession().getAttribute("myKey");
        resp.getWriter().println(data);
    

如果是Springboot项目

(1)application.properties配置文件

server.port=8077

spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=123456

#设置SpringSession过期时间,默认30分钟
#server.servlet.session.timeout=30m
#ָ设置cookie上下文路径
#server.servlet.session.cookie.path=/

#server.servlet.session.cookie.domain=myweb.com

(2)测试类controller

@RestController
public class TestController 
    @RequestMapping("/set")
    public Object set(HttpSession session)
        session.setAttribute("myKey","我的Session数据!");
        return "Session设置成功";
    

    @RequestMapping("/get")
    public Object get(HttpSession session)
        String data= (String) session.getAttribute("myKey");
        return data;
    

5、启动两个Tomcat,设置不同端口号,如8081,8082

(1)同域,同项目

​​​​​​http://localhost:8081/springsess-2/set

http://localhost:8082/springsess-2/get

  

(2)同域,不同相同

http://localhost:8081/shop/set

http://localhost:8082/buy/get

(3)同根域名,不同二级子域名下的项目

首先修改 C:\\Windows\\System32\\drivers\\etc 下的 hosts文件,添加如下配置

127.0.0.1 		shanghai.myweb.com
127.0.0.1 		suzhou.myweb.com

http://shanghai.myweb.com:8081/buy/set

http://suzhou.myweb.com:8082/shop/get

其中redis存放session

SpringSession实现流程

1、启动WEB项目的时候,会读取web.xml,读取顺序content-param --> listener --> filter --> servlet

2、ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息初始化根web应用程序上下文

3、SpringHttpSessionConfiguration注册 springSessionRepositoryFilter :bean,RedisHttpSessionConfiguration 注册 sessionRedisTemplate : bean  和 sessionRepository : bean

4、配置文件配置JedisConnectionFactory implements RedisConnectionFactory ,创建jedisConnectionFactory bean

以上是关于Java -- SpringSession实现session共享的主要内容,如果未能解决你的问题,请参考以下文章

从Spring-Session源码看Session机制的实现细节

SpringSession实现分布式系统session共享案例实战

SpringSpringBoot + SpringSession + Redis 实现Session共享

使用idea,springboot,springsession,redis实现分布式微服务的session 共享

SpringSession和Redis实现Session跨域

SpingSecurity + SpringSession 死磕