集群session的共享问题

news/2024/10/30 17:18:00/

基于redis实现共享session登录

1.集群session共享的问题

session共享问题:多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失问题

替代方案应该满足:

数据共享

内存存储

key、value结构

 

2.基于redis实现session共享登录

 

需要生成token令牌,通过token的值去确定哪个用户做出请求

将token作为key用户信息作为值存储到redis中,利用的模板时StringRedisTemplate这样不仅可以节省服务器的空间还增加可读性

@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {//1.校验手机号的格式String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {//2.不一致直接报错return Result.fail("手机号错误");}//3.比较验证码String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);String code = loginForm.getCode();if(session==null || !cacheCode.equals(code)){//4.不一致直接报错return Result.fail("错误信息");}//5.根据手机号查询用户LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();query.eq(User::getPhone,loginForm.getPhone());User user = this.getOne(query);if(user==null){//6.不存在直接创建新用户保存到数据库中user=createUserWithPhone(loginForm.getPhone());}UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);//7.最终将用户信息保存到redis中  使用UserDTO保护用户的隐私//7.1生成tokenString tokenKey = UUID.randomUUID().toString();/** 当用户第一次登录后,服务器生成一个token并将此token返回给客户端,* 以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。* *///7.2 将user转为hash存储Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(), CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));//7.3存储stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+tokenKey,userMap);//7.4设置存储的生命周期/** 这里设置过30min会自动从redis中剔除 但我们想要的效果是30min没有用到token时剔除* 这时我们需要去拦截器里面设置并且刷新token* */stringRedisTemplate.expire(LOGIN_USER_KEY+tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);//8.返回给客户端return Result.ok(tokenKey);
}

拦截器的设定之前设置的拦截器放行了部分页面,如果我们只访问那种部分页面时拦截器直接放行导致不能够刷新redis中的数据,现在设定两个拦截器,一个负责专门刷新redis的生命周期,另一个负责拦截

//注册拦截器  及其相关配置
@Configuration
public class MvcConfig implements WebMvcConfigurer {//添加拦截器@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//order的值越小 执行的优先级越高//登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);//刷新拦截器  拦截所有registry.addInterceptor(new ReFreshTokenInterceptor(redisTemplate)).order(0).addPathPatterns("/**");
​}
}
​
@SuppressWarnings("all")
//创建拦截器
public class ReFreshTokenInterceptor implements HandlerInterceptor {//有注册拦截器注入beanprivate StringRedisTemplate stringRedisTemplate;
​public ReFreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}
​@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取tokenString token = request.getHeader("authorization");if(StringUtils.isEmpty(token)){return true;}//2.通过token从redis中拿到用户信息Map<Object, Object> user = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY+token);if(user.isEmpty()){
​return true;}//4.存在  将Map对象转为UserDto对象UserDTO userDTO = BeanUtil.fillBeanWithMap(user, new UserDTO(), false);//5.存储到ThreadLocal中UserHolder.saveUser(userDTO);//6.设置token的刷新时间stringRedisTemplate.expire(LOGIN_CODE_KEY+token,LOGIN_USER_TTL, TimeUnit.MINUTES);//5.放行return true;}
​@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//避免造成内存泄露UserHolder.removeUser();}
}
public class LoginInterceptor implements HandlerInterceptor {//有注册拦截器注入bean@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.只需要判断ThreadLocal中有没有用户信息if(UserHolder.getUser()==null){//响应未授权的状态信息response.setStatus(401);//拦截return false;}//放行return true;}
​@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//避免造成内存泄露UserHolder.removeUser();}
}

http://www.ppmy.cn/news/61402.html

相关文章

【LeetCode】300. 最长递增子序列

300. 最长递增子序列&#xff08;中等&#xff09; 方法一&#xff1a;动态规划 思路 通常来说&#xff0c;子序列不要求连续&#xff0c;而子数组或子字符串必须连续&#xff1b;对于子序列问题&#xff0c;第一种动态规划方法是&#xff0c;定义 dp 数组&#xff0c;其中 dp[…

人工智能实践: 基于T-S 模型的模糊推理

模糊推理是一种基于行为的仿生推理方法, 主要用来解决带有模糊现象的复杂推理问题。由于模糊现象的普遍存在, 模糊推理系统被广泛的应用。模糊推理系统主要由模糊化、模糊规则库、模糊推理方法以及去模糊化组成, 其基本流程如图1所示。 ■ 图1 模糊推理流程图 传统的模糊推理是…

【C++】vector的介绍及使用

目录 一、vector的介绍二、vector的常用接口2.1 vector的定义2.2 vector iterator的使用2.3 vector 空间增长问题2.4 vector 增删查改2.4.1.尾插和尾删2.4.2.任意位置插入和删除以及查找2.4.3.vector 的交换与遍历 2.5 vector 迭代器失效问题 一、vector的介绍 vector是表示可…

RocketMQ不同的类型消息

目录 普通消息 可靠同步发送 可靠异步发送 单向发送 三种发送方式的对比 顺序消息 事物消息 两个概念 事务消息发送步骤 事务消息回查步骤 消息消费要注意的细节 RocketMQ支持两种消息模式: 普通消息 RocketMQ提供三种方式来发送普通消息&#xff1a;可靠同步发送、…

Android基于JNA集成调用第三方C/C++的so库

Android基于JNA集成调用第三方C/C的so库 &#xff08;1&#xff09;引入JNA。 基于JNA开源项目&#xff0c;JNA对Android NDK的封装&#xff0c;简化Android层JNI集成调用C/C的so库。 GitHub - java-native-access/jna: Java Native AccessJava Native Access. Contribute to…

简单总结:使用 gunicorn 进行 Python Web 应用部署

简单总结&#xff1a;使用 gunicorn 进行 Python Web 应用部署 Gunicorn (Green Unicorn) 是一个 Python Web 应用服务器&#xff0c;可以帮助我们轻松地部署 Python Web 应用程序。它是一个预先创建的 WSGI (Web Server Gateway Interface) 服务器&#xff0c;可以通过简单的…

代码随想录算法训练营第五十三天| 1143.最长公共子序列 、1035.不相交的线、53. 最大子序和 动态规划

文章目录 1143.最长公共子序列:star:1035.不相交的线53. 最大子序和 动态规划 1143.最长公共子序列⭐️ 题目链接&#xff1a;代码随想录 解题思路&#xff1a; 1.dp数组&#xff1a;长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp(i)[j]…

2023年的深度学习入门指南(12) - PEFT与LoRA

2023年的深度学习入门指南(12) - PEFT与LoRA 大家都知道&#xff0c;大模型的训练需要海量的算力。其实&#xff0c;即使是只对大模型做微调训练&#xff0c;也是需要大量的计算资源的。 有没有用更少的计算资源来进行微调的方法呢&#xff1f;研究者研发出了几种被Hugging F…