一、引言
在微服务架构和分布式系统中,会话管理(Session Management)成为了一个挑战。传统的基于Servlet容器的会话管理方式在分布式环境下无法有效工作,因为用户请求可能会被分发到不同的服务器上,导致会话数据无法共享。为了解决这个问题,Spring Session提供了一种基于外部存储(如Redis)的会话管理方式,使得会话数据可以在多个服务器之间共享。
二、Spring Session原理
- 替换HttpSession:Spring Session通过将Servlet容器实现的HttpSession替换为Spring Session,实现了会话数据的外部存储。这样,无论用户请求被分发到哪个服务器,都可以从外部存储中获取到相同的会话数据。
- 配置类:Spring Session提供了几个关键的配置类,如
RedisHttpSessionConfiguration
和SpringHttpSessionConfiguration
,用于配置Redis作为会话数据的存储后端。 - SessionRepositoryFilter:SessionRepositoryFilter是Spring Session的核心组件之一,它负责在请求处理之前和之后与会话存储进行交互,以确保会话数据的正确加载和保存。
三、如何用 Redis 实现分布式 Session?
在分布式系统中,通常会将 Session 存储在 Redis 中来实现 分布式 Session,这样就可以在多台服务器之间共享 Session 数据。实现分布式Session通常使用Redis的Hash结构:
-
用户登录:当用户成功登录后,服务端生成一个唯一的 Session ID(通常是一个随机字符串,如 UUID)。
-
存储 Session:将用户的会话信息(如用户ID、权限信息等)与 Session ID 关联,并存储在 Redis 中。Redis 使用 Hash 结构来存储这些数据,其中 Session ID 作为 Hash 的 key,用户的会话信息作为 Hash 的 value。
-
设置 Cookie:将 Session ID 通过 Cookie 发送到客户端浏览器。这样,客户端在每次请求时都会携带这个 Cookie。
-
请求处理:在客户端的每次请求中,服务端都会检查 Cookie 中的 Session ID。服务端使用这个 ID 从 Redis 中检索对应的 Session 数据。
-
认证与授权:通过检索到的 Session 数据,服务端可以验证用户的身份,并根据用户的会话信息进行授权。
Session 过期:设置 Session 的过期时间,当用户长时间不活动或者显式退出登录时,服务端会从 Redis 中删除对应的 Session 数据。
四、实战步骤
1. 添加依赖:
在Spring Boot项目中,通过添加spring-boot-starter-data-redis和spring-session-data-redis的依赖,引入Spring Session和Redis的支持。
<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>
2. application.yml中配置
server:
servlet:
session:
timeout: 3600 # session 有效期 1小时
spring:
session:
store-type: redis # 使用 Redis 存储会话
# 其他 Spring Session 相关配置
redis:
namespace: myapplication:session #Redis Session 存储的命名空间
# Redis 配置
spring:
redis:
host: 127.0.0.1
port: 6379
# 其他 Redis 相关配置
3. 启用Spring Session:
@EnableRedisHttpSession 是 Spring Session 的一个注解,用于在 Spring Boot 应用程序中启用基于 Redis 的 HTTP Session 管理。它会创建一个名为 springSessionRepositoryFilter
的 Bean,这个 Bean 负责替换默认的 HTTP Session 实现。
在Spring Boot的主类或配置类添加@EnableRedisHttpSession
注解,启用Spring Session对HttpSession的替换。
- 方式一:启动类配置
@SpringBootApplication
@EnableRedisHttpSession
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
- 方式二:增加 RedisSessionConfig 配置类
@Configuration
@EnableRedisHttpSession(redisNamespace = "myapplication:session", maxInactiveIntervalInSeconds = 3600, redisFlushMode = RedisFlushMode.IMMEDIATE)
public class RedisSessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
//cookie名字
defaultCookieSerializer.setCookieName("myapplication-sessionId");
defaultCookieSerializer.setUseHttpOnlyCookie(true);
return defaultCookieSerializer;
}
}
@EnableRedisHttpSession 参数说明:
maxInactiveIntervalInSeconds: 设置 Session 的最大非活动时间(秒)。
默认为 1800 秒(30 分钟)
。redisNamespace: 定义存储在 Redis 中的 Session 数据的命名空间。
默认为 "spring:session"
。redisFlushMode: 定义 Session 数据刷新到 Redis 的模式。默认为
RedisFlushMode.ON_SAVE
,表示每次调用 SessionRepository 的 save 方法时,Session 数据都会同步刷新到 Redis。其他可选的模式包括RedisFlushMode.IMMEDIATE
(立即刷新)和RedisFlushMode.NONE
(不刷新)cleanupCron: 定义一个 cron 表达式,用于
设置清理 Redis 中过期 Session 的计划。默认为每分钟执行一次
。
或通过 配置文件配置:
# HTTP Session 过期时间(秒)
server.servlet.session.timeout=1800
# Redis Session 存储的命名空间
spring.session.redis.namespace=spring:session
4. 配置 Redis 序列化 存储
以下是一个配置 RedisTemplate 以使用 JSON 序列化的示例,这里我们使用 Jackson2JsonRedisSerializer 作为序列化器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.activateDefaultTyping( LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
// 空字段不序列化
om.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
jsonRedisSerializer.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jsonRedisSerializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
//设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
关于序列化 及 RedisTemplate 配置可参考 序列化与消息转换器
5. 使用HttpSession:
在Controller或Service中,使用HttpSession对象来存储和获取会话数据,Spring Session会自动处理会话数据的存储和加载。
标签:session,redis,Springboot,--,Spring,Redis,Session,序列化 From: https://blog.csdn.net/dazhong2012/article/details/139211048