网站产品策划,东莞商城网站建设,牡丹江网站制作,php网站优化Gitee
https://gitee.com/Lin_DH/system
介绍
【SpringBoot】30 Cookie、Session、Token https://blog.csdn.net/weixin_44088274/article/details/144241595
背景
Spring Session 是 Spring 的一个子项目#xff0c;它提供了一种管理用户会话信息的方法#xff0c;无论…Gitee
https://gitee.com/Lin_DH/system
介绍
【SpringBoot】30 Cookie、Session、Token https://blog.csdn.net/weixin_44088274/article/details/144241595
背景
Spring Session 是 Spring 的一个子项目它提供了一种管理用户会话信息的方法无论是在单服务的应用程序中还是在分布式系统中都能运用。Spring Session 提供了一种机制可以将应用服务器的会话HttpSession抽象化使得开发者可以在不同的环境下使用系统的 API 来管理会话。
Spring Session
原理
Spring Session 使支持集群会话变得简单无需绑定到特定应用程序容器的解决方案。
HttpSession以通用的方式替代应用程序容器如 Tomcat中Servlet 容器实现的 HttpSession实现了会话数据的外部存储并支持在请求头Header中提供 sessionId方便提供 RESTful API。WebSocket提供在接收 WebSocket 消息时保持 HttpSession 活跃的能力。WebSession允许以与应用程序容器无关的方式替换 Spring WebFlux 的 WebSession。配置类提供了几个关键配置类如 RedisHttpSessionConfiguration 和 SpringHttpSessionConfiguration用于配置 Redis 作为会话数据的存储源。SessionRepositoryFilterSessionRepositoryFilter 是 Spring Session 的核心组件之一它负责在请求处理之前和之后与会话存储进行交互以确保会话数据的正确加载和保存。
组成
Spring Session Core提供核心的 Spring 会话功能和 Api。 Spring Session Data Redis提供 SessionRepository 和 ReactiveSessionRepository 的实现支持 Redis 和配置。 Spring Session JDBC提供由关系型数据库和配置支持所支持的 SessionRepository 实现。 Spring Session Hazelcast提供由 Hazelcast 和配置支持所支持的 SessionRepository 实现。
Redis 实现分布式 Session 在分布式系统中通常会将 Session 存储在 Redis 中来实现分布式 Session这样可以在多台服务器之间共享 Session 数据。实现分布式 Session 通常使用 Redis 的 Hash 结构。 1用户登录当用户登录成功后服务端会生成一个唯一的 SessionId通常是一串随机字符串如 UUID。 2存储 Session将用户的会话信息如用户ID权限信息等与 SessionId 关联并存储在 Redis 中Redis 使用 Hash 结构来存储这些数据其中 SessionID 作为 Hash 的 Key用户的会话信息作为 Hash 的 Value。 3设置 Cookie将 SessionId 通过 Cookie 发送到客户端浏览器客户端每次请求时都需要携带上 Cookie。 4请求处理在客户端每次发起请求时服务端会检查 Cookie 中的 SessionId服务端用该 SessionId 从 Redis 中检索对应的 Session 数据。 5认证与授权通过检索到的 Session 数据服务端可以验证用户身份并根据用户的会话信息进行授权。 Session 过期设置 Session 过期时间当用户长时间不活动或者显式退出登录时服务端会从 Redis 中删除对应的 Session 数据。
代码实现
依赖 pom.xml dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 --dependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependency!-- 实现对 Spring Data Redis 的自动化配置 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdexclusions!-- 去掉对 Lettuce 的依赖因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 --exclusiongroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactId/exclusion/exclusions/dependency!-- 引入 Jedis 的依赖这样 Spring Boot 实现对 Jedis 的自动化配置 --dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId/dependency配置 application.yml spring:redis:host: localhostport: 6379
# password:#Redis数据库号,默认为0database: 0 #连接超时时间,单位为毫秒 timeout: 10#对应 RedisProperties.Jedis 内部类jedis:pool:#连接池最大连接数,默认为8,使用负数表示没有限制max-active: 8 #默认连接池最大空闲的连接数,默认为8,使用负数表示没有限制max-idle: 8#默认连接池最小空闲的连接数,默认为0,允许设置0和正数min-idle: 0#连接池最大阻塞等待时间,单位为毫秒,默认为-1,表示不限制max-wait: -1添加配置类 SessionConfiguration.java package com.lm.system.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;/*** author DUHAOLIN* date 2024/11/22*/
Configuration
EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源
public class SessionConfiguration {Bean(name springSessionDefaultRedisSerializer)public RedisSerializerObject springSessionDefaultRedisSerializer() {return RedisSerializer.json();}}添加 EnableRedisHttpSession 注解开启自动化配置 Spring Session 使用 Redis 作为数据源。该注解有如下属性
maxInactiveIntervalInSecondsSession 不活跃后的过期时间默认为 1800 秒。redisNamespace在 Redis Key 的统一前缀默认为 “spring:session”。redisFlushModeRedis 会话刷新模式。目前有两种默认为 RedisFlushMode.ON_SAVE。RedisFlushMode.ON_SAVE请求执行完成时统一写入 Redis 存储。RedisFlushMode.IMMEDIATE每次修改 Session 时立即写入 Redis 存储。cleanupCron清理 Redis Session 会话过期的任务执行 Cron 表达式默认为 “0 * * * * *” 每分钟执行一次。虽说 Redis自带 Key 过期机制但是默认该机制为惰性删除策略实际过期的 Session 还保存在 Redis 的内存中。所以 Spring Session 通过定时任务删除 Redis 中过期的 Session使之尽快释放 Redis 的内存。 在 springSessionDefaultRedisSerializer() 方法中定义了一个 Bean 名字为 springSessionDefaultRedisSerializer 的 RedisSerializer Bean采用 JSON 序列化方式。默认情况下采用 Java 自带的序列化方式可读性较差所以需要进行替换。
配置 Redis 序列化 RedisConfig.java package com.lm.system.config;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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** author DUHAOLIN* date 2024/11/13*/
Configuration
public class RedisConfig {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) {RedisTemplateString, Object redisTemplate new RedisTemplate(); redisTemplate.setConnectionFactory(factory);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value序列化方式json使用GenericJackson2JsonRedisSerializer替代默认序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet(); return redisTemplate;}}测试 SessionController.java package com.lm.system.controller;import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;/*** author DUHAOLIN* date 2024/11/22*/
RestController
RequestMapping(session)
public class SessionController {PostMapping(set)public void set(HttpSession session, RequestParam(key) String key, RequestParam(value) String value) {session.setAttribute(key, value);}GetMapping(getAll)public MapString, Object getAll(HttpSession session) {MapString, Object resultMap new HashMap();for(EnumerationString names session.getAttributeNames(); names.hasMoreElements();) {String key names.nextElement();Object value session.getAttribute(key);resultMap.put(key, value);}return resultMap;}}效果图
启动程序后进行查询未查询到 Session 数据。 客户端访问完到 Redis 客户端进行查询则能够查询到 Spring Session 保存到 Redis 的 Session 数据。 每个 Session 对应 Redis 中 两个键值对。
头部以 spring:session 开头可以通过 EnableRedisHttpSession 直接的 redisNamespace 属性进行配置。结尾以对应 Session 的 sessionId 结尾。中间中间分别是 session、expirations、sessions:expires 。 从下图该 session 的内容可以看出其是一个 Redis Hash 数据结构。 客户端发起 set 请求设置 key 和 value。 此时再次在 Redis 客户端进行查询则该 Session 信息已经发生改变。
Redis 序列化
RedisSerializer
org.springframework.data.redis.serializer.RedisSerializer 接口Redis 序列化接口用于 Redis KEY 和 VALUE 的序列化。 RedisSerializer.java public interface RedisSerializerT {Nullablebyte[] serialize(Nullable T t) throws SerializationException;NullableT deserialize(Nullable byte[] bytes) throws SerializationException;}定义了对象 和二进制数组的转换。 Redis Client 传递给 Redis Server 是传递的 KEY 和 VALUE 都是二进制值数组。 RedisSerializer 的实现类 主要分为四类JDK 序列化方式、String 序列化方式、JSON 序列化方式、XML 序列化方式。
JDK序列化方式
介绍
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer 默认情况下RedisTemplate 使用该数据列化方式。 RedisTemplate#afterPropertiesSet() 方法在 RedisTemplate 未设置序列化的情况下使用 JdkSerializationRedisSerializer 作为序列化实现。在 Spring Boot 自动化配置 RedisTemplate Bean 对象时就未设置。 绝大情况下不会使用该序列化如下图所示写入的 KEY 前会携带 16 进制字符而通过该 KEY 获取的 VALUE 也携带有 16 进制字符获取的结果不方便阅读。 在 ObjectOutputStream#writeString(String strboolean unshared) 代码中实际是标志位 字符串长度 字符串内容。 注测试之前如果前面配置了序列化RedisConfig.java需要先注释 Configuration 注解。
测试代码 RedisTest.java package com.lm.system;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;/*** author DUHAOLIN* date 2024/11/29*/
SpringBootTest
RunWith(value SpringJUnit4ClassRunner.class)
public class RedisTest {Resourceprivate RedisTemplate redisTemplate;Testpublic void test01() {redisTemplate.opsForValue().set(name, Jack);}}
效果图 String序列化方式
org.springframework.data.redis.serializer.StringRedisSerializer字符串和二进制数组的直接转换。 StringRedisSerializer.java private final Charset charset;Override
public String deserialize(Nullable byte[] bytes) {return (bytes null ? null : new String(bytes, charset));
}Override
public byte[] serialize(Nullable String string) {return (string null ? null : string.getBytes(charset));
}绝大多数情况下KEY 和 VALUE 使用的此序列化方式。而 VALUE 的序列化和反序列化需要开发人员自己在逻辑调用 JSON 方法来序列化。 org.springframework.data.redis.serializer.GenericToStringSerializer 使用 Spring ConversionService 实现 对象和 String 的转换从而 String 和二进制数组的转换。 序列化的过程首先 对象通过 ConversionService 转换成 String 然后 String 再序列化成二进制数组。
JSON 序列化方式
1org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer使用 Jackson 实现 JSON 的序列化方式是支持所有类。 GenericJackson2JsonRedisSerializer.java public GenericJackson2JsonRedisSerializer(Nullable String classPropertyTypeName) {this(new ObjectMapper());// simply setting {code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need// the type hint embedded for deserialization using the default typing feature.mapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));//1if (StringUtils.hasText(classPropertyTypeName)) {mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);//2} else {mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);}
}1 如果传入 classPropertyTypeName 属性使用传入对象的 classPropertyTypeName 属性对应的值作为默认类型Default Typing。 2 如果未传入 classPropertyTypeName 属性则使用传入对象的类全名作为默认类型Default Typing。 2org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer使用 Jackson 实现 JSON 的序列化方式并且显示指定 类型。 Jackson2JsonRedisSerializer.java public class Jackson2JsonRedisSerializerT implements RedisSerializerT {//...public static final Charset DEFAULT_CHARSET StandardCharsets.UTF_8;/*** 指定类型和 T 要一致。*/private final JavaType javaType;private ObjectMapper objectMapper new ObjectMapper();}Jackson2JsonRedisSerializer 序列化类里已经声明了类型所以序列化的 JSON 字符串无需在存储一个 class 属性用于存储类型。 3com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer使用 FastJSON 实现 JSON 的序列化方式和 GenericJackson2JsonRedisSerializer 一致。 4com.alibaba.fastjson.support.spring.FastJsonRedisSerializer使用 FastJSON 实现 JSON 的序列化方式和 Jackson2JsonRedisSerializer 一致。
XML序列化方式
org.springframework.data.redis.serializer.OxmSerializer使用 Spring OXM 实现将对象和 String 的转换从而 String 和二进制数组的转换。
参考链接
【Spring Boot 分布式 Session 入门】https://www.iocoder.cn/Spring-Boot/Distributed-Session/?self