Spring Session

本文深入探讨了HTTP协议中Session和Cookie的工作原理,特别是在分布式环境下的Session管理。文章详细介绍了如何利用Redis集成Spring Session来实现集群环境下的Session共享。

一、Session与Cookie基础

由于http协议是无状态的协议,为了能够记住请求的状态,于是引入了Session和Cookie的机制。我们应该有一个很明确的概念,那就是Session是存在于服务器端的,在单体式应用中,他是由tomcat管理的,存在于tomcat的内存中,当我们为了解决分布式场景中的session共享问题时,引入了redis,其共享内存,以及支持key自动过期的特性,非常契合session的特性,我们在企业开发中最常用的也就是这种模式。但是只要你愿意,也可以选择存储在JDBC,Mongo中,这些,spring都提供了默认的实现,在大多数情况下,我们只需要引入配置即可。而Cookie则是存在于客户端,更方便理解的说法,可以说存在于浏览器。Cookie并不常用,至少在我不长的web开发生涯中,并没有什么场景需要我过多的关注Cookie。http协议允许从服务器返回Response时携带一些Cookie,并且同一个域下对Cookie的数量有所限制,之前说过Session的持久化依赖于服务端的策略,而Cookie的持久化则是依赖于本地文件。虽然说Cookie并不常用,但是有一类特殊的Cookie却是我们需要额外关注的,那便是与Session相关的sessionId,他是真正维系客户端和服务端的桥梁。


用户发起请求,服务器响应请求,并做一些用户信息的处理,随后返回响应给用户;用户再次发起请求,携带sessionId,服务器便能够识别,这个用户就是之前请求的那个。

使用Springboot编写一个非常简单的服务端,来加深对其的理解。需求很简单,当浏览器访问localhost:8080/test/cookie?browser=xxx时,如果没有获取到session,则将request中的browser存入session;如果获取到session,便将session中的browser值输出。顺便将request中的所有cookie打印出来。

第一次

不存在session,设置browser=xxx
__guid : 111872281.1696971316212943600.1511169461836.8748
COOKIE_SUPPORT : true
GUEST_LANGUAGE_ID : zh_CN
第二次以后
存在session,browser=xxx
COOKIE_SUPPORT : true
GUEST_LANGUAGE_ID : zh_CN
SCREEN_NAME : 742b6879536e315670383447536538734378547032773d3d
JSESSIONID : 7150301570450686E6CFF45504D8C742

当服务端往session中保存一些数据时,Response中自动添加了一个Cookie:JSESSIONID:xxxx,再后续的请求中,浏览器也是自动的带上了这个Cookie,服务端根据Cookie中的JSESSIONID取到了对应的session。这验证了一开始的说法,客户端服务端是通过JSESSIONID进行交互的,并且,添加和携带key为JSESSIONID的Cookie都是tomcat和浏览器自动帮助我们完成的,这很关键。

不同浏览器,访问是隔离的,甚至重新打开同一个浏览器,JSESSIONID也是不同的。

存放在客户端的Cookie的确是存在安全问题的,我们使用不同浏览器的JSESSIONID“骗”过了服务器。毕竟,服务器只能通过Cookie中的JSESSIONID来辨别身份。

二、使用Redis集成Spring Session

使用第三方仓储来实现集群session管理,也就是常说的分布式session容器,替换应用容器(如tomcat的session容器)。仓储的实现,Spring Session提供了三个实现(redis,mongodb,jdbc),其中redis使我们最常用的。程序的实现,使用AOP技术,几乎可以做到透明化地替换。(核心)

1.pom.xml

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

2.配置类

@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig {

}

3.程序

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
public class test {

    @RequestMapping("/login")
    public String cookie(@RequestParam("username") String username, HttpSession session) {
        //取出session中的browser
        Object sessionBrowser = session.getAttribute("username");
        if (sessionBrowser == null) {
            System.out.println("不存在session,设置username=" + username);
            session.setAttribute("username", username);
        } else {
            System.out.println("存在session,username=" + sessionBrowser.toString());
        }

        return "index";

    }
    @RequestMapping("/test/cookie")
    public String cookie( HttpSession session) {
        //取出session中的browser
        Object sessionBrowser = session.getAttribute("username");

        return "index";

    }
}

4.结果

会发现和原生的session管理是有一些差别,原先的信息中我们记得Cookie中记录的Key值是JSESSIONID,而替换成RedisHttpSession之后变成了SESSION。

存在session,browser=xxx
__guid : 111872281.1696971316212943600.1511169461836.8748
COOKIE_SUPPORT : true
GUEST_LANGUAGE_ID : zh_CN
JSESSIONID : 4E512E392246CA4D582617F12E8E61E2
SESSION : 710b31f3-a0f3-49a7-9811-76916bccc745
monitor_count : 6

​a spring:session是默认的Redis HttpSession前缀(redis中,我们常用’:’作为分割符)。

b 每一个session都会有三个相关的key,第三个key最为重要,它是一个HASH数据结构,将内存中的session信息序列化到了redis中。如上文的browser,就被记录为sessionAttr:browser=chrome,还有一些meta信息,如创建时间,最后访问时间等。

c 另外两个key,expirations:1504446540000和sessions:expires:7079…我发现大多数的文章都没有对其分析,前者是一个SET类型,后者是一个STRING类型,可能会有读者发出这样的疑问,redis自身就有过期时间的设置方式TTL,为什么要额外添加两个key来维持session过期的特性呢?这需要对redis有一定深入的了解才能想到这层设计。当然这不是本节的重点,简单提一下:redis清除过期key的行为是一个异步行为且是一个低优先级的行为,用文档中的原话来说便是,可能会导致session不被清除。于是引入了专门的expiresKey,来专门负责session的清除,包括我们自己在使用redis时也需要关注这一点。在开发层面,我们仅仅需要关注第三个key就行了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

javafanwk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值