(6)SprintBoot 2.X 基于Redis的分布式session

it2022-06-23  79

(6)SprintBoot 2.X 基于Redis的分布式session

1.基本原理1. Session的作用2. 分布式Session存在的问题?3. 解决方案4. 本系统解决方案 2. 代码实现1. Controller业务逻辑2. 需要存到redis缓存中的key的前缀3. MiaoshaUserService实现分布式session

1.基本原理

1. Session的作用

Session 是客户端与服务器通讯会话跟踪技术,服务器与客户端保持整个通讯的会话基本信息。

客户端在第一次访问服务端的时候,服务端会响应一个sessionId并且将它存入到本地cookie中,在之后的访问会将cookie中的sessionId放入到请求头中去访问服务器,如果通过这个sessionid没有找到对应的数据那么服务器会创建一个新的sessionid并且响应给客户端。

2. 分布式Session存在的问题?

假设第一次访问服务A生成一个sessionid并且存入cookie中,第二次却访问服务B客户端会在cookie中读取sessionid加入到请求头中,如果在服务B通过sessionid没有找到对应的数据那么它创建一个新的并且将sessionid返回给客户端,这样并不能共享我们的Session无法达到我们想要的目的。

3. 解决方案

使用Redis作为session存储容器,登录时将session信息存储至cookie客户端,同时服务端将session信息存至redis缓存,双重保障,接下来的接口调用直接可以获取到cookie中的token信息作为参数传递进来即可,如果发现token为空,则再从redis中获取,如果两者都为空,则说明session已过期。

4. 本系统解决方案

用户登录成功之后,给这个用户生成一个sessionId(用token来标识这个用户,token利用uuid生成),写到cookie中,传递给客户端。然后客户端在随后的访问中,都在cookie中上传这个token,然后服务端拿到这个token之后,就根据这个token来取得对应的session信息。

2. 代码实现

1. Controller业务逻辑

@RequestMapping("/do_login") @ResponseBody public Result<String> doLogin(HttpServletResponse response , @Valid LoginVo loginVo) { log.info(loginVo.toString()); String token = userService.login(response,loginVo); return Result.success(token); }

2. 需要存到redis缓存中的key的前缀

public class MiaoshaUserKey extends BasePrefix { public static final int TOKEN_EXPIRE = 3600*24 *2;//默认两天 /** * 防止被外面实例化 */ private MiaoshaUserKey(int expireSeconds, String prefix) { super(expireSeconds, prefix); } /** * 需要缓存的字段 */ public static MiaoshaUserKey token = new MiaoshaUserKey(TOKEN_EXPIRE,"tk"); public static MiaoshaUserKey getById = new MiaoshaUserKey(0, "id"); }

3. MiaoshaUserService实现分布式session

将token做为key,用户信息做为value 存入redis模拟session,键值对为(token,user) 同时将token存入cookie,返还给客户端保存登录状态,键值对为(“token”,token)当客户端再请发送请求时,如果getByToken()获取缓存对象成功,则需要再次调用addCookie()延长session的有效期。 @Service public class MiaoshaUserService { public static final String COOKIE_NAME_TOKEN = "token"; @Autowired MiaoShaUserDao miaoShaUserDao; @Autowired RedisService redisService; public MiaoshaUser getById(long id){ //对象缓存 MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, "" + id, MiaoshaUser.class); if (user != null) { return user; } //取数据库 user = miaoShaUserDao.getById(id); //再存入缓存 if (user != null) { redisService.set(MiaoshaUserKey.getById, "" + id, user); } return user; } /** * 典型缓存同步场景:更新密码 */ public boolean updatePassword(String token, long id, String formPass) { //取user MiaoshaUser user = getById(id); if(user == null) { throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } //更新数据库 MiaoshaUser toBeUpdate = new MiaoshaUser(); toBeUpdate.setId(id); toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt())); miaoShaUserDao.update(toBeUpdate); //更新缓存:先删除再插入 redisService.delete(MiaoshaUserKey.getById, ""+id); user.setPassword(toBeUpdate.getPassword()); redisService.set(MiaoshaUserKey.token, token, user); return true; } public String login(HttpServletResponse response,LoginVo loginVo){ if (loginVo == null) { throw new GlobalException(CodeMsg.SERVER_ERROR); } String mobile = loginVo.getMobile(); String formPass = loginVo.getPassword(); //判断手机号是否存在 MiaoshaUser user = getById(Long.parseLong(mobile)); if (user == null) { throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST); } //验证密码 String dbPass = user.getPassword(); String saltDB = user.getSalt(); String calcPass = MD5Util.formPassToDBPass(formPass, saltDB); if (!calcPass.equals(dbPass)) { throw new GlobalException(CodeMsg.PASSWORD_ERROR); } String token = UUIDUtil.uuid(); addCookie(response, token, user); return token; } /** * 将token做为key,用户信息做为value 存入redis模拟session,键值对为(token,user) * 同时将token存入cookie,保存登录状态,键值对为("token",token) */ public void addCookie(HttpServletResponse response, String token , MiaoshaUser user) { //String token = UUIDUtil.uuid(); redisService.set(MiaoshaUserKey.token, token, user); Cookie cookie = new Cookie(COOKIE_NAME_TOKEN, token); cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds()); cookie.setPath("/");//设置为网站根目录 response.addCookie(cookie); } public MiaoshaUser getByToken(HttpServletResponse response, String token) { if(StringUtils.isEmpty(token)){ return null; } MiaoshaUser user = redisService.get(MiaoshaUserKey.token,token,MiaoshaUser.class); //延长缓存key的有效期 if(user !=null){ addCookie(response, token, user); } return user; } }

最新回复(0)