Spring Boot入门教程(五十六): Spring Session

一:Web容器Session

HTTP是无状态协议的,服务器端要想记住客户端的状态需要通过Session和Cookie来完成。

Session是由Web服务器端(Tomcat)维护的,Cookie是由客户端(浏览器)维护的,浏览器每次请求都会自动携带Cookie信息,当服务器端首次往session中存储值时(session.setAttribute(name, value)),服务器端(Tomcat)会自动向响应头(Response Head)中增加一个Set-Cookie的头值为JSESSIONID的键值对Set-Cookie: JSESSIONID=B509E856BF4E4F2A0D3C572179A6857F,当客户端(浏览器)下次访问服务器时浏览器会自动携带Cookie并且该Cookie中会包含JSESSIONID,当服务器收到客户端的请求时服务器就能解析到Cookie中的JSESSIONID并根据JSESSIONID值获取对应的Session对象,这样服务器端就知道客户端的状态了,就知道该请求是哪个用户请求的了。

Session对应着一个浏览器窗口,当浏览器关闭了该Session也就消失了,即关掉浏览器再次打开浏览器访问服务器首次访问session也会重新创建一个新的Session。不同类型的浏览器对Session是隔离的,不能互相访问。不同类型的浏览器首次访问也都会创建不同的session。

Cookie是保存在浏览器中的,即保存在本机的,我们可以直接修改Cookie中的JSESSIONID的值,客户端是否修改JSESSIONID的值服务器是不知道的,服务器只会通过JSESSIONID获取Session,我们可以将JSESSIONID中的值修改为别人的值(可以通过浏览器插件EditThisCookie来修改Cookie的值),这样能够骗过服务器拿到别人session的信息,这样是不安全的。即Cookie是不安全的。

二:Spring Session

传统Session的问题

Session是由Web容器管理的,即一个session只保存在一台机器上,适合于单体应用,随着架构的演练,不断的向微服务分布式集群演进,传统的Session就不能工作了。如:现在有3台web服务器,客户端访问服务器通过负载均衡nginx负载到某一台服务器上,用户此次的数据就保存到这台服务器的Web容器中了,当用户下次请求如果被负载到其它机器上,那么就拿不到之前保存的数据了。这时候就需要整个服务器集群共享同一个Session。为了解决所有服务器共享一套Session,那么Session就不能单独保存在自己的Web容器中,而是保存在一个公共的会话仓库(Session Repository)中,所有服务器都访问同一个仓库,这样所有服务器的状态都一致了。Spring Session支持的仓库有Reids、MongoDB、JDBC。

Spring Session的优点

  • Spring Session是基于servlet规范实现的一套Session管理框架。Spring Session主要解决了分布式场景下Session的共享问题。Spring Session最核心的类是SessionRepositoryFilter过滤器,用于包装用户的请求和响应。
  • 可在程序中直接替换掉HttpSession,而无需修改一行代码
  • 可以很方便的与Spring Security集成,增加诸如findSessionsByUserName,rememberMe,限制同一个账号可以同时在线的Session数(如设置成1,即可达到把前一次登录顶掉的效果)等等

Spring Session的核心类

- org.springframework.session.web.http.SessionRepositoryFilter: 会话仓库过滤器
	- SessionRepositoryRequestWrapper extends HttpServletRequestWrapper
		- getSession(boolean create) 
	- SessionRepositoryResponseWrapper extends HttpServletResponseWrapper
	- HttpSessionWrapper
- org.springframework.session.SessionRepository
	- createSession()
	- save(S session)
	- deleteById(String id) 

三:Spring Session示例

  1. pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
  1. application.yml
spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    jedis:
      pool:
        max-active: 100
        max-wait: 10
        max-idle: 10
        min-idle: 10
  1. Configuration
/**
 * 开启Redis Http Session
 */
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfiguration {
}
  1. Controller
@RestController
public class SessionController {

    @RequestMapping("/session")
    public Object springSession(@RequestParam("username") String username, HttpServletRequest request, HttpSession session) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().contains("JSESSION")) {
                    System.out.println(cookie.getName() + "=" + cookie.getValue());
                }
            }
        }

        Object value = session.getAttribute("username");
        if (value == null) {
            System.out.println("用户不存在");
            session.setAttribute("username", "{username: '" + username+ "', age: 28}");
        } else {
            System.out.println("用户存在");
        }

        return "username=" + value;
    }
}
  1. 启动项目
 # 将springboot项目打成jar包

cd springboot-session-example
mvn clean package

 # 分别以两个不同的端口启动两个项目, 用于模拟集群中的两个服务
cd springboot-session-example/target
java -jar springboot-session-example-0.0.1-SNAPSHOT.jar --server.port=8080
java -jar springboot-session-example-0.0.1-SNAPSHOT.jar --server.port=8081
  1. 第一次访问8080项目,服务器响应头中会有Set-Cookie: SESSION=MDIzNTk4NTgtODllOC00NjhiLTljNDYtZDFjMmM3OTljNzhk; Path=/; HttpOnly; SameSite=Lax, 客户端就会将SESSION写入到Cookie中, 注意Spring Session中的键为SESSION,这里的Path=/ 表示根站点
  2. 查看redis可以看到生成3个key,其中最重要的key是spring:session:sessions:xxx记录这session的信息
  3. 第二次访问8080项目,浏览器会在请求头中自动携带Cookie信息,并且Cookie中会包含SESSION对应的键值对,服务器端根据Cookie中的SESSION就能获取到对应的Session对象
  4. 在同一个浏览的另一个tab页来访问8081,可以看到请求头中的Cookie对应的SESSION值和访问8080工程携带的SESSION值一样

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四:参考文章

发布了308 篇原创文章 · 获赞 936 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/89204972
今日推荐