基于Redis进行登录校验

本文从前端讲解数据流转过程

【前端如下】:首先输入手机号并点击发送验证码,将手机号数据进行提交到服务器

【服务器】如下:客户端 发送的请求和携带的数据【手机号】12345678910。

【第一步】首先会被拦截器拦截,这里配置了两个拦截器,根据拦截器拦截内容,第一个拦截器会进行释放,第二个拦截器由于拦截范围原因也会进行释放。

 【第二步】数据【手机号】12345678910,会流转到验证码发送业务。

  • 首先判断手机号是否符合要求
  • 生成验证码
  • Redis进行全局数据缓存,将验证码信息缓冲到Redis当中 
@Override
    public Result sendCode(String phone, HttpSession session) {
        // 1. 验证手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 如果不符合要求直接返回
            return Result.fail("手机号不正确");
        }
        // 2.生成验证码
        String code = RandomUtil.randomNumbers(6);
        // 3.保存验证码到Redis中--验证码设置过期时间为60s
        template.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

        // 4.发送验证码
        log.debug("验证码是:{}", code);
        return Result.ok();
    }

【第三步】用户点击登录按钮,数据【手机号+验证码】都会流转到登录业务

  • 首先进行手机号和 验证码的校验。
  • 如果校验成功判断是否是新用户
  • 如果是新用户,则将信息添加到 数据库中
  • 然后将用户信息存入Redis,采用的是Hash结构,注意设置过期时间和数据类型的转换。
  • 将生成的TokenKey返回给前端。
@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1. 验证手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误");
        }
        // 2. 从Redis中获取验证码
        String code = template.opsForValue().get(LOGIN_CODE_KEY + phone);
        if (code == null || !code.equals(loginForm.getCode())) {
            return Result.fail("验证码错误");
        }
        // 3.根据手机号查询用户信息
        User user = query().eq("phone", phone).one();
        // 4. 用户不存在则创建用户到数据库
        if (user == null) {
            user = CreateNewUser(phone);
        }
        // 5.保存用户到Redis
        String token = UUID.randomUUID(false).toString();
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        // 设置Long类型的数据转换为字符串类型的。
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).
                        setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
        String tokenKey = LOGIN_USER_KEY + token;

        template.opsForHash().putAll(tokenKey,userMap);
        template.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);

        // 6.返回Token
        return Result.ok(token);
    }

    private User CreateNewUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(6));
        save(user);
        return user;
    }

【第四步】登录状态校验

首先数据会被我们之前提到的拦截器进行拦截

  • 获取上一步存入Redis中用户的信息,
  • 将信息转为DTO对象
  • 将对象保存在ThreadLocal中,保证线程安全,
  • 然后对Redis数据的有效期进行刷新

第一个拦截器代码如下:

package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;

public class RefleshInterceptor implements HandlerInterceptor {

    private StringRedisTemplate template;

    public RefleshInterceptor(StringRedisTemplate template) {
        this.template = template;
    }

    @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;
        }
        String tokenKey = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = template.opsForHash().entries(tokenKey);
        // 2.判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        // 3. 获取用户并转换为UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //4.存在保存到ThreadLocal
        UserHolder.saveUser(userDTO);
        // 5. 刷新token的有效期
        template.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        UserHolder.removeUser();
    }
}

【第五步】

从第一个拦截器中出来以后,会进入第二个拦截器

  • 检查是否有用户信息
  • 如果不存在则直接拦截
  • 存在则放行

至此所有登录相关校验执行关闭。

package com.hmdp.utils;


import com.hmdp.dto.UserDTO;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 查询ThreadLocal
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            // 不存在拦截
            response.setStatus(401);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        UserHolder.removeUser();
    }
}

【完整流程图如下】:首先进行短信验证码登录,注册--->校验登录状态。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兜兜转转m

一毛钱助力博主实现愿望

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值