redis之:短信登录模块(session和redis)

目录

一、session短信登录

service层业务功能:

Interceptor拦截器

拦截器配置

二、redis短信登录

service层业务功能:

Interceptor拦截器

拦截器配置

三、总结


一、session短信登录

service层业务功能:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public Result sendCode(String phone,HttpSession httpSession) {
        //1.校验手机号格式
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机格式错误");
        }
        //2.生成验证码
        String code = RandomUtil.randomNumbers(6);
        //3.将手机号和验证码存入session中防止验证码比对成功后更改手机号从而登录别人账号的bug
        //存入手机号是
        httpSession.setAttribute("phone",phone);
        httpSession.setAttribute("code",code);
        //4.发送验证码
        log.debug("验证码为:"+code);
        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm,HttpSession httpSession) {
        //1.校验手机号格式
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机格式错误");
        }
        //2.校验验证码
        String cacheCode = httpSession.getAttribute("code").toString();
        String code = loginForm.getCode();
        if (!code.equals(cacheCode) || code==null) {
            return Result.fail("验证码错误");
        }
        //3.校验手机号前后是否一致
        String cachePhone = httpSession.getAttribute("phone").toString();
        if (!cachePhone.equals(phone) || phone==null) {
            return Result.fail("手机号错误");
        }
        //4.根据手机号从数据库查用户 select * from tb_user where phone=?
        User user = query().eq("phone", phone).one();
        //4.1判断用户是否存在,不存在创建用户并保存
        if (user == null) {
            user = new User();
            user.setPhone(phone);
            user.setNickName(RandomUtil.randomString(10));
            save(user);
        }
        //4.2将user转换成userDTO
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        //5.保存用户信息到session
        httpSession.setAttribute("user",userDTO);
        //不用返回用户信息coojie里有session数据
        return Result.ok();
    }
}

Interceptor拦截器

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取session
        HttpSession session = request.getSession();
        //2.获取存入session的用户
        UserDTO userDTO = (UserDTO)session.getAttribute("user");
        //3.判断session里的user是否为空
        if (userDTO == null) {
            //没有,需要拦截,设置状态码
            response.setStatus(401);
            //拦截
            return false;
        }
        //4.有用户,存入线程池放行
        UserHolder.saveUser(userDTO);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //释放线程池
        UserHolder.removeUser();
    }
}

拦截器配置

@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LoginInterceptor loginInterceptor = new LoginInterceptor();
        ArrayList list = new ArrayList();
        list.add("/shop/**");
        list.add("/vouchar/**");
        list.add("/shop-type/**");
        list.add("/upload/**");
        list.add("/blog/hot");
        list.add("/user/code");
        list.add("/user/login");

        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(list);
    }
}

二、redis短信登录

service层业务功能:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone) {
        //1.校验电话号码是否规范
        boolean phoneInvalid = RegexUtils.isPhoneInvalid(phone);

//        2.不符合返回错误信息
        if (phoneInvalid) {
            return Result.fail("手机格式错误");
        }

        //3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);  //hutool的生成随机数方法
//        Integer code = Captcha.generateCaptcha();  //自定义的随机数方法
//        String s = String.valueOf(code);
//        4.保存验证码到redis中,key为phone,一个phone对应一个验证码,这样后面不用判断输入前后电话号码是否一致
        //手机号加了login:code前缀,以便区分
        //加了过期时间
//        stringRedisTemplate.opsForValue().set("login:code:" + phone,code.toString(),2L, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);

//        5.发送验证码
        log.debug("验证码为:"+code);
        System.out.println(code);

        return Result.ok();
    }

    @Override
    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 (code == null || !code.equals(cacheCode)) {
            //不一致,报错
            return Result.fail("验证码错误");
        }

//        4.一致,根据手机号查询用户 select * from tb_user where phone=?
        User user = query().eq("phone", phone).one();

//        5.判断用户是否存在,不存在创建用户并保存
        if (user == null) {
            user = new User();
            user.setPhone(phone);
            user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
//            保存用户到数据库
            save(user);
        }

//        6.保存用户信息到reids中
//        6.1随机生成token,作为登录令牌
        //hutool的UUIDrandom里面的true是是否安全选项,toString里的true是是否去掉'-'
            String token = UUID.randomUUID(true).toString(true);
//        6.2将User对象转为HashMap存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        //注意:因为hset的key,feild,value是String类型,而userDto里面的id是Integer类型,所以要用lambda转成String
        Map<String, Object> map = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName,fieldValue) -> fieldValue.toString()));
//        6.3存储
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token,map);
//        6.4设置token有效期
        //注意:和session有效期30min不一样,redis无论操不操作30min直接清除,所以要刷新token有效期
        //怎么知道用户是活跃用户?或者说怎么知道用户访问我们资源?答:设置一级拦截器,拦截所有请求但都放行,只做token有效期刷新操作
        stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL,TimeUnit.MINUTES);

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

Interceptor拦截器

RefreshTokenInterceptor:一级拦截器,只作为token刷新作用

//一级拦截器,拦截所有,但都放行,只做token刷新操作
public class RefreshTokenInterceptor implements HandlerInterceptor {

//注:不能用@Autowired,因为拦截器是在spring容器初始化之前,注入了的值为null,可以通过构造器方式进行注入
    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        1.获取请求头中的token
        String token = request.getHeader("authorization ");
        //token判断不能用token==null,因为返回的是length
        if (StrUtil.isBlank(token)) {
            return true;
        }

//        2.基于token获取redis用户
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);

//        3.判断用户是否存在
        if (map.isEmpty()) {
            return true;
        }

//        4.将查询到的hash数据转为userDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(map,new UserDTO(),false);

//        5.保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);

//        6.刷新token有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);

        return true;
    }

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

LoginInterceptor:二级拦截器

//二级拦截器
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;
    }
}

拦截器配置

@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {

    //Configuration类,该类可以被spring容器管理
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LoginInterceptor loginInterceptor = new LoginInterceptor();
        ArrayList list = new ArrayList();
        list.add("/shop/**");
        list.add("/vouchar/**");
        list.add("/shop-type/**");
        list.add("/upload/**");
        list.add("/blog/hot");
        list.add("/user/code");
        list.add("/user/login");

        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
        registry.addInterceptor(loginInterceptor).excludePathPatterns(list).order(1);
    }
}

三、总结

session和redis关系

快速弄懂session和redis

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值