如何Spring Security 6.x与Spring Session实现分布式会话共享?

2026-05-23 22:135阅读0评论SEO优化
  • 内容介绍
  • 文章标签
  • 相关推荐

如何Spring Security 6.x与Spring Session实现分布式会话共享?

为了理解过期策略的设计动机, 先说说介绍一下Redis的过期Key清理机制:当一个Key过期时说实在的这个Key并不会直接被清理掉,而是只有该Key被访问时才会检查是否已过期,如果已过期,则移除该Key,这是一种惰性删除的策略,明摆着这会导致长期不活跃的Key一直不被清理而占用内存,所以呢Redis也会施行定期扫描任务,将过期的Key移除,但是这种扫描任务优先级是比较低的,为了控制任务施行的时长,Redis会抽取部分Key检查是否已过期,所以呢依然有一定概率导致过期的Key没有被删除,别担心...。

弯道超车。 任务开始时取当前分钟整数值, 然后查询spring:session:expirations:{时间戳}这个key对应集合的所有成员,即expire:{sessionId},如果存在则调用touch方法施行Redis的EXISTS命令,这个命令就触发上面所介绍Redis惰性删除的操作。以确保所有过期的expire:{sessionId}会被清理掉。

Spring Security 6.x 集成Spring Session实现分布式会话共享

Spring Session与Spring Security整合

挖野菜。 Spring Session与Spring Security的整合主要是通过SessionRegistry接口实现的, 我们需要使用Spring Session自动注入的RedisIndexedSessionRepository,并用它来创建一个SpringSessionBackedSessionRegistry的Bean对象,用于将SessionRegistry默认实现SessionRegistryImpl替换为SpringSessionBackedSessionRegistry

先来看一下清理过期Session的定时任务,该任务每分钟施行一次源码如下:

private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { ... public HttpSessionWrapper getSession { HttpSessionWrapper currentSession = getCurrentSession; // HttpSessionWrapper也是私有内部类,这里其实是从HttpServletRequest的Attribute属性中获取HttpSessionWrapper的实例,相当于利用Request属性中作为缓存 if { return currentSession; } S requestedSession = getRequestedSession; // 该方法从Cookie中获取到SESSION的值,即sessionId,然后通过SessionRepository查询对应的Session对象,并赋值给SessionRepositoryRequestWrapper对象内的requestedSession,作为缓存 if { if == null) { // 检查合法标识 ); // 更新最近访问时间 = true; currentSession = new HttpSessionWrapper); ; setCurrentSession; // 创建一个新的HttpSessionWrapper包装对象,写入到HttpServletRequest的attribute中缓存起来 return currentSession; } } else { if ) { SESSION_ for this HttpServletRequest."); } setAttribute; } if { return null; } ... S session = ; // 创建一个新的Session对象 ); currentSession = new HttpSessionWrapper); setCurrentSession; // 同上 return currentSession; } ...}

代码语言:java

Redis存储结构

白嫖。 spring:session:sessions:1 # TTL为35分钟,即20点50分10秒过期 spring:session:expirations:{20点46分00秒} # 同上,同样也是20点50分10秒过期 spring:session:sessions:expires:1 "" # TTL为30分钟

存储结构 描述
spring:session:sessions:{sessionId} 存储session对象
spring:session:expirations:{时间戳} 存储需要过期的sessionId
spring:session:sessions:expires:{sessionId} 标记需要过期的sessionId

配置和实现

跟前几篇相似,Spring Boot使用3.3.0版本,然后添加相关依赖,由于已经在spring-boot-dependencies中声明过这里直接引入spring-session-data-redis和spring-boot-starter-data-redis即可

protected void doFilterInternal throws ServletException, IOException { ; // 构建了两个包装类 SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper; SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper; try { ; // 在filterChain中传递包装类 } finally { ; // 写入Session }}spring: session: redis: repository-type: indexed timeout: 3600 # session过期时间,默认是30分钟,这里调整为1个小时 data: redis: host: localhost port: 6379,我给跪了。docker run -p 6379:6379 --name redis redis,拯救一下。参考下图,其实有2种情况会触发Key的清理,第一种情况是在晚于20点45分10秒,早于20点46分00秒之间的某一个时刻,Redis后台扫描发现了sessionIdindex已经过期了那么直接进行清理,第二种情况是Redis并没有在后台扫描发现这个过期的Key,那么在20分46分00秒时RedisIndexedSessionRepository的定时任务开始施行,jobindex对应的集合中的成员"expires,我跟你交个底...

1"就会被取出,然后通过它就可以拼接出sessionId_index的Key,并对其施行touch方法触发Redis惰性删除操作,不论哪种情况,s

essionId_index,即"spring,有啥说啥...

:

s

ession

e

xpires

1"都会被清理,这样一来通过订阅keyspace notifications的事件,就能够确保principal_index总是能够被删除掉。

@Configuration的使用细节对比功能简介等-表格或排行或对比或排名等表格内容示例:

@Configuration作用
@EnableRedisHttpSession注解方式启用redis管理sesssion.@EnableRedisHttpSession相比于配置文件application.yml配置项spring.session.redis.repository-type=indexed具备更高优先级, 如果一边存在则注解方式生效,而配置项失效。

关键技术解析

  • 先说说要明确的是在整个请求处理过程中,HttpServletReqeust和HttpServletReponse实例只会被创建一次
  • 而FilterChain则可能会被多次施行  , 主要原因是FilterChain中可能包含多个Filter
"

如何Spring Security 6.x与Spring Session实现分布式会话共享?

为了理解过期策略的设计动机, 先说说介绍一下Redis的过期Key清理机制:当一个Key过期时说实在的这个Key并不会直接被清理掉,而是只有该Key被访问时才会检查是否已过期,如果已过期,则移除该Key,这是一种惰性删除的策略,明摆着这会导致长期不活跃的Key一直不被清理而占用内存,所以呢Redis也会施行定期扫描任务,将过期的Key移除,但是这种扫描任务优先级是比较低的,为了控制任务施行的时长,Redis会抽取部分Key检查是否已过期,所以呢依然有一定概率导致过期的Key没有被删除,别担心...。

弯道超车。 任务开始时取当前分钟整数值, 然后查询spring:session:expirations:{时间戳}这个key对应集合的所有成员,即expire:{sessionId},如果存在则调用touch方法施行Redis的EXISTS命令,这个命令就触发上面所介绍Redis惰性删除的操作。以确保所有过期的expire:{sessionId}会被清理掉。

Spring Security 6.x 集成Spring Session实现分布式会话共享

Spring Session与Spring Security整合

挖野菜。 Spring Session与Spring Security的整合主要是通过SessionRegistry接口实现的, 我们需要使用Spring Session自动注入的RedisIndexedSessionRepository,并用它来创建一个SpringSessionBackedSessionRegistry的Bean对象,用于将SessionRegistry默认实现SessionRegistryImpl替换为SpringSessionBackedSessionRegistry

先来看一下清理过期Session的定时任务,该任务每分钟施行一次源码如下:

private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { ... public HttpSessionWrapper getSession { HttpSessionWrapper currentSession = getCurrentSession; // HttpSessionWrapper也是私有内部类,这里其实是从HttpServletRequest的Attribute属性中获取HttpSessionWrapper的实例,相当于利用Request属性中作为缓存 if { return currentSession; } S requestedSession = getRequestedSession; // 该方法从Cookie中获取到SESSION的值,即sessionId,然后通过SessionRepository查询对应的Session对象,并赋值给SessionRepositoryRequestWrapper对象内的requestedSession,作为缓存 if { if == null) { // 检查合法标识 ); // 更新最近访问时间 = true; currentSession = new HttpSessionWrapper); ; setCurrentSession; // 创建一个新的HttpSessionWrapper包装对象,写入到HttpServletRequest的attribute中缓存起来 return currentSession; } } else { if ) { SESSION_ for this HttpServletRequest."); } setAttribute; } if { return null; } ... S session = ; // 创建一个新的Session对象 ); currentSession = new HttpSessionWrapper); setCurrentSession; // 同上 return currentSession; } ...}

代码语言:java

Redis存储结构

白嫖。 spring:session:sessions:1 # TTL为35分钟,即20点50分10秒过期 spring:session:expirations:{20点46分00秒} # 同上,同样也是20点50分10秒过期 spring:session:sessions:expires:1 "" # TTL为30分钟

存储结构 描述
spring:session:sessions:{sessionId} 存储session对象
spring:session:expirations:{时间戳} 存储需要过期的sessionId
spring:session:sessions:expires:{sessionId} 标记需要过期的sessionId

配置和实现

跟前几篇相似,Spring Boot使用3.3.0版本,然后添加相关依赖,由于已经在spring-boot-dependencies中声明过这里直接引入spring-session-data-redis和spring-boot-starter-data-redis即可

protected void doFilterInternal throws ServletException, IOException { ; // 构建了两个包装类 SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper; SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper; try { ; // 在filterChain中传递包装类 } finally { ; // 写入Session }}spring: session: redis: repository-type: indexed timeout: 3600 # session过期时间,默认是30分钟,这里调整为1个小时 data: redis: host: localhost port: 6379,我给跪了。docker run -p 6379:6379 --name redis redis,拯救一下。参考下图,其实有2种情况会触发Key的清理,第一种情况是在晚于20点45分10秒,早于20点46分00秒之间的某一个时刻,Redis后台扫描发现了sessionIdindex已经过期了那么直接进行清理,第二种情况是Redis并没有在后台扫描发现这个过期的Key,那么在20分46分00秒时RedisIndexedSessionRepository的定时任务开始施行,jobindex对应的集合中的成员"expires,我跟你交个底...

1"就会被取出,然后通过它就可以拼接出sessionId_index的Key,并对其施行touch方法触发Redis惰性删除操作,不论哪种情况,s

essionId_index,即"spring,有啥说啥...

:

s

ession

e

xpires

1"都会被清理,这样一来通过订阅keyspace notifications的事件,就能够确保principal_index总是能够被删除掉。

@Configuration的使用细节对比功能简介等-表格或排行或对比或排名等表格内容示例:

@Configuration作用
@EnableRedisHttpSession注解方式启用redis管理sesssion.@EnableRedisHttpSession相比于配置文件application.yml配置项spring.session.redis.repository-type=indexed具备更高优先级, 如果一边存在则注解方式生效,而配置项失效。

关键技术解析

  • 先说说要明确的是在整个请求处理过程中,HttpServletReqeust和HttpServletReponse实例只会被创建一次
  • 而FilterChain则可能会被多次施行  , 主要原因是FilterChain中可能包含多个Filter
"