资料来源:https://www.bilibili.com/video/BV1iK411p7dZ
Spring Session版本 1.3.1RELEASE
Spring Session Redis版本 1.3.1RELEASE
Spring Data Redis版本 1.8.8.RELEASE
Jedis版本 2.9.0
Spring Web版本 4.3.16
第1章 Session会话管理概述
一、Web中的Session和Cookie回顾
1、Session机制
由于HTTP协议是无状态的协议,一次浏览器和服务器的交互过程就是:
浏览器:你好吗?
服务器:很好!
这就是一次会话,对话完成后,这次会话就结束了,服务器端并不能记住这个人,下次再对话时,服务器端并不知道是上一次的这个人,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是Session。
2、Cookie
服务端如何识别特定的客户?
这个时候需要使用Cookie。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。
实际上大多数的应用都是用Cookie 来实现Session跟踪的,第一次创建Session时,服务端会在HTTP协议中向客户端 Cookie 中记录一个Session ID,以后每次请求把这个会话ID发送到服务器,这样服务端就知道客户端是谁了。
3、url重写
那么如果客户端的浏览器禁用了Cookie 怎么办?
一般这种情况下,会使用一种叫做URL重写的技术来进行session会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sessionId=xxxxx 这样的参数,服务端据此来识别客户端是谁
二、Session会话管理及带来的问题
在Web项目开发中,Session会话管理是一个很重要的部分,用于存储与记录用户的状态或相关的数据。<br /> 通常情况下session交由容器(tomcat)来负责存储和管理,但是如果项目部署在多台tomcat中,则session管理存在很大的问题:
- 多台tomcat之间无法共享session,比如用户在tomcat A服务器上已经登录了,但当负载均衡跳转到tomcat B时,由于tomcat B服务器并没有用户的登录信息,session就失效了,用户就退出了登录
一旦tomcat容器关闭或重启也会导致session会话失效
因此如果项目部署在多台tomcat中,就需要解决session共享的问题
三、Session会话共享方案
1、第一种是使用容器扩展插件来实现,比如基于Tomcat的tomcat-redis-session-manager插件,基于Jetty的jetty-session-redis插件、memcached-session-manager插件;这个方案的好处是对项目来说是透明的,无需改动代码,但是由于过于依赖容器,一旦容器升级或者更换意味着又得重新配置
其实底层是,复制session到其它服务器,所以会有一定的延迟,也不能部署太多的服务器。
2、第二种是使用Nginx负载均衡的ip_hash策略实现用户每次访问都绑定到同一台具体的后台tomcat服务器实现session总是存在。
这种方案的局限性是ip不能变,如果手机从北京跳到河北,那么ip会发生变化;另外负载均衡的时候,如果某一台服务器发生故障,那么会重新定位,也会跳转到别的机器。
3、第三种是自己写一套Session会话管理的工具类,在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中,这个方案灵活性很好,但开发需要一些额外的时间。
4、第四种是使用框架的会话管理工具,也就是我们要介绍的Spring session,这个方案既不依赖tomcat容器,又不需要改动代码,由Spring session框架为我们提供,可以说是目前非常完美的session共享解决方案第2章 Spring Session入门
一、Spring Session简介
Spring Session 是Spring家族中的一个子项目,它提供一组API和实现,用于管理用户的session信息
它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中;
官网:https://spring.io/
Spring Session的特性
- 提供用户session管理的API和实现
- 提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session
支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题
二、入门案例
1、环境配置
(1)创建一个空的Maven project,名字及路径根据自己的情况定。
(2)空project创建好后,会提示创建模块,我们暂时先取消
(3)设置字体
(4)设置编码方式
(5)设置maven信息
(6)创建一个Maven的web module,名字为01-springsession-web
(7)完善Maven项目的结构
A、 在main目录下,创建java目录,并标记为Sources Root
B、 在main目录下,创建resources目录,并标记为Resources Root2、代码开发
2.1 创建向session放数据的servlet
1)在java目录下创建包com.bjpowernode.session.servlet包
2)在servlet包下创建SetSessionServlet
3)在Servlet中通过注解指定urlPatterns,并编写代码2.2 创建从session放数据的servlet
1)在servlet包下创建GetSessionServlet
2)在Servlet中通过注解指定urlPatterns,并编写代码2.3 向pom.xml文件中添加servlet及jsp的配置
<dependencies>
<!-- servlet依赖的jar包start -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- servlet依赖的jar包start -->
<!-- jsp依赖jar包start -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- jsp依赖jar包end -->
<!--jstl标签依赖的jar包start -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--jstl标签依赖的jar包end -->
</dependencies>
2.4 部署访问测试(目前无法实现session共享)
(1)配置tomcat9100服务器
① 打开Edit Configurations选项
② 添加tomcat配置
③ 给tomcat服务器取名,并修改端口号
④ 将项目部署到tomcat9100上
⑤ 指定项目的上下文根为/01-springsession-web
⑥ 为了实现热部署,在Server选项卡中,配置以下两个选项
(2)配置tomcat9200服务器
操作步骤同配置tomcat9100,配完之后在Application Servers窗口中如下3、SpringSession集成配置
3.1 添加Spring Session依赖
```xml
org.springframework.session spring-session-data-redis 1.3.1.RELEASE
<a name="SUqFb"></a>
#### 3.2 web.xml中配置springSessionRepositoryFilter过滤器
xml
<a name="i0ERG"></a>
#### 3.3 web.xml中加载Spring配置文件
xml
<a name="Q2hAO"></a>
#### 3.4 创建applicationContext-session.xml
① 配置一个RedisHttpSessionConfiguration类<br /><context:annotation-config/>:用于激活已经在Spring容器中注册的bean或者注解,因为我们通过容器创建的bean中,底层有可能使用了其它的注解,我们通过<context:component-scan>就不能指定具体的包了,所以可以使用<context:annotation-config/>激活
xml
② 配置Spring-data-redis
xml
<a name="jH27C"></a>
#### 3.5 配置redis.properties文件
xml
redis.hostName=192.168.235.128
redis.port=6379
redis.password=123456
redis.usePool=true
redis.timeout=15000
<a name="qcyiD"></a>
#### 3.6 applicationContext.xml中导入applicationContext-session.xml
<br />点击config将这两个配置文件进行关联<br />
<a name="HkLQR"></a>
### **4、部署测试**
4.1 思路<br />为了演示session的共享,我们这里配置两个tomcat服务器,端口号分别为9100和9200,将我们上面创建好的项目分别部署到这两台服务器上。一台服务器执行放session,另一台服务器执行取session的操作<br />4.2 启动Linux上的redis服务器<br />4.3 启动两台tomcat服务器<br />(1)在浏览器中访问tomcat9100服务器的setSession<br /><br />(2)在浏览器中访问tomcat9200服务器的getSession<br /><br />4.4 分析<br />tomcat9200服务器上的项目可以访问tomcat9100上的session,说明session共享成功<br />4.5 进一步验证<br />打开Resis客户端工具(RedisDesktopManager),查看Redis里面的session数据<br />其实标准的redis的key格式就是用冒号分割,客户端工具会以目录的形式展示<br /> 
<a name="W8gEf"></a>
# **第3章 Spring Session常见的应用场景**
<a name="Qodxd"></a>
## **一、同域名下相同项目(集群环境)实现Session共享**
在同一个域名下,比如:[www.p2p.com](http://www.p2p.com)<br />同一个项目,部署了多台tomcat,这就是典型的集群。我们的入门案例就属于这种应用场景,只不过在实际开发的过程中,我们如果存在了tomcat集群,那么肯定会使用nginx进行负载均衡,那么这种情况下我们该如何处理。
<a name="weod3"></a>
### **1、案例设计思路**
我们将上一个阶段的p2p项目实现集群部署下的Session共享,因为我们只是演示Session共享,所以我们试用一个简易版本的p2p,在我给大家提供的资料中,该p2p中只包含p2p和dataservice,在Linux服务器上,我们准备三台tomcat,其中两台部署p2p,并实现session共享,另一台部署dataservice
<a name="O1NAF"></a>
### **2、架构图**

<a name="tUVJN"></a>
### **3、实现步骤**
3.1 使用Xftp将p2p上传到tomcat9100和9200的webapps目录下<br /><br /><br />3.2 使用Xftp将dataservice上传到tomcat9300的webapps目录下<br /><br />3.3 使用资源下的SQL脚本,重新创建数据库的表<br />因为目前这个p2p的项目表结构和上一个阶段的稍微有些区别,所以我们这里更新一下<br />① 启动mysql数据库<br /><br />② 通过MySQL客户端工具Navivat创建新的库<br /><br />③ 指定数据库名字为p2p2,字符集编码为utf-8<br /><br />④ 新建查询,执行p2p-data.sql脚本<br /><br />⑤ 执行成功后,表结构如下<br /><br />3.4 通过Xftp工具连接Linux,修改tomcat9300下的dataservice的连接信息<br />① 使用记事本打开,修改redis.properties,保存<br /><br />② 修改datasource.properties,保存<br /><br />③ 修改applicationContext-dubbo-provide.xml注册中心的地址,并保存<br /><br />3.5 通过Xftp工具连接Linux,修改tomcat9100下的p2p的连接信息<br />这里只需要修改applicationContext-dubbo-consumer.xml文件中zk注册中心的地址即可<br /><br />3.6 通过Xftp工具连接Linux,修改tomcat9200下的p2p的连接信息<br />这里只需要修改applicationContext-dubbo-consumer.xml文件中zk注册中心的地址即可<br /><br />3.7 确保Linux系统上的各应用服务器启动<br />注意:先通过ps –ef | grep XXX命令查看,如果已经启动,就不需要再启动了<br />A、启动ZooKeeper服务器<br /><br />A、启动MySQL服务器<br /><br />B、启动Redis服务器<br /><br />C、启动tomcat9300服务器(为了避免出错先关闭,再启动)<br /><br />D、启动tomcat9100服务器(为了避免出错先关闭,再启动)<br /><br />E、启动tomcat9200服务器(为了避免出错先关闭,再启动)<br /><br />F、直接访问tomcat的方式,在浏览器输入地址访问tomcat9100和tomcat9200<br /><br /><br />3.8 使用Nginx对tomcat9100和tomcat9200进行负载均衡<br />① 负载均衡的配置,这里使用的是轮询策略
xml
upstream www.p2p.com{
server 127.0.0.1:9100;
server 127.0.0.1:9200;}
② location匹配的配置,注意:这里对静态资源的处理,我们暂时先注释掉
xml
location /p2p{
proxy_pass http://www.p2p.com;
}
如果要是实现了静态代理,别忘了启动所有的nginx服务器(负载|代理)<br /><br />③ 重启Nginx<br /><br />④ 在浏览器中输入地址,直接访问Nginx服务器,实现负载均衡<br /><br />3.9 Nginx对集群负载均衡之后,登录不成功,但是直接访问tomcat9100或者tomcat9200都是可以成功登录的(Session丢失)<br />账号:13700000000 密码:123456<br />分析原因:<br />因为默认我们负载均衡使用的是轮询策略,每次发送请求给nginx服务器,都会切换tomcat服务器,这个时候没有使用任何session共享策略,所以登录不成功<br />3.10 Nginx对集群负载均衡之后,Session共享方案<br />**① 修改nginx.conf配置文件,将轮询策略修改为ip_hash**<br /><br />但是这种情况,一旦ip发生变化,或者某台服务器出现故障,会重新分配,不稳定<br />所以我们看下这种情况后,将ip_hash注释掉<br /><br />**② 使用SpringSession**<br />使用Spring Session实现session共享,我们不需要修改代码,只要修改一些配置文件即可,为了演示方便,我们直接使用Xftp修改已经发布到tomcat上的项目<br />1)向tomcat9100和tomcat9200的p2p项目中加jar包,这个jar包我已经准备好了<br /><br /><br />2)修改tomcat9100和tomcat9200的p2p项目的web.xml配置文件,添加Spring Session过滤器,因为我们项目本身已经通过springMVC启动了容器,所以spring监听器不需要加了,直接从01-springsession-web中拷贝即可<br /><br /><br />3)将01-springsession-web项目中resources下的applicationContext-session.xml和redis.properties拷贝到tomcat9100和tomcat9200的p2p项目WEB-INF/classes下<br /><br /><br />4)修改tomcat9100和tomcat9200的p2p项目WEB-INF/classes下的applicationContext.xml文件,引入applicationContext-session.xml<br /><br /><br />5)重启三台tomcat服务器,浏览器访问进行登录测试,可以实现Session共享
<a name="IJkoq"></a>
## **二、同域名下不同项目实现Session共享**
在同一个域名下,有多个不同的项目(项目的上下文根不一样)比如:<br /> www.web.com/p2p<br /> www.web.com/shop<br /> 如图:<br />
<a name="JW8xR"></a>
### **1、做法**
设置Cookie路径为根/上下文
<a name="Jojwd"></a>
### **2、案例设计思路**
在01-springsession-web项目的基础上,将本地tomcat9100的上下文根修改为p2p,将本地tomcat9200的上下文根修改为shop
<a name="o0CPF"></a>
### **3、实现步骤**
3.1 打开Edit Configurations进行配置<br /><br />3.2 在Deployment选项卡下,设置本地tomcat9100的Application context为/p2p<br /> <br />3.3 在Deployment选项卡下,设置本地tomcat9200的Application context为/shop<br /><br />3.4 在idea中重新启动本地的两台tomcat服务器<br />3.5 在浏览器中访问tomcat9100(p2p),设置session<br /><br />3.6 在浏览器中访问tomcat9200(shop),获取session<br /><br />**3.7 分析Session共享失败原因**<br />我们通过浏览器提供的开发人员工具可以发现,这两个请求的cookie的路径(path)不一致,虽然我们已经加了Spring Session共享机制,但是后台**服务器认为这是两个不同的会话(session)**,可以通过Redis客户端工具(Redis Destop Mananger)查看,先清空,然后访问,发现是维护了两个不同的session,所以不能实现共享<br /><br />3.8 解决方案:**设置Cookie路径为根/上下文**<br />在applicationContext-session.xml文件中,加如下配置:
xml
<br />3.9 在idea中重新启动本地的两台tomcat服务器<br />3.10 在浏览器中访问tomcat9100(p2p),设置session<br /><br />3.11 在浏览器中访问tomcat9200(shop),获取session<br /><br />**注意:测试的时候要先清空浏览器缓存**
<a name="RXJp1"></a>
## **三、同根域名不同二级子域名下的项目实现Session共享**
同一个根域名,不同的二级子域名<br />比如:<br /> www.web.com<br /> beijing.web.com<br /> nanjing.web.com<br />如图:<br />
<a name="eUm96"></a>
### **1、做法**
- 设置Cookie路径为根/上下文,项目名一样的话,此步骤可以省略
- 设置cookie的域名为根域名 web.com
<a name="moNDn"></a>
### **2、案例设计思路**
在01-springsession-web项目的基础上,将本地tomcat9100的上下文根修改为p2p,将本地tomcat9200的上下文根修改为shop;在本机host文件中修改127.0.0.1的映射关系模拟不同的域名访问
<a name="kgoA2"></a>
### **3、实现步骤**
3.1 延续上面的案例的配置,两台本地tomcat服务器9100和9200,上下文根分别是p2p和shop<br />3.2 修改本地hosts文件,加入如下配置<br /><br /><br />3.3 在idea中重新启动本地的两台tomcat服务器<br />3.4 在浏览器中访问tomcat9100(p2p),设置session<br />注意:这里不再使用localhost访问,而是使用我们映射的域名<br /><br />3.5 在浏览器中访问tomcat9200(shop),获取session<br />注意,这里也不再使用localhost访问,而是使用我们映射的域名<br /> <br />**3.6 分析Session共享失败原因**<br />我们通过浏览器提供的开发人员工具可以发现,虽然这两个cookie的路径(path)都设置为了“/”,但是这两个cookie的域名不一致,虽然我们已经加了Spring Session共享机制,但是后台**服务器同样认为这是两个不同的会话(session)**,可以通过Redis客户端工具(Redis Destop Mananger)查看,先清空,然后访问,发现是维护了两个不同的session,所以不能实现共享,也就是说后台区分是否同一个session和路径和域名有关。<br /><br />
3.7 解决方案:**设置Cookie的域名为根域名 web.com**<br />在applicationContext-session.xml文件中,加如下配置:<br />注意:域名要和hosts文件中配置的域名后面一样
xml

3.8 在idea中重新启动本地的两台tomcat服务器
3.9 在浏览器中访问tomcat9100(p2p),设置session

3.10 在浏览器中访问tomcat9200(shop),获取session

注意:清空浏览器缓存 ## 四、单点登录(了解) 不同根域名下的项目实现Session共享,
比如阿里巴巴这样的公司,有多个业务线,多个网站,用户在一个网站登录,那么其他网站也会是登录了的状态,比如:登录了淘宝网,则天猫网也是登录的;
www.taobao.com
www.tmall.com
比如:
www.web.com
www.p2p.com
www.dai.com
对于不同根域名的场景,要实现一处登录,处处登录,Spring Session不支持
单点登录(Single Sign On),简称为 SSO,是流行的企业业务整合的解决方案之一,SSO是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统
# *第4章 SpringBoot集成SpringSession ## 1、导入依赖 ```xml
<a name="c9mB2"></a>
## 2、配置文件
```xml
spring.redis.host=192.168.115.129
spring.redis.port=6379
spring.redis.password=123456
# 设置SpringSession的Session声明周期为30m,表示30分钟,默认为30m
# server.servlet.session.timeout=30m
# 指定Cookie的存放路径为根路径用于实现同域名不同项目的Session共享
# server.servlet.session.cookie.path=/
# 指定Cookie的存域名用于实现同根域名不同二级子域名的Session共享
# server.servlet.session.cookie.domain=myweb.com
server.port=9400
3、编写Controller
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@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;
}
}
4、编译打包文件
5、启动9300服务、9400服务
6、修改nginx配置
7、测试执行
第5章 Spring Session的执行流程(源码分析)
1、页面请求被全局的过滤器org.springframework.web.filter.DelegatingFilterProxy过滤
2、全局的过滤器是一个代理过滤器,它不执行真正的过滤逻辑,它代理了一个Spring容器中的名为:springSessionRepositoryFilter 的一个过滤器
3、代理的这个springSessionRepositoryFilter 过滤器是从spring容器中获取的,真正执行过滤逻辑的是 SessionRepositoryFilter
@Bean注解
相当于:
……..
4、该SessionRepositoryFilter过滤器覆盖了原来servlet中的request和response接口中定义的操作session方法,替换成自己的session方法
5、在过滤的时候,总是会执行一个finally语句块,在finally中提交session,保存到Redis session以hash结构存放在redis
6、默认的过期时间30分钟
<!-- Spring session 的配置类 -->
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<!--设置session过期时间,单位是秒,默认是30分钟-->
<property name="maxInactiveIntervalInSeconds" value="3600"/>
<!--设置cookie的存放方式-->
<property name="cookieSerializer" ref="defaultCookieSerializer"/>
</bean>