redis day1

发送验证码接口

1.Controller层

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    
    @Resource
    private IUserService userService;
    
    @PostMapping("/code")
    public Result sendCode(@RequestParam("phone") String phone) {
        return userService.sendCode(phone);
    }
}

2.Service层实现

复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public Result sendCode(String phone) {
        // 1. 验证手机号格式
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误!");
        }
        
        // 2. 生成6位随机验证码
        String code = RandomUtil.randomNumbers(6);
        
        // 3. 保存验证码到Redis(2分钟过期)
        String key = RedisConstants.LOGIN_CODE_KEY + phone; // "login:code:13800138000"
        stringRedisTemplate.opsForValue().set(key, code, 
            RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
        
        // 4. 模拟发送短信(实际项目调用短信服务)
        log.debug("发送短信验证码成功,验证码:{}", code);
        
        return Result.ok();
    }
}

登录接口

1.Controller层

复制代码
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm) {
    return userService.login(loginForm);
}

2.Service层实现

复制代码
@Override
public Result login(LoginFormDTO loginForm) {
    String phone = loginForm.getPhone();
    
    // 1. 验证手机号格式
    if (RegexUtils.isPhoneInvalid(phone)) {
        return Result.fail("手机号格式错误!");
    }
    
    // 2. 从Redis获取验证码
    String cacheKey = RedisConstants.LOGIN_CODE_KEY + phone;
    String cacheCode = stringRedisTemplate.opsForValue().get(cacheKey);
    String inputCode = loginForm.getCode();
    
    // 3. 验证码校验
    if (cacheCode == null || !cacheCode.equals(inputCode)) {
        return Result.fail("验证码错误");
    }
    
    // 4. 查询用户(MySQL)
    User user = query().eq("phone", phone).one();
    
    // 5. 用户不存在则创建
    if (user == null) {
        user = createUserWithPhone(phone);
    }
    
    // 6. 生成Token(UUID去掉连字符)
    String token = UUID.randomUUID().toString().replace("-", "");
    
    // 7. 转换为UserDTO(脱敏)
    UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
    
    // 8. 用户信息转为Map(Hash存储)
    Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
        CopyOptions.create()
            .setIgnoreNullValue(true)
            .setFieldValueEditor((fieldName, fieldValue) -> 
                fieldValue == null ? "" : fieldValue.toString()
            )
    );
    
    // 9. 保存到Redis(Hash结构,30分钟过期)
    String tokenKey = RedisConstants.LOGIN_USER_KEY + token; // "login:token:abc123"
    stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
    stringRedisTemplate.expire(tokenKey, 
        RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
    
    // 10. 删除已使用的验证码
    stringRedisTemplate.delete(cacheKey);
    
    // 11. 返回token给前端
    return Result.ok(token);
}

3.拦截器实现

复制代码
@Component
public class RefreshTokenInterceptor implements HandlerInterceptor {
    
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        
        // 1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true; // 没有token,放行(由LoginInterceptor处理)
        }
        
        // 2. 从Redis获取用户信息
        String key = RedisConstants.LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        
        // 3. 用户不存在,放行
        if (userMap.isEmpty()) {
            return true;
        }
        
        // 4. Hash转UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        
        // 5. 保存到ThreadLocal(线程隔离)
        UserHolder.saveUser(userDTO);
        
        // 6. 刷新Token过期时间(续期30分钟)
        stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, 
                                Object handler, Exception ex) throws Exception {
        // 请求结束后移除用户,防止内存泄漏
        UserHolder.removeUser();
    }
}

内存泄漏(Memory Leak) 是指程序在申请内存后,无法释放已不再使用的内存空间,导致可用内存逐渐减少,最终可能引发程序崩溃或系统性能下降。

LoginInterceptor
复制代码
@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        
        // 1. 从ThreadLocal获取用户
        UserDTO user = UserHolder.getUser();
        
        // 2. 用户不存在,返回401未授权
        if (user == null) {
            response.setStatus(401);
            return false;
        }
        
        // 3. 用户存在,放行
        return true;
    }
}

实现两个拦截器的原因

**第一个拦截器(RefreshTokenInterceptor)**的工作:

  1. 每次你访问网站,它都会检查你的"令牌"(token)

  2. 如果令牌有效,就把你的用户信息记下来

  3. 同时帮你延长令牌有效期(从30分钟重新开始计算)

  4. 它从不拒绝任何人访问

**第二个拦截器(LoginInterceptor)**的工作:

两个拦截器通过职责分离(一个只管刷新token,一个只管检查登录),实现了自动续期登录状态的同时精准控制接口权限,既保证用户体验(无感续期)又确保系统安全。

  1. 只在你访问需要登录的页面时才工作

  2. 检查第一个拦截器有没有记下你的信息

  3. 如果有信息,让你通过

  4. 如果没有信息,让你去登录

相关推荐
鱼鱼块4 分钟前
彻底搞懂 React useRef:从自动聚焦到非受控表单的完整指南
前端·react.js·面试
2501_946675646 分钟前
Flutter与OpenHarmony打卡轮播图组件
java·javascript·flutter
独自破碎E14 分钟前
Spring Boot 3.x和2.x版本相比有哪些区别与改进?
java·spring boot·后端
nwsuaf_huasir23 分钟前
积分旁瓣电平-matlab函数
前端·javascript·matlab
坚持学习前端日记23 分钟前
个人运营小网站的最佳策略
java·学习·程序人生·职场和发展·创业创新
幽络源小助理28 分钟前
SpringBoot+Vue美食网站系统源码 | Java餐饮项目免费下载 – 幽络源
java·vue.js·spring boot
k***921628 分钟前
C++:继承
java·数据库·c++
韭菜炒大葱32 分钟前
React Hooks :useRef、useState 与受控/非受控组件全解析
前端·react.js·前端框架
Cache技术分享36 分钟前
280. Java Stream API - Debugging Streams:如何调试 Java 流处理过程?
前端·后端
Coder_Boy_37 分钟前
基于SpringAI企业级智能教学考试平台考试模块全业务闭环方案
java·人工智能·spring boot·aiops