目录
实践:SpringBoot 使用Redis实现分布式Session
原理:如何用Redis实现分布式Session
1-1 什么是Session
Session是一种在 服务器 上存储用户数据以支持 Web应用会话管理的技术。当用户与Web应用交互时,服务器为每个用户创建一个独特的会话。这个会话允许服务器存储用户的状态信息,比如用户的身份认证信息、购物车内容、偏好设置等,即使用户在多个页面间导航,这些信息也会被保留。
1.用户登录(Login)
- 用户首次登录Web应用时,会通过输入用户名和密码请求服务器验证身份。
2.创建Session和设置属性(Create session + setAttribute)
- 一旦身份验证通过,服务器会为该用户创建一个Session。
- 服务器为这个Session设置一个独特的标识符,通常称为Session ID。
- 服务器还可以在Session中设置属性,这些属性是关于用户的特定信息,如用户ID、角色、权限等。
3.设置Cookie(set-cookie: JSESSIONID:888)
- 服务器将创建的Session ID发送回客户端(用户的浏览器),通常是通过设置一个名为JSESSIONID的Cookie实现的。
- 该Cookie会保存在用户的浏览器中,并且每次向服务器发送请求时都会自动包含这个Cookie。
4.后续请求(Request cookie:JSESSIONID:888)
- 用户在后续的每一个请求中,浏览器都会携带这个Cookie,包含Session ID,发送给服务器。
- 服务器通过Session ID识别出用户,并获取之前存储的Session信息。
5.资源访问(resource)
- 服务器根据Session信息处理用户的请求,并返回相应的资源,例如用户请求的网页或数据。
- 这样用户就能访问只有登录后才能看到的资源,如个人信息页面、购物车等。
通过以上步骤,Session允许用户在Web应用中进行无缝的、状态保持的交互,即使是在无状态的HTTP协议上。服务器通过Session机制识别用户并提供个性化服务,而用户无需在每个页面或请求中重复登录。
1-2 Session和Cookie的区别
Session:
Session
是服务端用来维护用户状态的技术,常用于跟踪如购物车等用户特定操作。HTTP协议本身无法识别用户,但通过为每位用户创建独特的Session,服务器可以识别并追踪用户的行为。
Cookie
- 与之相对的是
Cookie
,它存储在客户端浏览器中,用于存储用户信息。由于保存在客户端,Cookie的安全性相对较低,因此敏感信息不应直接存储在Cookie中,而应进行加密处理,只在服务器端解密使用。
总结来说,Cookie和Session都用于跟踪用户状态,但它们存储的位置不同:Cookie存储在客户端,易于访问但安全性较低;Session存储在服务器端,安全性较高。
1-3 什么是分布式 Session
分布式Session是一种在服务器集群中保持用户会话状态一致性的机制。在单体应用中,用户的Session信息通常由单个服务器管理。但在分布式系统或微服务架构中,应用可能被部署在多个服务器上,这些服务器共同对外提供服务。分布式Session的目的是确保用户的会话信息能在这个服务器集群中共享,无论用户的请求是被哪个服务器接收。
问题:
在集群环境下,如果不采用分布式Session,那么会有一些潜在的问题和后果,如图所示,有两个服务器(Server1和Server2)以及一个用户,这里是可能发生的情况:
1.Session不一致:
- 如果用户首次请求被Server1处理,并且在这里创建了Session,那么这个Session信息存储在Server1的内存中。
- 当用户的下一个请求由于负载均衡的原因被路由到Server2时,Server2没有这个用户的Session信息,因为Server1和Server2不共享内存。
- 结果是Server2不认识这个用户的Session,可能导致用户需要重新登录。
2.用户体验不佳:
- 用户可能在一个会话中多次被要求登录,每次他们的请求被路由到不同的服务器时,都需要重新验证身份。
- 这不仅影响用户体验,也可能导致用户的操作无法持续进行,比如购物车中的物品可能在不同服务器间丢失。
3.数据丢失:
- 如果用户在Server1上执行了某些操作(比如添加商品到购物车),这些操作的结果存储在Server1的Session中。
- 如果后续的请求转到了Server2,而Server2没有这些信息,那么这些操作就会丢失,因为Server2不知道用户之前在Server1上做了什么。
4.安全隐患:
- 由于Session数据分散在多个服务器上,这可能增加数据一致性和同步的复杂性,从而增加了出错和安全漏洞的风险。
解决方案:
- 共享存储 ,而不是把数据放到单台服务器的内存中
1-4 如何用 Redis 实现分布式 Session?
在分布式系统中,通常会将 Session 存储在 Redis 中来实现 分布式 Session,这样就可以在多台服务器之间共享 Session 数据。实现分布式Session通常使用Redis的Hash结构:
- 1. 在用户登录成功后,将 Session 数据存储在 Redis 中。
- 2. 将 Redis 中的 Session 数据的 Key 设置为一个全局唯一的 ID,一般使用类似于“session:token”这样的格式,其中 token 是一个随机生成的字符串,用来标识这个 Session 数据。
- 3. 在客户端返回响应的同时,将 Session ID(即 token)以 Cookie 的形式返回给客户端。客户端在后续的请求中都会携带这个 Cookie。
- 4. 在后续的请求中,服务器会从客户端传递过来的 Cookie 中获取 Session ID,然后根据这个 ID 从 Redis 中获取对应的 Session 数据。如果 Redis 中没有找到对应的 Session 数据,那么就表示这个请求无法通过认证。
- 5. 在用户退出登录或 Session 失效时,需要将 Redis 中的对应 Session 数据删除。
实践:SpringBoot 使用Redis实现分布式Session
步骤1:添加依赖
pom.xml 文件添加如下依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.6.3</version>
</dependency>
步骤2:配置Redis连接,配置Session
- 配置 Redis
- 从Spring Boot 2.0开始,如果你已经添加了spring-boot-starter-data-redis依赖,并且在配置文件中配置了Redis的信息,Spring Boot会自动配置Redis作为Session存储。
# application.yml
spring:
redis:
host: redis服务器地址
port: 6379
password: 你的Redis密码
// session失效时间
session:
timeout: 86400
store-type: redis
步骤3:添加注解(可省略)
@EnableRedisHttpSession
注解是 Spring Session 项目提供的,用于启用Spring Session支持和配置,它会创建一个名为springSessionRepositoryFilter
的 Bean,这个 Bean 负责替换默认的 HTTP Session 实现。
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {
// 可以添加更多的配置信息
}
在Spring Boot中,如果你依赖于 spring-boot-starter-data-redis
并且已经配置了 application.yml 中的 spring.session.store-type
为 redis
,Spring Boot 的自动配置机制可能会自动注册必要的 Spring Session 仓库和配置,因此你可能不需要显式地添加 @EnableRedisHttpSession
注解。Spring Boot的自动配置机制足够智能,能够根据你的依赖和配置属性来决定是否启用特定的配置。
Redis 存储 Session 值乱码解决
在 Spring Session 中,可以通过定义一个 RedisSerializer bean来覆盖默认的序列化器。这个bean的名字必须是 springSessionDefaultRedisSerializer
。可以在现有的 Redis 配置类中添加该 bean
/**
*
* 自定义序列化
*
*/
@Configuration
public class RedisTemplateConfig {
// 添加的 Bean 解决 Session 值乱码
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
//创建RedisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
//设置Key的序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
//创建Json序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置Value的序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}