session
我们之前有介绍过(可见springboot整合springsecurity),简单来说就是将用户信息或者数据存储在服务器上,通常用于验证用户身份或者避免通过获取相关信息。
但是,缺点也是非常明显:
占用服务器负载:我们可以使用token
,用时间换取空间
对于多服务器环境,session
无法共享
对于第二点缺点,我们目前有几种比较常用的解决方法
使用cookie
加密的方式将session
保存在客户端上
优点是可以减轻服务器压力,缺点是每次请求都要带上cookie
信息,占用一定带宽。此外若用户禁用cookie
则无法使用
服务器间同步
通过配置tomcat
集群,在集群中广播自己的session
信息,但是缺点很明显,当集群规模较大时,会占用大量资源来进行session
同步处理
基于分布式缓存的session
共享机制
将session
缓存到redis
中,这样不同服务器都可以直接到内存中获取session
,效率高,也最常用
springSession
是spring
旗下的一个项目,把servlet
容器实现的httpSession
替换为springSession
,专注于解决session
管理问题。可简单快速且无缝的集成到我们的应用中。
springsession
就是spring
的一个框架,实现了我们上面说的基于分布式缓存的session
共享机制
我们先来看看简单的操作实例
程序源码
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
<version>2.5.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.5.1version>
dependency>
spring:
redis:
# 数据库索引,默认为0
database: 0
# redis host ip
host: 192.168.56.129
# redis 连接端口
port: 6379
# 服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 1000
#设置springsession存储类型,默认为redis
session:
store-type: redis
@EnableRedisHttpSession
@SpringBootApplication
@EnableRedisHttpSession
public class SpringsessionApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsessionApplication.class, args);
}
}
这里我们构建一个简单的controller
测试是否实现了session
共享
@RestController
@RequestMapping("/session")
public class SessionController {
/**
* 设置session
* @param request
* @param attributes
* @return
*/
@PostMapping("/set")
public Map<String,Object> setSession(HttpServletRequest request, @RequestParam("attributes")String attributes){
request.getSession().setAttribute("attributes",attributes);
Map<String,Object> map = new HashMap<String,Object>();
map.put("SessionID:",request.getSession().getId());
return map;
}
/**
* 获取session
* @param request
* @return
*/
@GetMapping("/get")
public String getSession(HttpServletRequest request){
String attributes = (String) request.getSession().getAttribute("attributes");
return attributes;
}
}
两个方法都很简单
setSession
:通过请求中的参数设置session
中的attributes
getSession
:测试是否能获取在其他端口设定的session
中对应attributes
的值
由于要体现session
共享,所以我们这里将在两个不同端口运行程序
通过
IDEA
不同端口启用同一个项目,可以在右上角运行处选择edit config
,然后添加springboot
项目,并同意parallel run
port:8080
/session/set
可以看到成功设置了session
并且返回了SessionID
port:8090
/session/get
成功获取到了session
中的值,标明我们实现了session
共享
我们再来看看redis
存储的session
这里以sessionID
为KEY
,session
为value
存储,但是sessionID
前面的命名空间太长了,而且不具有项目标识,我们可以通过在application.yml
中设置namespace="xx"
即可
我们可以看到springsession
使用非常简单,对于用户几乎不用进行什么操作,那么springsession
具体为我们做了什么工作呢?
其实springsession
通过autoconfigure
帮我们自动配置了一个过滤器SessionRepositoryFitlter
SessionRepositoryFitlter
@Order(-2147483598)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
定义来看:
这里为SessionRepositoryFilter
设定了一个非常小的order
值,以确保能够在filterchain
中被优先执行。
优先执行是为了将原生
HttpRequest
进行替换和封装
继承了OncePerRequestFilter
,确保一次请求只通过一次
源码命名太长,直接贴代码太乱了,这里就直接截图了
可以看到,我们这里首先对request
和response
进行封装,之后将封装请求传入doFilter()
所以具体逻辑就在两个封装类中,这里我们着重关注request
的封装类
获取session
的逻辑如下:
this.getCurrentSession()
检查serlvet
容器中是否有session
,如果有session
则直接返回,如果没有则去redis
中去拿
this.getRequestedSession()
根据请求中的信息获取sessionID
,然后根据sessionID
去redis
中获取session
这里并不是每次查询都是去redis
中查询,而是设置了一个session
缓存,每次查询先检查缓存中有没有,如果有则直接拿值,如果没有则通过httpSessionIdResolver.resolveSessionIds(this)
获取sessionID
获取
sessionid
有两种方式,一种是根据请求中Header
信息获取,一种是放在cookie
中我们这里
httpSessionIdResolver = new CookieHttpSessionIdResolver()
选择使用cookie
获取sessionID
查询到session
之后,更新session
相关信息并返回
如果在redis
中没有找到,则根据create
判断是否创建新的session
基本的逻辑非常清楚也非常好理解,我们再来具体看看springsession
是如何在redis
中查询session
的
SessionRepository
springsession
为我们提供了这样一个session
仓库,能够完成对session
的CRUD
操作
我们这里使用的是RedisSessionRepository
,实现了将session
存储在redis
上的CRUD
操作
这就是我们getRequestedSession()
中使用的findById()
可以看到里面就是对redis
数据库的直接操作
本文主要介绍了springboot
如何整合springsession
实现session
共享,也简单介绍了其中逻辑原理,具体的部分还是建议仔细阅读源码。
redis
上的CRUD
操作
[外链图片转存中…(img-guh79ZOd-1624583023491)]
这就是我们getRequestedSession()
中使用的findById()
可以看到里面就是对redis
数据库的直接操作
本文主要介绍了springboot
如何整合springsession
实现session
共享,也简单介绍了其中逻辑原理,具体的部分还是建议仔细阅读源码。