SpringBoot 分布式 session
在如今服务器集群的情况下,用户登录会话状态的保存也从单机的变成了分布式要求的,下面详细说一下几种分布式 session 存储方案。
- session 复制:在支持 session 复制的服务器上进行,同步 session,保持 session 一致
方案:
tomcat-redis-session-manager
- session 粘滞:强行分发 session 到各个服务器
方案:负载均衡
- cookie 存储 session:把 sessionid 存储到 cookie 中(不安全,cookie 容易被盗取,可以存储不重要的数据)
- session 集中管理:把用户的 session 存储在单台或者集群服务器的缓存中,所有 web 服务器从中拿取 session,实现 session 共享
方案:Redis 存储用户生成的 sessionId 或者存储保存 sessionId 的 cookie
这里只讲解第四种方案,使用最多最稳定
Redis 做缓存持久化存储 session
Redis 存储 cookie,里面保存用户生成的 uuid(token 作为 sessionId)
- cookie 名称和过期时间
public static final String COOKIE_NAME_TOKEN = "token";
private static final ex = 3600;
- 创建 cookie 并存储到 redis
private String addCookie(HttpServletResponse response,SeckillUser user){String token = UUIDUtil.uuid();
redisService.set(token,user);// 使用 redis 把 token 作为键存储 user 作为值 我使用 jedis 自己实现的也可以使用 redisTemplate
Cookie cookie = new Cookie(COOKIE_NAME_TOKEN, token);// 设置 cookie 名称为 token
cookie.setMaxAge(ex);// 设置过期时间
cookie.setPath("/");
response.addCookie(cookie);
return token;
}
- uuid 作为 sessionid 生成工具
public class UUIDUtil {public static String uuid(){return UUID.randomUUID().toString();}
}
使用 Shiro 集成的crazy-cake
(使用 Redis 存储 SessionId)
- shiro 是一个 Web 安全框架,用于登录认证,用户身份授权,使用易于
spring-security
,还可以继承 session、cookie 分布式存储
不了解的可以看这篇博文:Shiro 的使用以及集成 redis 做缓存
缓存
- 使用缓存存储 session(单服务器使用 EhCacheManager)
- 但是在分布式系统中,服务器集群情况下,EhCacheManager 无法解决数据共享(会多次查询数据库),则选择使用 redis 作为缓存
Redis 实现 shiro 缓存
- 分布式共享 session 和授权信息需要把 session 和授权持久化到数据库或者缓存 shiro 集群为了防止多次插查询数据库
-
自定义实现类: 或者使用 crazycake 开源 shiro-redis 实现好的工具
- RedisSessionDAO 可以继承 EnterpriseCacheSessionDAO 实现 session 控制
- RedisCache 继承 Cache 类实现具体 redis 操作缓存(remove、get、set、keys
- RedisCacheManager 实现接口 CacheManager 的 getCache 获得 RedisCache 交给 securityManager 管理
使用了 ConcurrentMap 管理数据和缓存,更加高效
- 在
ShiroConfig
配置类中把sessionManager
交给DefaultWebSecurityManager
管理
@Bean
public DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//session 时间
sessionManager.setGlobalSessionTimeout(redisConfig().getTimeout());
// 删除无效 session
sessionManager.setDeleteInvalidSessions(true);
log.info("sessionManager 注入成功");
sessionManager.setSessionIdCookie(cookie());
// sessionDao 分布式共享 session 和授权信息需要把 session 和授权持久化到数据库或者缓存 shiro 集群为了防止多次插查询数据库
sessionManager.setSessionDAO(redisSessionDao());
return sessionManager;
}
- 在
sessionManager
中注入 cookie 存储jsessionId
@Bean
public SimpleCookie cookie() {SimpleCookie cookie = new SimpleCookie("JSESSIONID");
cookie.setHttpOnly(true);
cookie.setPath("/");
return cookie;
}
- 再在
sessionManager
中注入redisSessionDao
负责 session 持久化
@Bean
public RedisSessionDAO redisSessionDao(){RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
return redisSessionDAO;
}