一、分布式Session

文章目录


学习 黑马点评项目整理总结: https://www.bilibili.com/video/BV1cr4y1671t/?vd_source=5f3396d3af2c3945929d54c786f289e5

官方命令文档:https://redis.io/commands/

分布式Session

流程为:用户先发送验证码,将验证码和发送验证码的ip存入redis。用户去登录会去校验用户提交的手机号和验证码,是否一致,如果一致,则根据手机号查询用户信息,不存在则新建,最后将用户数据保存到redis,并且生成token(token中携带用户信息)返回给用户,当我们校验用户是否登录时,会去携带着token进行访问,从token中解析出用户信息,拿用户id从redis中取出对应的value,判断是否存在这个数据,如果没有则拦截,如果存在则将其保存到threadLocal中,并且放行。
在这里插入图片描述

1.获取短信验证码

@PostMapping("/code")
public Result sendPhoneCode(@RequestParam("phone") String phone, HttpServletRequest request) {

    // 1.校验手机号(是否为空、是否合法)
    if (phone == null || "".equals(phone)) {
        return Result.fail("手机号格式错误!");
    }

    // 2.生成一个发送短信的随机数
    String code = (int) ((Math.random() * 9 + 1) * 100000) + "";
    // 3.调用发送短信的接口
    boolean sendSMS = smsService.sendSMS(phone, code);
//        boolean sendSMS = true;
    //短信发送成功
    if (sendSMS) {
        // 获取用户IP,防止用户在60s更换手机或者刷新应用重新发送
        String userIp = IpUtils.getIpAddr(request);
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("login:sms:limit:" + userIp, code, 60, TimeUnit.SECONDS);
        if (!flag) {
            return Result.fail("同一ip禁止重复发送手机验证码");
        }
        // 把生成的验证码放入到redis缓存中,用于后续的验证
        redisTemplate.opsForValue().set("login:sms:" + phone, code, 60, TimeUnit.SECONDS);
        // 发送短信成功
        return Result.ok();
    } else {
        return Result.fail("手机验证码发送失败");
    }
}

2.登录

@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm) {
    // 1.校验手机号
    String phone = loginForm.getPhone();
    if (RegexUtils.isPhoneInvalid(phone)) {
        return Result.fail("手机号格式错误!");
    }
    // 2.校验验证码
    String cacheCode = redisTemplate.opsForValue().get("login:sms:" + phone);
    if (cacheCode == null) {
        return Result.fail("验证码已失效!");
    }
    if (!cacheCode.equalsIgnoreCase(loginForm.getCode())) {
        return Result.fail("输入的验证码有误!");
    }

    // 3.数据库中根据手机号查询用户
    User user = userMapper.selectOne(
            new LambdaQueryWrapper<User>()
                    .eq(User::getPhone, phone)
    );

    // 4.判断用户是否存在
    if (user == null) {
        //不存在,则创建
        user = new User();
        user.setPhone(phone);
        userMapper.insert(user);
    }
    // 5.保存用户信息到redis中
    // 5.1: 随机生成token,作为登录令牌(token中包含用户id)
    String token = UUID.randomUUID().toString();
    // 5.2: 登录成功以后,把用户信息放入到缓存中,只要是为了后续业务的需要,登录有效期1小时
    redisTemplate.opsForValue().set("login:user:" + user.getId(), JSON.toJSONString(user), 60 * 60 * 1000L, TimeUnit.MINUTES);
    // 5.3: 而且短信码一定注册成功其实也没有任何用处,会占用redis内存空间,所有删除掉
    redisTemplate.delete("login:sms:" + phone);

    return Result.ok(token);
}

3.登录拦截器(做登录拦截及token续期)

public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate redisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = 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 false;
        }

        // 解析出token中的用户id
        String id = "1010";
        // 2.基于TOKEN获取redis中的用户
        String json = redisTemplate.opsForValue().get("login:user:" + id);
        // 3.判断用户是否存在
        if (json == null) {
            //没有用户信息表示未登录/登录过期
            return false;
        }
        // 5.将查询到的hash数据转为UserDTO
        User user = JSON.parseObject(json, User.class);
        // 6.存在,保存用户信息到 ThreadLocal
        UserHolder.saveUser(user);
        // 7.刷新token有效期
        redisTemplate.expire("login:user:" + id, 60 * 60 * 1000L, TimeUnit.MINUTES);
        // 8.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();
    }
}

4.webmvc配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // token刷新的拦截器
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).excludePathPatterns(
                "/user/code",
                "/user/login"
        ).order(0);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值