Java会话技术之 —— Spring Session

前言

在上一篇我们聊到了会话技术的基础原理中session和cookie的使用,基于cookie和session可以实现客户端(浏览器)和服务端的会话存储,从请求的无状态变为一定程度的有状态,在文章最后,通过一个简单的演示,看到这样一种现象,即在分布式环境下,假如客户端第一次携带着JSESSINID访问了A服务器的某个接口,再次访问B服务器相同服务的相同接口时,却发现获取到的JSESSINID值为null

很明显,在分布式环境下,基于单机模式下的session和cookie的值是无法跨进程互通,于是我们想,是否可以通过某种方式将JSESSINID或者说一个客户端与服务端的交互凭证存放在某个存储介质中呢?答案是肯定的

在Java中,提供了多种对于此问题的解决方案,目前使用较多也比较成熟的方案像spring-session,token + redis ,JWT,oauth2等,都可以实现在分布式环境下session共享问题,当然这些方案并不是相互隔离的,可以组合搭配,甚至可以基于某一种做定制化的方案都可以

下面来探讨下小而美的分布式session共享的解决方案的spring-session

spring-session基本原理

spring-session提供了一种扩展存储分布式会话session的解决方案,即通过引入spring-session的相关依赖,可以将会话的session信息存放到指定的存储介质中,可以是mongodb,redis,mysql等,实现自由灵活的存取

下面演示基于redis存储session信息实现分布式会话问题的解决

1、添加基础依赖

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

    </dependencies>

2、配置yml

server:
  port: 8081

spring:
  #redis基础配置 ###################################
  redis:
    port: 6379
    host: localhost
    database: 1

  #session配置 ###################################
  session:
    store-type: redis
    timeout: 3600s

在这里插入图片描述
可以看到,spring-session提供了丰富的session存储方式,可以根据自己的情况自由选择

3、编写登录接口和获取登录信息接口

@RestController
public class LoginController {

    @GetMapping("/login")
    public String login(HttpSession session,HttpServletRequest request,HttpServletResponse response,
                        String username, String password) {
        if(username.equals("admin") && password.equals("123456")){
            session.setAttribute("login_info",username);
            return "login success:" + username;
        }
        return "login fail:" + username;
    }

    @GetMapping("/info")
    public String getLoginInfo(HttpSession session,HttpServletRequest request,HttpServletResponse response) {
        return "info:" + session.getAttribute("login_info");
    }

}

仍然按照之前的测试方式,通过浏览器观察下session的信息,

调用接口:http://localhost:8081/login?username=admin&password=123456
在这里插入图片描述
这时候发现,cookie一栏不再有JSESSIONID,而是一个SESSION,说明使用了spring-sesion的方式之后,会话信息的存储方式发生了改变

再通过redis的客户端工具观察下redis库中的session存储情况,发现这里多出来了很多和session相关的信息,比如expires表示会话的过期时间等(具体的过期时间可以从TTL的时间看出来)
在这里插入图片描述

这时,再去访问获取会话信息接口:
在这里插入图片描述

既然看到session的信息存储到了redis,即在过期时间范围内持久化了,就算重启,也可以访问,即重启后,再次访问上面的接口仍然可以看到 info:admin

在这里插入图片描述
重启后,再次访问,依然可以访问到会话的信息,下面再启动一个相同的服务,使用端口进行区分

启动成功后,访问:http://localhost:8082/info,发现也可以成功访问到相同的session信息,这样就简单实现了在分布式环境下会话共享的问题
在这里插入图片描述

一个扩展配置

在使用redis存储会话信息时候,默认情况下,保存会话信息的key前缀是以spring开头的,在实际开发过程中,我们希望key带有一定的业务标识,比如redis中存储的会话信息,希望和登录用户的ID有关联,就可以使用下面的这个配置:

session.redis.namespace

在这里插入图片描述
即存储在redis中的session的key就可以按照自定义的方式存储了,删除Session信息重启下项目再次测试,可以看到,可以的前置就是自定义的了
在这里插入图片描述
而实际在开发过程中,更通用的做法是,将保存在redis中的会话信息的key和登录用户的ID进行关联,即在用户登录成功后,保存到redis中,后续在会话有效的时间范围内,用户再次访问时候,通过网关或者通用拦截器校验用户的会话信息即可,着在spring-session中该如何实现呢?这里留下一个小小的疑问,有兴趣的同学可以深入探究下

本篇到此结束,最后感谢观看!

猜你喜欢

转载自blog.csdn.net/zhangcongyi420/article/details/113444556