基于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生成token
String 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 {
//添加拦截器
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public 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 {
//有注册拦截器注入bean
private StringRedisTemplate stringRedisTemplate;
public ReFreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.获取token
String 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;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//避免造成内存泄露
UserHolder.removeUser();
}
}
public class LoginInterceptor implements HandlerInterceptor {
//有注册拦截器注入bean
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.只需要判断ThreadLocal中有没有用户信息
if(UserHolder.getUser()==null){
//响应未授权的状态信息
response.setStatus(401);
//拦截
return false;
}
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//避免造成内存泄露
UserHolder.removeUser();
}
}



















![[ Azure 云计算从业者 | AZ-900 ] Chapter 06 | 认识与了解 Azure 中相关的计算服务](https://img-blog.csdnimg.cn/a96a93b244d3474ea486758a2b64011b.png)