找回密码
 立即注册
首页 业界区 安全 登录认证-下篇:基于 Redis 实现共享session登录 ...

登录认证-下篇:基于 Redis 实现共享session登录

胥望雅 2025-10-1 16:47:26
将验证码 (session.setAttribute("code", code));用户信息 (session.setAttribute("user", userDTO))改为存入redis中
将随机生成的token作为登录凭证,放在请求头中的authorization字段
并设置两层拦截器,解决状态登录刷新的问题
1.png

业务流程图1
2.png

业务流程图2
3.png

具体实现:
UserServiceImpl
  1. package com.hmdp.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.bean.copier.CopyOptions;
  4. import cn.hutool.core.lang.UUID;
  5. import cn.hutool.core.util.RandomUtil;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import com.hmdp.dto.LoginFormDTO;
  8. import com.hmdp.dto.Result;
  9. import com.hmdp.dto.UserDTO;
  10. import com.hmdp.entity.User;
  11. import com.hmdp.mapper.UserMapper;
  12. import com.hmdp.service.IUserService;
  13. import com.hmdp.utils.RegexUtils;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.springframework.data.redis.core.StringRedisTemplate;
  16. import org.springframework.stereotype.Service;
  17. import javax.annotation.Resource;
  18. import javax.servlet.http.HttpSession;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. import java.util.concurrent.TimeUnit;
  22. import static com.hmdp.utils.RedisConstants.*;
  23. import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;
  24. /**
  25. * <p>
  26. * 服务实现类
  27. * </p>
  28. *
  29. * @author ztn
  30. * @since 2025-9-10
  31. */
  32. @Slf4j
  33. @Service
  34. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
  35.     @Resource
  36.     private StringRedisTemplate stringRedisTemplate;
  37.     @Override
  38.     public Result sendCode(String phone, HttpSession session) {
  39.         //1.校验手机号,isPhoneInvalid是无效
  40.         if(RegexUtils.isPhoneInvalid(phone)){
  41.             //2.如果不符合,返回错误信息
  42.             return Result.fail("手机号格式错误!");
  43.         }
  44.         //3.符合,生成验证码
  45.         String code = RandomUtil.randomNumbers(6);
  46.         //4.保存验证码到redis //set key value ex 120 两分钟有效期
  47. //        session.setAttribute("code",code);
  48. //        stringRedisTemplate.opsForValue().set("login:code" + phone,code,2, TimeUnit.MINUTES);
  49.         stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
  50.         //5.发送验证码
  51.         log.debug("发送短信验证码成功,验证码:{}",code);
  52.         //返回ok
  53.         return Result.ok();
  54.     }
  55.     @Override
  56.     public Result login(LoginFormDTO loginForm, HttpSession session) {
  57.         String phone = loginForm.getPhone();
  58.         //1.校验手机号,isPhoneInvalid是无效
  59.         if(RegexUtils.isPhoneInvalid(phone)){
  60.             //2.如果不符合,返回错误信息
  61.             return Result.fail("手机号格式错误!");
  62.         }
  63.         //3.从redis中获取并校验验证码
  64.         String cachecode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
  65.         String code = loginForm.getCode();
  66.         if(cachecode == null && !cachecode.equals(code)){
  67.             //4.不一致,报错
  68.             return Result.fail("验证码错误");
  69.         }
  70.         //4.一致,根据手机号查询用户select * from tb_user where phone = ?
  71.         User user = query().eq("phone", phone).one();
  72.         //5.判断用户是否存在
  73.         if(user == null){
  74.             //6.不存在,创建用户并保存
  75.             user = createUserWithphone(phone);
  76.         }
  77.         //7.保存用户信息到redis中
  78.         //7.1生成随机token,作为登录令牌
  79.         String token = UUID.randomUUID().toString(true);
  80.         //7.2将user对象转为HashMap存储
  81.         UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
  82.         Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
  83.                 CopyOptions.create()
  84.                         .setIgnoreNullValue(true)
  85.                         .setFieldValueEditor((fieldName,fieldValue) ->fieldValue.toString()));
  86.         //7.3存储
  87.         stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token,userMap);
  88.         //7.4有效期
  89.         stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
  90.         //8.返回token
  91.         return Result.ok(token);
  92.     }
  93.     private User createUserWithphone(String phone) {
  94.         //1.创建用户
  95.         User user = new User();
  96.         user.setPhone(phone);
  97.         user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
  98.         //2.保存用户
  99.         save(user);
  100.         return user;
  101.     }
  102. }
复制代码
RefreshTokenInterceptor
  1. package com.hmdp.utils;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.util.StrUtil;
  4. import com.hmdp.dto.UserDTO;
  5. import org.springframework.data.redis.core.StringRedisTemplate;
  6. import org.springframework.lang.Nullable;
  7. import org.springframework.web.servlet.HandlerInterceptor;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.util.Map;
  11. import java.util.concurrent.TimeUnit;
  12. import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;
  13. public class RefreshTokenInterceptor implements HandlerInterceptor {
  14.     private StringRedisTemplate stringRedisTemplate;
  15.     public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
  16.         this.stringRedisTemplate = stringRedisTemplate;
  17.     }
  18.     @Override
  19.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  20.         //1.获取请求头中的token
  21.         String token = request.getHeader("authorization");
  22.         if(StrUtil.isBlank(token)){
  23.             return true;
  24.         }
  25.         //2.获取redis中的用户
  26.         Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
  27.                 .entries(RedisConstants.LOGIN_USER_KEY + token);
  28.         //3.判断用户是否存在
  29.         if (userMap.isEmpty()) {
  30.             return true;
  31.         }
  32.         //5.将查询到的Hash数据转为userdto对象
  33.         UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
  34.         //6.存在,保存用户信息到ThreadLocal
  35.         UserHolder.saveUser(userDTO);
  36.         //7.刷新token有效期
  37.         stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
  38.         //8.放行
  39.         return true;
  40.     }
  41.     @Override
  42.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
  43.         //移除用户
  44.         UserHolder.removeUser();
  45.     }
  46. }
复制代码
LoginInterceptor
  1. package com.hmdp.utils;
  2. import org.springframework.web.servlet.HandlerInterceptor;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. public class LoginInterceptor implements HandlerInterceptor {
  6.     @Override
  7.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  8.        //判断是否要拦截(ThreadLocal中是否有用户)
  9.         if(UserHolder.getUser() == null){
  10.             response.setStatus(401);
  11.             return false;
  12.         }
  13.         return true;
  14.     }
  15. }
复制代码
MvcConfig
  1. package com.hmdp.config;
  2. import com.hmdp.utils.LoginInterceptor;
  3. import com.hmdp.utils.RefreshTokenInterceptor;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.redis.core.StringRedisTemplate;
  6. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. import javax.annotation.Resource;
  9. @Configuration
  10. public class MvcConfig implements WebMvcConfigurer {
  11.     @Resource
  12.     private StringRedisTemplate stringRedisTemplate;
  13.     @Override
  14.     public void addInterceptors(InterceptorRegistry registry) {
  15.         registry.addInterceptor(new LoginInterceptor())
  16.                 .excludePathPatterns(//放行
  17.                         "/user/code",
  18.                         "/user/login",
  19.                         "/blog/hot",
  20.                         "/shop/**",
  21.                         "/upload/**",
  22.                         "/shop-type/**",
  23.                         "/voucher/**"
  24.                 ).order(1);
  25.         registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
  26.     }
  27. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册