文章目录

  • 目标
  • 集群会话
  • session 保持
  • session 复制
  • session 共享
  • Session 共享实现
  • 代码实践
  • 核心依赖
  • 核心配置
  • redisson.yml
  • spring session 的配置
  • Spring Security 整合 Spring session
  • 问题解决


目标

  • 了解与Spring session ,redis 的整合
    参考:spring security 实战书籍 6.6 章节

集群会话

单机提供单服务只能存在于测试环境,正式环境部署工程,一般都是集群部署或者单机多服务部署。看下两者会话信息的不同:

单机单服务:通过一个服务中间件实例,例如tomcat,来提供服务。使用session来保持会话信息,session 信息被存储在内存中。

集群部署或者单机多服务:采用了多台服务器,或者多个tomcat提供服务,但是多台服务器或者多个tomcat ,session 是无法共享的。大概有三种解决方案:

  • session 保持
  • session 复制
  • session 共享

session 保持

一般依靠负载均衡组件(例如nginx)ip哈希负载策略来将相同客户端的请求转发到同一个tomcat实例上。这样同一个客户的会话信息,只会存在于一台tomcat,就无需对session 进行处理。优点:简单。缺点:可能会出现负载失衡,可能使用同个ip出口,导致这些请求都转发到了一个tomcat 上。

如果就部署个两台,这种方式简单快捷,优先使用。

session 复制

将每个集群服务器的session信息,复制到其他服务器上,保持会话一致。缺点:实现难度大,消耗资源多。

session 共享

集群部署推荐方式,将session 数据都抽离到第三方容器中,每个服务器实例都可以操作这个第三方容器,实现对session 的存取。优点: 第三方容器数据容量相较于服务器内存大很多,服务实例中断,不会导致会话信息丢失。缺点:需要引入第三方组件,降低系统的稳定性,增加网络开销等。

Session 共享实现

数据容器:Redis 采用Redisson框架(具有内存中数据网格功能的Redis Java客户端)

集成session 框架:spring session

项目环境:spring boot 2.2.7

简单说下Redisson,通常使用该框架,来实现redis 分布式锁的实现。当然还包含了很多其他工程。可以与spring 的众多框架结合(spring boot ,spring data,spring cache ,spring session),官方地址:https://github.com/redisson/redisson

代码实践

代码参考:https://github.com/gengzi/gengzi_spring_security

核心依赖

spring session 与 redis 结合,只需要导入 spring-session-data-redis 。 无需再次引入 spring-session-core 。

redisson 提供的 spring session 结合的配置和依赖:https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#147-spring-session

只说明核心依赖,其他请参考:pom.xml

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--data-redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
         <!--session-redis-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <!-- for Spring Data Redis v.2.2.x -->
            <artifactId>redisson-spring-data-22</artifactId>
            <version>3.13.6</version>
        </dependency>

核心配置

代码参考:RedissonSpringDataConfig

这个也是 redisson 官方提供的配置类,另外需要在 resources 目录配置 redisson.yml ,用于配置连接redis服务器的配置。

@Configuration
// 启用redis管理会话
@EnableRedisHttpSession
public class RedissonSpringDataConfig extends AbstractHttpSessionApplicationInitializer {
 
    @Bean
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }
 
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson(@Value("classpath:/redisson.yml") Resource configFile) throws IOException {
        Config config = Config.fromYAML(configFile.getInputStream());
        return Redisson.create(config);
    }
 
}

简单说下,AbstractHttpSessionApplicationInitializer 的作用:

spring session 提供了一个 springSessionRepositoryFilter 的类,来使得spring session 替换 HttpSession。那为了确保我们的Servlet容器(即Tomcat)对每个请求都使用springSessionRepositoryFilter,需要继承 AbstractHttpSessionApplicationInitializer ,来保证spring 会加载我们的 配置。

redisson.yml

配置在 resources 目录下:

更多配置请参考Redisson 的说明:https://github.com/redisson/redisson/wiki/Table-of-Content 提供了各个模式的配置

代码参考:redisson.yml

singleServerConfig:
  address: "redis://127.0.0.1:6379"
  password: xxx
  database: 1

spring session 的配置

如果不使用 Redisson ,也可以直接提供的默认配置来连接redis 服务器

application.yml

session:
      # 会话存储类型
    store-type: redis

Spring Security 整合 Spring session

代码参考:WebSecurityConfig

// ------------  会话相关配置 start -------------
    // 会话存储库
    @Autowired
    private FindByIndexNameSessionRepository sessionRepository;
 
    // spring session 会话注册表
    @Bean
    public SpringSessionBackedSessionRegistry sessionRegistry() {
        return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
    }
    // ------------  会话相关配置 end -------------
 
 
        @Override
    protected void configure(HttpSecurity http) throws Exception {
 
        http.
                .sessionManagement((sessionManagement) -> sessionManagement
                        .maximumSessions(100)
                        .sessionRegistry(sessionRegistry()));
    }

简单说明一下:FindByIndexNameSessionRepository 返回了一个 session 的实例对象

这个配置其实是 Spring Security并发会话控制 , maximumSessions (最大session个数)控制单个用户同时活动的数量。测试环境,建议调大,便于测试。生产环境,看业务需要,如果限制单个用户只能在一端登陆,就设置为1.

  • Spring Security记住我支持
    这个暂时不说明,后面会细说。需要的直接看官方文档:https://docs.spring.io/spring-session/docs/2.4.1/reference/html5/#spring-security-rememberme

问题解决

  1. 报错:RedisConnectionFactory is required 异常
    原因,导错包了,仅导入了spring-session-core的依赖,导致每次启动项目都不加载redisson 的配置,每次去创建 RedisTemplate 都提示 RedisConnectionFactory 为null。