Springboot整合springsession实现session共享

Springboot整合springsession实现session共享

简介

session我们之前有介绍过(可见springboot整合springsecurity),简单来说就是将用户信息或者数据存储在服务器上,通常用于验证用户身份或者避免通过获取相关信息。

但是,缺点也是非常明显:

  1. 占用服务器负载:我们可以使用token,用时间换取空间

  2. 对于多服务器环境,session无法共享

对于第二点缺点,我们目前有几种比较常用的解决方法

  1. 使用cookie加密的方式将session保存在客户端上

    优点是可以减轻服务器压力,缺点是每次请求都要带上cookie信息,占用一定带宽。此外若用户禁用cookie则无法使用

  2. 服务器间同步

    通过配置tomcat集群,在集群中广播自己的session信息,但是缺点很明显,当集群规模较大时,会占用大量资源来进行session同步处理

  3. 基于分布式缓存的session共享机制

    session缓存到redis中,这样不同服务器都可以直接到内存中获取session,效率高,也最常用

Springsession

springSessionspring 旗下的一个项目,把 servlet 容器实现的 httpSession替换为springSession,专注于解决session管理问题。可简单快速且无缝的集成到我们的应用中。

springsession就是spring的一个框架,实现了我们上面说的基于分布式缓存的session共享机制

操作实例

我们先来看看简单的操作实例

程序源码

pom.xml

        <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>
application.yml
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);
    }
}
SessionController

这里我们构建一个简单的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

Springboot整合springsession实现session共享_第1张图片

可以看到成功设置了session并且返回了SessionID

port:8090 /session/get

Springboot整合springsession实现session共享_第2张图片

成功获取到了session中的值,标明我们实现了session共享

我们再来看看redis存储的session

Springboot整合springsession实现session共享_第3张图片

这里以sessionIDKEY,sessionvalue存储,但是sessionID前面的命名空间太长了,而且不具有项目标识,我们可以通过在application.yml中设置namespace="xx"即可

Springsession 逻辑分析

我们可以看到springsession使用非常简单,对于用户几乎不用进行什么操作,那么springsession具体为我们做了什么工作呢?

其实springsession通过autoconfigure帮我们自动配置了一个过滤器SessionRepositoryFitlter

SessionRepositoryFitlter
@Order(-2147483598)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {

定义来看:

  • 这里为SessionRepositoryFilter设定了一个非常小的order值,以确保能够在filterchain中被优先执行。

    优先执行是为了将原生HttpRequest进行替换和封装

  • 继承了OncePerRequestFilter,确保一次请求只通过一次

源码命名太长,直接贴代码太乱了,这里就直接截图了

Springboot整合springsession实现session共享_第4张图片

可以看到,我们这里首先对requestresponse进行封装,之后将封装请求传入doFilter()

所以具体逻辑就在两个封装类中,这里我们着重关注request的封装类

Springboot整合springsession实现session共享_第5张图片

Springboot整合springsession实现session共享_第6张图片

获取session的逻辑如下:

  1. this.getCurrentSession() 检查serlvet容器中是否有session,如果有session则直接返回,如果没有则去redis中去拿

  2. this.getRequestedSession() 根据请求中的信息获取sessionID,然后根据sessionIDredis中获取session

    Springboot整合springsession实现session共享_第7张图片

    这里并不是每次查询都是去redis中查询,而是设置了一个session缓存,每次查询先检查缓存中有没有,如果有则直接拿值,如果没有则通过httpSessionIdResolver.resolveSessionIds(this)获取sessionID

    获取sessionid有两种方式,一种是根据请求中Header信息获取,一种是放在cookie

    我们这里httpSessionIdResolver = new CookieHttpSessionIdResolver()选择使用cookie获取sessionID

    查询到session之后,更新session相关信息并返回

  3. 如果在redis中没有找到,则根据create判断是否创建新的session

基本的逻辑非常清楚也非常好理解,我们再来具体看看springsession是如何在redis中查询session

SessionRepository

springsession为我们提供了这样一个session仓库,能够完成对sessionCRUD操作

我们这里使用的是RedisSessionRepository,实现了将session存储在redis上的CRUD操作

Springboot整合springsession实现session共享_第8张图片

这就是我们getRequestedSession()中使用的findById()

可以看到里面就是对redis数据库的直接操作

总结

本文主要介绍了springboot如何整合springsession实现session共享,也简单介绍了其中逻辑原理,具体的部分还是建议仔细阅读源码。

redis上的CRUD操作

[外链图片转存中…(img-guh79ZOd-1624583023491)]

这就是我们getRequestedSession()中使用的findById()

可以看到里面就是对redis数据库的直接操作

总结

本文主要介绍了springboot如何整合springsession实现session共享,也简单介绍了其中逻辑原理,具体的部分还是建议仔细阅读源码。

你可能感兴趣的:(springboot,spring,boot,session)