分布式RedisSession的探索

221 阅读3分钟

`这是我参与更文挑战的第8天,活动详情查看: 更文挑战

立个Flag每天写点东西,坚持下去。

分布式RedisSession的探索

​ 为什么需要分布式session,对于API接口是不需要session的,对于后台管理页面,基于session的认证及授权如shiro是需要分布式session的。

​ spring cloud常用的分布式session解决方案redisSession是基于cookie来存贮session的,对于前后端分离的项目,可以在请求中携带cookie。

1. 搭建一个项目 distribute-session

启动类入口增加 @EnableRedisHttpSession注解 启用分布式session,将服务注册到注册中心nacos.

主要依赖

<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>

增加redis配置

spring:
  redis:
    host: 127.0.0.1
    port: 6379
#    password: 123456

增加测试方法,通过指定不同port启动两个服务。

@GetMapping("/index")
public String index(HttpServletRequest request){
  String sessionId = request.getSession().getId();
  log.info("HomeController index port {} sessionId: {}",port,sessionId);

  return sessionId;
}

2. 创建网关项目

路由配置

spring:
  cloud:
    gateway:
      #路由配置:参数为一个List
      routes:
      #唯一标识
      - id: paw-distribute-session-route
        #转发的地址,写服务名称
        uri: lb://paw-distribute-session
        #判断匹配条件,即地址带有path的请求
        predicates:
        - Path=/distribute-session-api/**
        filters:
        #去掉Path前缀,参数为1代表去掉1ceng
        - StripPrefix=1

​ 启动服务,访问 http://127.0.0.1:9000/distribute-session-api/index 会发现 不同port服务返回的sessionId相同,即分布式session搭建完成。

3. RedisHttpSession的探索

从注解中看到配置类 RedisHttpSessionConfiguration,类中注入了session持久化的类RedisIndexedSessionRepository,该类实现了SessionRepository。

查看createSession方法,通过MapSession以UUID的方式创建了sessionId,通过redisTemplate存储到redis中

public RedisSession createSession() {
 MapSession cached = new MapSession();
 if (this.defaultMaxInactiveInterval != null) {
  cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
 }
 RedisSession session = new RedisSession(cached, true);
 session.flushImmediateIfNecessary();
 return session;
}
private static String generateId() {
 return UUID.randomUUID().toString();
}

EnableRedisHttpSession 说明中更多的配置参见 EnableSpringHttpSession,打开配置类SpringHttpSessionConfiguration,其中有一些 required=false的配置,程序内部提供了默认实现,即用户可以进行进行自定义配置。如HttpSessionIdResolver 提供了基于cookie的sessionId解析器,并默认进行base64编码。

private HttpSessionIdResolver httpSessionIdResolver = this.defaultHttpSessionIdResolver;

@Autowired(required = false)
public void setHttpSessionIdResolver(HttpSessionIdResolver httpSessionIdResolver) {
 this.httpSessionIdResolver = httpSessionIdResolver;
}

支持redisHttpSession的大致配置及流程已有脉路。

4. 基于token的分布式session

思考: 对于前后端分离的管理后台项目,是否可以携带token的方式进行保持session会话。

自定义 HttpSessionIdResolver 并注入到配置中

// 实现接口 HttpSessionIdResolver
public class TokenHttpSessionIdResolver implements HttpSessionIdResolver {
  
  public static final String TOKEN = "token";
  
  public TokenHttpSessionIdResolver(){
  }
  
  @Override
  public List<String> resolveSessionIds (HttpServletRequest request) {
    List<String> list = new ArrayList<>();
    // 从header中取出token作为sessionId
    String token = request.getHeader(TOKEN);
    if(StrUtil.isNotEmpty(token)){
      list.add(token);
    }
    return list;
  }

  @Override
  public void setSessionId (HttpServletRequest request, HttpServletResponse response, String sessionId) {
  }

  @Override
  public void expireSession (HttpServletRequest request, HttpServletResponse response) {
  }
}
@Configuration
public class WebConfiguration {
	// 注入bean
  @Bean
  public HttpSessionIdResolver httpSessionIdResolver(){
    return new TokenHttpSessionIdResolver();
  }

}

增加获取token的接口

@GetMapping("/getToken")
public String getToken(HttpServletRequest request){
  String sessionId = request.getSession().getId();
  log.info("sessionId: {}",sessionId);

  return sessionId;
}

获取token后,再次请求在header中携带token,sessionId保持不变。

此种方式仅供娱乐研究。

总结:

RedisHttpSession 通过生成一个UUID的session 存储在Redis中,并写入到cookies中返回给浏览器,浏览器再次访问时携带cookies, springHttpSession解析获取session,并根据redis存贮的对比一致,则是同一个会话。