Redis实战——短信登录(二)

这篇具有很好参考价值的文章主要介绍了Redis实战——短信登录(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Redis代替session

  • redis中设计key
    • 在使用session时,每个用户都会有自己的session,这样虽然验证码的键都是“code”,但是相互不影响,从而确保每个用户获取到的验证码只能够自己使用,当使用redis时,redis的key是共享的,不分用户,就要求在redis中存储验证码时,不能直接将验证码的键设置为"code",这样无法保证其唯一性。
  • redis中设计value
    • 到底该使用redis中什么数据类型存储数据,主要需要看数据样式和使用方式,一般会考虑使用String、Hash,String存储时,会多占一点内存空间,则相对来说Hash存储时,会少占用一点内存空间。
      • String结构:以Json字符串来存储,比较直观
      • Hash结构:,每个对象中每个字段独立存储,可以针对单个字段做CRUD
redis实现登录
发送验证码

Redis实战——短信登录(二),java

  • 添加redis

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
  • 设置redis的连接信息

    spring:
      redis:
        host: 192.168.175.128
        port: 6379
        password: liang
        lettuce:
          pool:
            max-active: 10
            max-idle: 10
            min-idle: 1
            time-between-eviction-runs: 10s
    
  • 增加相关常量

    /**
     * 保存验证码的redis中的key
     */
    public static final String LOGIN_CODE_KEY = "login:code:";
    /**
     * 验证码的过期时间
     */
    public static final Long LOGIN_CODE_TTL = 2L;
    
  • 修改Service层

       @Autowired
        StringRedisTemplate stringRedisTemplate;
    
        @Override
        public boolean sendCode(String phone, HttpSession session) {
            //获取手机号,验证手机号是否合规
            boolean mobile = PhoneUtil.isMobile(phone);
            //不合规,则提示
            if (!mobile){
                return false;
            }
            //生成验证码
            String code = RandomUtil.randomNumbers(6);
            //保存验证码到redis,并设置过期时间
            stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
            //发送验证码,这里就通过打印验证码模拟了下发送验证码
            System.out.println("验证码:" + code);
            return true;
        }
    
  • 修改Controller层

    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
        String uuid = userService.sendCode(phone, session);
        return uuid.equals("") ? Result.fail("手机号码不合规"): Result.ok(uuid);
    }
    
验证码登录、注册

Redis实战——短信登录(二),java

  • 增加相关常量

    public static final String LOGIN_USER_KEY = "login:token:";
    public static final Long LOGIN_USER_TTL = 30L;
    
  • 修改Service层

    @Override
    public String login(LoginFormDTO loginForm, HttpSession session) {
        //获取手机号
        String phone = loginForm.getPhone();
        //验证手机号是否合理
        boolean mobile = PhoneUtil.isMobile(phone);
        //如果不合理 提示
        if (!mobile){
            //提示用户手机号不合理
            return "";
        }
        //手机号合理 进行验证码验证
        String code = loginForm.getCode();
        //从redis中获取验证码
        String redisCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        //如果验证码输入的是错误的  提示
        if (!code.equals(redisCode)){
            return "";
        }
        //如果验证码也正确 那么通过手机号进行查询
        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        // 数据库中没查询到用户信息
        if (ObjectUtil.isNull(user)){
            user = new User();
            user.setPhone(phone);
            user.setNickName("user_"+ RandomUtil.randomString(10));
            this.save(user);
        }
        // 将用户信息保存到Redis中,注意避免保存用户敏感信息
        UserDTO userDTO = BeanUtil.toBean(user, UserDTO.class);
        // 设置UUID保存用户信息
        String uuid = IdUtil.fastSimpleUUID();
        // 将user对象转化为Map,同时将Map中的值存储为String类型的
        Map<String, Object> userDTOMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().ignoreNullValue()
                        .setFieldValueEditor((key, value) -> value.toString()));
        stringRedisTemplate.opsForHash().putAll( LOGIN_USER_KEY + uuid, userDTOMap);
        //设置过期时间
        stringRedisTemplate.expire(LOGIN_USER_KEY + uuid, LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 通过UUID生成简单的token
        String token = uuid + userDTO.getId();
        return token;
    }    
    
    String login(LoginFormDTO loginForm, HttpSession session);
    
  • 修改Controller层

    @PostMapping("/login")
    public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        String token = userService.login(loginForm, session);
        return StrUtil.isNotBlank(token) ? Result.ok(token) : Result.fail("手机号或验证码错误");
    }
    
校验登录状态

Redis实战——短信登录(二),java

  • 修改LoginInterceptor拦截器

    private StringRedisTemplate stringRedisTemplate;
    
    /**
     * 构造函数
     * @param stringRedisTemplate
     */
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    
    /**
     * preHandle方法的返回值决定是否放行,该方法在控制层方法执行前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //从请求头中获取token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)){
            return false;
        }
    
        String uuid = token.substring(0,token.lastIndexOf("-"));
        System.out.println(uuid);
        //从redis中获取值
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + uuid);
        if (ObjectUtil.isNull(entries)){
            return false;
        }
        //将map转化为UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(entries, new UserDTO(), true);
        //将用户信息保存到 ThreadLocal
        UserHolder.saveUser(userDTO);
        return true;
    }
    
    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
    
        @Resource
        StringRedisTemplate stringRedisTemplate;
    
        /**
         * 添加拦截器
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //添加拦截器
            registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                    //放行资源
                    .excludePathPatterns(
                            "/shop/**",
                            "/voucher/**",
                            "/shop-type/**",
                            "/upload/**",
                            "/blog/hot",
                            "/user/code",
                            "/user/login"
                    )
                    // 设置拦截器优先级
                    .order(1);
        }
    }
    
登录状态的刷新问题
  • 因为设置了redis中存储的用户的有效期,所以在用户访问界面的时,需要更新token令牌的存活时间,例如修改LoginInterceptor拦截器,在此拦截器中刷新过期时间

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //从请求头中获取token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)){
            return false;
        }
    
        String uuid = token.substring(0,token.lastIndexOf("-"));
        System.out.println(uuid);
        //从redis中获取值
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + uuid);
        if (ObjectUtil.isNull(entries)){
            return false;
        }
        //将map转化为UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(entries, new UserDTO(), true);
        //将用户信息保存到 ThreadLocal
        UserHolder.saveUser(userDTO);
        //刷新token有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY + uuid, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }
    
  • 但是需要注意的是,自定义的登录拦截器只是针对需要登录访问的请求进行了拦截,如果用户访问没被拦截的请求,该拦截器不会生效,则token令牌不能进行更新,当用户长时间访问不需要登录的页面,token令牌失效,再去访问被拦截的请求,则需要重新登录,这是不合理的。所有我们还需要在定义一个拦截器,进行token令牌刷新。

Redis实战——短信登录(二),java

  • 刷新令牌的Interceptor

    /**
     * 刷新令牌的拦截器
     * @author liang
     */
    public class RefreshTokenInterceptor implements HandlerInterceptor {
    
        private StringRedisTemplate redisTemplate;
    
        public RefreshTokenInterceptor(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            //从请求头中获取token
            String token = request.getHeader("authorization");
            if (StrUtil.isBlank(token)){
                return false;
            }
            String uuid = token.substring(0, token.lastIndexOf("-"));
            //从Redis中获取值
            Map<Object, Object> userMap = redisTemplate.opsForHash().entries(LOGIN_USER_KEY + uuid);
            if (ObjectUtil.isNull(userMap)){
                return false;
            }
            //将map转换为UserDTO对象
            UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
            //将用户信息保存到 ThreadLocal
            UserHolder.saveUser(userDTO);
            //刷新token有效期
            redisTemplate.expire(LOGIN_USER_KEY + uuid, LOGIN_USER_TTL, TimeUnit.MINUTES);
            return true;
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            UserHolder.removeUser();
        }
    }
    
  • 修改登录的Interceptor

    public class LoginInterceptor implements HandlerInterceptor {
    
        /**
         * preHandle方法的返回值决定是否放行,该方法在控制层方法执行前执行
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            UserDTO user = UserHolder.getUser();
            return ObjectUtil.isNotNull(user);
        }
    }    
    
  • 修改WebMvcConfigurer配置类

    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
    
        @Resource
        StringRedisTemplate stringRedisTemplate;
    
        /**
         * 添加拦截器
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //添加拦截器
            registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate));
            registry.addInterceptor(new LoginInterceptor())
                    //放行资源
                    .excludePathPatterns(
                            "/shop/**",
                            "/voucher/**",
                            "/shop-type/**",
                            "/upload/**",
                            "/blog/hot",
                            "/user/code",
                            "/user/login"
                    )
                    // 设置拦截器优先级
                    .order(1);
        }
    }
    

本文由mdnice多平台发布文章来源地址https://www.toymoban.com/news/detail-530216.html

到了这里,关于Redis实战——短信登录(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Redis】2、Redis应用之【根据 Session 和 Redis 进行登录校验和发送短信验证码】

    🌼 文章基于 B 站黑马程序员视频教程编写 🌼 做笔记便于日后复习 ① 手机号格式后端校验 手机号校验的正则表达式 校验工具类: ② 生成短信验证码 🌼 hutool 工具的详细使用: https://doc.hutool.cn/pages/index/ 🌿 根据 Cookie 中的 JSESSIONID 获取到 Session 🌿 然后从 Session 中获取到

    2024年02月11日
    浏览(58)
  • 腾讯云短信服务实现 Java 发送手机验证码(SpringBoot+Redis 实现)

    前置:需要腾讯云的账号,后期授权需要,不需要买云服务器,有需要的可以购买短信套餐(几块钱) 搜索框输入短信,可以买一个短信套餐包,便宜不贵,进入短信服务的控制台 发送短信有频率限制,企业用户可以修改设置 之后我们需要对短信内容进行设置      类型有网站

    2024年02月09日
    浏览(48)
  • django-短信登录

       2.2.1视图函数 sms_login函数是短信登录的逻辑。 sms_send函数是获取点击短信验证码,校验我表单中手机号是否填的正确,后调用第三方的sdk(腾讯云或者阿里云)来发送短信,获取了短信在Redis中缓存等待用户输入校验。  2.2.2 froms组件

    2024年04月15日
    浏览(28)
  • 若依RuoYi整合短信验证码登录

    背景:若依默认使用账号密码进行登录,但是咱们客户需要增加一个短信登录功能,即在不更改原有账号密码登录的基础上,整合 短信验证码登录 。 本案例基于RuoYi-Vue版本实现,其他版本应该大同小异。 一、自定义短信登录 token 验证 仿照 UsernamePasswordAuthenticationToken 类,编

    2023年04月16日
    浏览(52)
  • 【若依RuoYi短信验证码登录】汇总

    遇到一个场景,需要同时支持手机号或者邮箱和密码或者验证码进行登录的场景,故来记录一下。 说明:此流程主要是基于若依框架集成的多种方式登录,主要演示登录业务逻辑和前端登录密码和验证码切换组件和配置Security 一:后端登录业务逻辑代码: 因为有多个端,多个语

    2024年02月11日
    浏览(41)
  • SpringBoot——短信发送、手机验证码登录

    目录 一、短信发送 1.1 阿里云短信服务 1.1.1 设置短信签名 1.1.2 模板管理 1.1.3 设置AccessKey 1.2 短信发送——代码开发 1.2.1 导入maven坐标 1.2.2 调用API 1.2  手机验证码登录 1.2.1 用户数据库表 1.2.2  修改过滤器 1.2.3   随机生成验证码的工具类 1.2.4 手机验证码登录-- 发送验证码

    2023年04月22日
    浏览(43)
  • spring boot3登录开发-3(2短信验证登录/注册逻辑实现)

      ⛰️个人主页:     蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途 目录 写在前面 上文衔接 内容简介 功能分析 短信验证登录实现 1.创建交互对象 用户短信登录/注册DTO 创建用户登录VO 2.创建自定义业务异常 创建验证码错误异常 创建用户被封禁异

    2024年04月09日
    浏览(44)
  • uni-app 之 短信验证码登录

    uni-app 之 短信验证码登录 image.png image.png

    2024年02月07日
    浏览(52)
  • TP6----------阿里云短信包验证码登录

    首先开通阿里云短信包,之后申请短信包签名,这里大家自行去阿里云申请 安装阿里云sdk 首先创建Sample类方便调用,我们需要有短信签名,签名模板,阿里云keyId和accessKeySecret 阿里云key在阿里云首页,右上角有个acesskey管理就可以看到 Sample.php文件 业务逻辑层生成随机验证码

    2024年02月15日
    浏览(67)
  • Java Web 安全实战:从登录到退出

    在当今互联网时代,用户信息安全至关重要。在Java Web开发中,Spring Security是一个强大且灵活的身份验证和访问控制框架,它可以帮助我们构建安全可靠的应用程序。本文将介绍如何使用Spring Security实现一个安全的Java Web应用,涵盖登录、记住我、授权、退出登录、验证码、

    2024年01月16日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包