长沙网站建设大全,长沙数字引擎信息技术有限公司,自己做一个网站一年的费用,制作宣传册的步骤文章目录 传统session缺点整体访问流程代码实现生成验证码登录 问题具体思路 传统session缺点
传统单体项目一般是把session存入tomcat#xff0c;但是每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat#xff0c;并且把自己的信息存放到第一台服务器… 文章目录 传统session缺点整体访问流程代码实现生成验证码登录 问题具体思路 传统session缺点
传统单体项目一般是把session存入tomcat但是每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat并且把自己的信息存放到第一台服务器的session中但是第二次这个用户访问到了第二台tomcat那么在第二台服务器上肯定没有第一台服务器存放的session所以此时 整个登录拦截功能就会出现问题。为了解决这个问题我们用redis作为中间件把用户的信息存入redis中这样不同tomcat在检验用户是否登陆时都向redis请求数据这样就可以在不同的tomcat进行session共享。
整体访问流程
用户去登录会去校验用户提交的手机号和验证码是否一致如果一致则根据手机号查询用户信息不存在则新建最后将用户数据保存到redis并且生成token作为redis的key当我们校验用户是否登录时会去携带着token进行访问从redis中取出token对应的value判断是否存在这个数据如果没有则拦截如果存在则将其保存到threadLocal中并且放行。
代码实现
生成验证码
生成验证码并把生成的验证码存入redis的代码。 public Result sendCode(String phone) {// 1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合返回错误信息return Result.fail(手机号格式错误);}// 3.符合生成验证码String code RandomUtil.randomNumbers(6);// 4.保存验证码到 sessionstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);// 5.发送验证码log.debug(发送短信验证码成功验证码{}, code);// 返回okreturn Result.ok();}登录
登陆的功能是前端传来用户的手机号和验证码如果手机号和验证码都正确就判断用户是否存在存在就登陆不存在就创建用户然后登陆如果手机号或者验证码其中一个不正确就返回错误。 public Result login(LoginFormDTO loginForm) {// 1.校验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合返回错误信息return Result.fail(手机号格式错误);}// 3.从redis获取验证码并校验String cacheCode stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY phone);String code loginForm.getCode();if (cacheCode null || !cacheCode.equals(code)) {// 不一致报错return Result.fail(验证码错误);}// 4.一致根据手机号查询用户 select * from tb_user where phone ?User user query().eq(phone, phone).one();// 5.判断用户是否存在if (user null) {// 6.不存在创建新用户并保存user createUserWithPhone(phone);}// 7.保存用户信息到 redis中// 7.1.随机生成token作为登录令牌String token UUID.randomUUID().toString(true);// 7.2.将User对象转为HashMap存储UserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);MapString, Object userMap BeanUtil.beanToMap(userDTO, new HashMap(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString()));// 7.3.存储String tokenKey LOGIN_USER_KEY token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);// 7.4.设置token有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.返回tokenreturn Result.ok(token);}问题
上面的代码仍然存在问题,就是session刷新问题原本利用HttpSession每次访问就刷新session维持时间就会刷新然而我们上面的代码是利用redis作为中间件共享session不能自动刷新为了解决这个问题我们需要自己设置拦截器进行手动刷新。
具体思路
首先我们要配置一个拦截器它把他的优先设为最高他拦截一切路径然后它的方法内每次拦截的时候都会检查redis中是否有session如果有就刷新session这样就可以维持session维持的时间。 设置拦截器
public class RefreshTokenInterceptor implements HandlerInterceptor {private 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 (StrUtil.isBlank(token)) {return true;}// 2.基于TOKEN获取redis中的用户String key LOGIN_USER_KEY token;MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);// 3.判断用户是否存在if (userMap.isEmpty()) {return true;}// 5.将查询到的hash数据转为UserDTOUserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6.存在保存用户信息到 ThreadLocalUserHolder.saveUser(userDTO);// 7.刷新token有效期stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserHolder.removeUser();}
}添加拦截器
Configuration
public class MvcConfig implements WebMvcConfigurer {Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login
// 值越小优先级越高。).order(1);// token刷新的拦截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns(/**).order(0);}
}