返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >基于Redis实现短信验证码登录项目示例(附源码)
  • 281
分享到

基于Redis实现短信验证码登录项目示例(附源码)

2024-04-02 19:04:59 281人浏览 八月长安
摘要

目录Redis短信登录流程描述短信验证码的发送短信验证码的验证是否登录的验证源码分析模拟发送短信验证码短信验证码的验证校验是否登录登录验证优化Redis短信登录流程描述 短信验证码的

Redis短信登录流程描述

短信验证码的发送

用户提交手机号,系统验证手机号是否有效,毕竟无效手机号会消耗你的短信验证次数还会导致系统的性能下降。如果手机号为无效的话就让用户重新提交手机号,如果有效就生成验证码并将该验证码作为value保存到redis中对应的key是手机号,之所以这么做的原因是保证key的唯一性,如果使用固定字符串作为可以的话会被后面的数据所覆盖。然后在控制台输出验证码模拟发送验证码的过程

短信验证码的验证

用户的手机号接收到验证码后在平台上提交验证码,系统从redis中根据手机号读取验证码并进行校验,如果验证通过的话就根据用户验证使用的手机号去数据库中进行查询用户信息。如果存在就将查询到的用户信息保存到redis中,完成登录;如果不存在的话就创建一个新用户,并将该用户的信息分别保存到sql数据库和redis中,生成随机token作为key、使用hash结构存储user数据作为value,并将这个token返回给客户端,至此完成登录注册

是否登录的验证

用户访问系统业务逻辑的时候需要校验他是否已经登录,如果登录可以访问否则就去登录,那么该如何完成是否登录的校验呢?这就要了解session的相关知识了,每一个session都有一个sessionId信息保存在浏览器的cookie中,当用户使用浏览器发送请求的时候会携带上cookie信息,此时系统就可以使用cookie中的sessionId获取到session信息,并通过session获取到登录时存储的用户信息。如果此时用户在数据库中存在的话就将该用户的信息缓存在ThreadLocal(方便后续验证)中,并放行该访问;否则就说明发送请求的用户未登录或不合法,就要拦截到他的请求前往登录

源码分析

模拟发送短信验证码

UserController定义与前端交互

@Resource
private IUserService userService;


@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, httpsession session) {
    // 发送短信验证码并保存验证码
    return userService.sendCode(phone, session);
}

上面使用到了sendCode方法,在userService里定义一下接口,然后在对应实现类中按照上面的流程重写该方法的业务逻辑代码

@Override
public Result sendCode(String phone, HttpSession session) {
    // 校验手机号
    if (RegexUtils.isPhoneInvalid(phone)) {
        // 无效手机号,返回错误信息
        return Result.fail("手机号格式有误!");
    }
    // 有效生成验证码
    String code = RandomUtil.randomNumbers(6);
    // 保存 (固定前缀+手机号) 和验证码到Redis中,设置验证码的有效期为2分钟
    // RedisConstants.LOGIN_CODE_KEY = “login:code:”
    // RedisConstants.LOGIN_CODE_TTL = 2L
    stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);
    // 模拟发送验证码
    log.debug("验证码:{}", code);
    // 返回
    return Result.ok();
}

手机号格式校验使用到的RegexUtils类中的工具方法


public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";


public static boolean isPhoneInvalid(String phone){
    return mismatch(phone, RegexPatterns.PHONE_REGEX);
}

// 校验是否不符合正则格式
private static boolean mismatch(String str, String regex){
    if (StrUtil.isBlank(str)) {
        return true;
    }
    return !str.matches(regex);
}

短信验证码的验证

UserController定义与前端交互,其中参数LoginFORMDTO 是前端使用手机号+验证码登录或者手机号+密码登录是传递过来的JSON数据


@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
    // 实现登录功能
    return userService.login(loginForm, session);
}

上面使用到了login方法,在userService里定义一下接口,然后在对应实现类中按照上卖弄的流程描述重写该方法的业务逻辑代码

@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
    String phone = loginForm.getPhone();

    // 验证码校验
    String code = loginForm.getCode();
    String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
    if (cacheCode == null || !code.equals(cacheCode)) {
        return Result.fail("验证码错误!");
    }

    // 根据手机号查询用户信息
    User user = query().eq("phone", phone).one();
    if (user == null) {
        // 不存在就创建一个新用户
        user = createUserWithPhone(phone);
    }

    // 保存用户信息到redis中
    // 生成随机token
    String token = UUID.randomUUID().toString(true);
    // user先转userDTO再转HashMap存储  转HashMap时的第三个参数的意思是忽略null值将值都转换成String类型
    UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
    Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
            CopyOptions.create()
                    .setIgnoreNullValue(true)
                    .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
    // RedisConstants.LOGIN_USER_KEY = "login:token:"
    stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, userMap);
    // 设置失效时间为30分钟
    // RedisConstants.LOGIN_USER_TTL = 30L
    stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
    // 返回前端token
    return Result.ok(token);
}

private User createUserWithPhone(String phone) {
    User user = new User();
    user.setPhone(phone);
    // SystemConstants.USER_NICK_NAME_PREFIX = "user_"
    user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
    save(user);
    return user;
}

保存的时候使用BeanUtil将User转换成UserDTO进行存储,UserDTO的结构如下,只保存一部分的数据,一方面可以不用来回传递用户有关的隐私数据,一方面也节省内存提高性能。由于这里的id是数值类型,但是stringRedisTemplate存储时需要hash的键值都是String型,所以说应该在存储之前将id的值转换成String类型,就在上面代码块的24~27行完成了这个操作

@Data
public class UserDTO {
    private Long id;
    private String nickName;
    private String icon;
}

校验是否登录

用户发送请求不止一次,所以说登录验证也不止进行一次,于是可以使用拦截器完成验证,拦截器的使用可分为两步:

创建拦截器


@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求头中的token信息
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            // token为空,返回401未授权状态码,拦截
            response.setStatus(401);
            return false;
        }
        // 根据token获取redis中的用户value
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        HttpSession session = request.getSession();

        // 判断用户是否存在
        if (userMap.isEmpty()) {
            // 用户不存在,返回401未授权状态码,拦截
            response.setStatus(401);
            return false;
        }

        // 用户存在,将hash数据转换为userDTO,存信息到ThreadLocal
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        UserHolder.saveUser(userDTO);

        // 刷新token有效期,放行
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

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

注册拦截器


@Configuration
public class mvcConfig implements WEBMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorReGIStry registry) {
        registry.addInterceptor(loginInterceptor)
                .excludePathPatterns(
                        "/shop
@Component
public class RefreshTokenInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求头中的token信息
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            // token为空 直接放行
            return true;
        }

        // 根据token获取redis中的用户value
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        HttpSession session = request.getSession();
        // 判断用户是否存在
        if (userMap.isEmpty()) {
            // 用户不存在 直接放行
            return true;
        }

        // 用户存在,将hash数据转换为userDTO,存信息到ThreadLocal
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        UserHolder.saveUser(userDTO);

        // 刷新token有效期,放行
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

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

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断登录
        if (UserHolder.getUser() == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

创建完拦截器之后要将两个拦截器通过配置类配置到容器中生效,多个拦截器的优先级,默认按照添加顺序执行优先级,但是也可以使用order方法指定优先级,按参数的大小排序优先级,参数越小优先级越高


@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private RefreshTokenInterceptor refreshTokenInterceptor;
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 前置拦截器
        registry.addInterceptor(refreshTokenInterceptor)
                .addPathPatterns("/**")
                .order(0);
        // 后置拦截器
        registry.addInterceptor(loginInterceptor)
                .excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                )
                .order(1);
    }
}

到此这篇关于基于Redis实现短信验证码登录项目示例(附源码)的文章就介绍到这了,更多相关Redis 短信验证码登录内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 基于Redis实现短信验证码登录项目示例(附源码)

本文链接: https://lsjlt.com/news/149208.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 基于Redis实现短信验证码登录项目示例(附源码)
    目录Redis短信登录流程描述短信验证码的发送短信验证码的验证是否登录的验证源码分析模拟发送短信验证码短信验证码的验证校验是否登录登录验证优化Redis短信登录流程描述 短信验证码的...
    99+
    2024-04-02
  • Redis实现短信验证码登录的示例代码
    目录效果图pom.xmlapplicatoin.ymlRedis配置类controllerserviceImplmapper效果图 发送验证码 输入手机号、密码以及验证码完成登录操作 pom.xml 核心依赖 <...
    99+
    2022-06-13
    Redis短信验证码登录 Redis验证码登录 Redis短信验证码
  • SSM项目实现短信验证码登录功能的示例代码
    目录1.登入网站 zz短信平台2.导入工具类MessageUtil3.ajax 模块4. html页面5.编写controller层1.登入网站 zz短信平台 http:/...
    99+
    2024-04-02
  • Java实现短信验证码的示例代码
    目录项目需求需求来由代码实现发送验证码方法注册方法忘记密码前端代码编码中遇到的问题如何改进短信验证码相信大家都不陌生吗,但是短信验证码怎么生成的你真的了解吗,本文揭示本人项目中对短信...
    99+
    2024-04-02
  • vue实现通过手机号发送短信验证码登录的示例代码
    本文主要介绍了vue实现通过手机号发送短信验证码登录的示例代码,分享给大家,具体如下: <template> <div class="get-mobile...
    99+
    2024-04-02
  • java如何实现短信验证码登录功能
    小编给大家分享一下java如何实现短信验证码登录功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!业务案例如下所示,是一个大家熟知的采用短信登录的入口输入手机号之...
    99+
    2023-06-25
  • Java简单实现短信验证登录(Session、Redis)
    前端设计 {{codeBtnMsg}} 未注册...
    99+
    2023-09-27
    java redis 前端
  • 瑞吉外卖项目:短信服务与手机验证码登录
    一. 短信发送 现在有很多第三方提供的短信服务,这些短信服务会与短信运营商所对接,我们只需按照提供的接口文档进行开发调用就可以发送短信服务,这个过程一般是需要收费的。 常用的第三方短信服务: 阿里云,华为云,腾讯云,京东,梦网,乐信等。...
    99+
    2023-09-30
    服务器 运维
  • 基于 antd pro 的短信验证码登录功能(流程分析)
    目录概要整体流程前端页面代码请求验证码和登录的 service (src/services/login.js)处理登录的 model (src/models/login.js)后端短...
    99+
    2024-04-02
  • java短信验证码登录功能设计与实现
    目录前言业务案例业务关键点剖析短信验证码功能实现思路有效期问题操作步骤前言 现在不管是各类的网站,还是大小社交app,登录方式是越来越多了,其中基于短信验证码的登录可以说是各类app...
    99+
    2024-04-02
  • IDEA SSM整合Redis项目实例 附源码
    IDEA SSM整合Redis项目实例 1、pom.xml 配置 <!-- https://mvnrepository.com/artifact/redis.client...
    99+
    2024-04-02
  • 如何实现基于vue的短信验证码倒计时
    小编给大家分享一下如何实现基于vue的短信验证码倒计时,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一般获取短信验证码的时候会用...
    99+
    2024-04-02
  • javascript实现发送短信验证码案例
    本文实例为大家分享了javascript实现发送短信验证码的具体代码,供大家参考,具体内容如下 效果如下: 代码思路: 1.按钮点击之后,会禁用disabled 为true2.同时...
    99+
    2024-04-02
  • 在Web项目中手机短信验证码实现的全过程记录
    前言最近在做远程智能水表管理系统这个过程有一个功能是在注册页面可以使用手机注册,找了许久才大致了解了手机验证码实现流程,今天在此和大家分享一下。下面话不多说了,来一起看看详细的介绍吧。短信验证码实现流程   &...
    99+
    2023-05-30
    web项目 验证码 短信
  • 微信小程序手机号验证码登录的项目实现
    本文主要介绍了小程序手机号验证码登录,具体如下: wxml: <view class="content_bottom"> <form bindsub...
    99+
    2024-04-02
  • redis 手机验证码实现示例
    本文主要介绍了redis 手机验证码实现示例,分享给大家,具体如下: public class PhoneCode { public static void main...
    99+
    2024-04-02
  • 基于Java实现扫码登录的示例代码
    目录基本介绍原理解析1. 身份认证机制2. 流程概述代码实现1. 环境准备2. 主要依赖3. 生成二维码4. 扫描二维码5. 确认登录6. PC 端轮询7. 拦截器配置效果演示1. ...
    99+
    2024-04-02
  • 用vue实现注册页效果 vue实现短信验证码登录
    本文实例为大家分享了vue实现注册页效果 的具体代码,供大家参考,具体内容如下 一、实现效果图    二、实现代码 1、实现头部 <template> <d...
    99+
    2024-04-02
  • Android开发中通过手机号+短信验证码登录的实例代码
    首先,需要一个电话号码,目前很多账户都是将账户名设置成手机号,然后点击按钮获取手机验证码。 其次,你需要后台给你手机短信的验证接口,各个公司用的不一样,这个身为前端,不需要你来考虑,你只要让你后台给你写好接口,你直接调用就好了。activi...
    99+
    2023-05-31
    android 验证码 登录
  • 基于Python实现原生的登录验证码详情
    目录1、概述2、验证码实现的演进过程2.1 路由及页面2.2 视图函数中验证码的推导2.2.1 图片发送到前端2.2.2 引入动态图片2.2.3 内存管理模块图片2.2.4 完整图片...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作