基于Redis的短信验证登录

article/2025/9/15 16:17:13

基于Redis的短信验证登录

      • 1、用户调用发送短信验证码接口
      • 2、用户调用登录/注册接口
      • 3、用户调用校验接口
      • 4、SpringMvc拦截器注册
      • 5、token刷新拦截器
      • 6、登录拦截器

在这里插入图片描述

1、用户调用发送短信验证码接口

用户调用sendCode()接口,把phone传到后端,后端对phone进行格式校验,如果通过校验,则生成6位数验证码,并保存到redis中,phone为key,code为value,注意设置有效期

    /*** 基于Redis的发送并保存验证码** @param phone* @param session* @return*/@Overridepublic Result sendCode(final String phone, final HttpSession session) {
//         1.校验手机号是否正确,如果手机号不通过校验,则returnif (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机号码格式错误!请重新输入!");}
//         2.如果手机号通过校验,则自动生成6位数的随机验证码String code = RandomUtil.randomNumbers(6);
//         3.把验证码保存到redis中stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);log.debug("验证码生成成功:" + code);
//         4.返回okreturn Result.ok("验证码发送成功!");}

2、用户调用登录/注册接口

  1. 用户调用login(LoginData loginForm)接口,把phone跟code传到后端

  2. 后端先对phone进行格式校验,如果不通过校验,则返回提示信息

  3. 如果通过校验,则根据phone从redis中查询缓存的cacheCode,跟用户传过来的code进行匹配,如果不匹配或者cacheCode已经过期,那么提示用户验证码错误或者验证码已经过期

  4. 如果phone格式正确并且code跟缓存中的cacheCode匹配,则根据phone字段在数据库中查找用户的数据,如果用户不存在,则说明用户发送验证码是首次登录,需要进行信息注册,那么把user保存到数据库中

  5. 如果数据库中的用户已经存在,说明用户发送验证码和手机号是为了登录,则自动生成token作为key,用户信息user作为value,选择hash数据类型或者string类型存储到Redis中,并设置有效期

  6. 把生成的token返回给前端,以便前端把token保存到浏览器的缓存中,在每次发送请求之前在浏览器缓存中中取出token,放到请求头的authorization中,以便请求发送到后端时,可以在拦截器中根据token从redis缓存中获取用户信息,判断用户是否处于登录状态

    /*** 用户登录或者注册** @param loginForm* @return*/@Overridepublic Result login(LoginFormDTO loginForm) {
//         1.用户传来phone和code,先校验手机号是否正确String phone = loginForm.getPhone();
//         2.如果手机号不通过校验,则返回手机号格式错误if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机号格式错误!请重新输入!");}String code = loginForm.getCode();
//         3.从redis中查询cacheCode,如果不匹配,则返回验证码错误String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);if (cacheCode == null || !cacheCode.equals(code)) {return Result.fail("验证码错误!请重新输入!");}
//        4.如果匹配,则可以登录,但是如果用户是首次登录,则数据库中没有该用户相关数据,需要创建一个新用户
//        5.根据phone从数据库中查询用户信息User user = query().eq("phone", phone).one();if (user == null) {user = createUserWithPhone(phone);//此时数据库中一定有了用户}
//        6.用token作为redis中保存用户信息的key
//        使用hutool的工具包生成tokenString token = UUID.randomUUID().toString(true);
//        7.把user转换成UserDTOUserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
//        8.转换成Map进行存储,因为存储到Redis选择Hash结构,需要注意BeanUtil工具类只能转换类中String类型的字段,所以需要自定义字段转换器,把字段值转换成String类型Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
//        9.把userMap存储到Redis中stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, userMap);
//        10.设置token的有效期stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//        11.返回token给前端,前端会把token保存到浏览器缓存中,然后每次发送请求事,请求拦截器会把token添加到请求头authorization中
//        这样每次发送请求,只要有token,就说明用户处于登录状态return Result.ok(token);}private User createUserWithPhone(String phone) {User user = new User();user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomNumbers(10));user.setPhone(phone);save(user);return user;}

3、用户调用校验接口

用户调用me()接口,从ThreadLocal中获取用户信息,返回给前端

    @GetMapping("/me")public Result me() {
//        返回UserDTO,省略掉了敏感字段return Result.ok(UserHolder.getUser());}

4、SpringMvc拦截器注册

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 注册拦截器** @param registry*/@Overridepublic void addInterceptors(final InterceptorRegistry registry) {
//        添加登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/login",//用户登录接口"/shop/**",//购物模块的浏览"/shop-type/**",//商品类型"/upload/**",//用户上传博客模块"/blog/hot",//博客热点接口"/voucher/**",//优惠卷模块"/user/code"//获取验证码接口).order(1);
//        添加刷新token拦截器,拦截并校验所有页面registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);}
}

5、token刷新拦截器

注册RedisFreshInterceptor拦截器,实现HandlerInterceptor接口,重写调用controller前的方法preHandle,对所有页面进行拦截,判断是否存在token,如果不存在,则直接放行给后面进行登录校验或者普通页面,如果Redis中有token,则刷新token的有效期,并从Redis中获取用户信息,保存到本地线程对象ThreadLocal中,以便me()接口进行用户登录状态校验

public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}/*** 刷新用户token* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
//        1.获取请求头中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {//如果没有token,则一定没有用户信息,不需要刷新return true;//放行给后面校验}
//        2.从redis中获取token对应的用户信息String userKey = RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(userKey);if(userMap.isEmpty()){//如果token已经过期,也直接放行return true;}
//        3.把userMap转换成UserDTOUserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
//        4.把UserDTO存放到ThreadLocal中,方便登录拦截器获取校验UserHolder.saveUser(userDTO);
//        5.刷新token的有效期stringRedisTemplate.expire(userKey, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//        6.放行return true;}@Overridepublic void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

6、登录拦截器

对普通页面进行放行,对需要用户登录状态的页面进行拦截,从本地线程对象ThreadLocal中获取用户信息,如果用户信息不存在,则说明用户未进行登录,进行请求拦截不放行,提示用户进行登录,返回状态码401

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
//        1.从本地线程对象中获取userDTOUserDTO userDTO = UserHolder.getUser();
//        2.判断是否有用户信息,如果没有,则说明用户没有登录if(userDTO == null){response.setStatus(401);return false;}
//        3.放行return true;}@Overridepublic void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

http://chatgpt.dhexx.cn/article/FBq165ll.shtml

相关文章

使用聚合数据短信API测试(短信验证登录)

搞一手聚合数据短信API测试&#xff08;之前用阿里云的搞过&#xff0c;今天我们用聚合&#xff09; 注册聚合账号&#xff01;聚合官网链接登陆后进入短信服务API&#xff08;免费提供十次&#xff09; 添加自定义模板&#xff08;审核速度看脸&#xff09; 审核成功后得…

android studio 实现短信验证 登录

登录 http://www.mob.com/ 注册 创建项目 加入依赖 贴代码 classpath “com.mob.sdk:MobSDK:2018.0319.1724” apply plugin: ‘com.mob.sdk’ // 在MobSDK的扩展中注册SMSSDK的相关信息 这里使用自己的 appKey appSecret MobSDK {appKey “2e2974aec0” appSecret “1d35b87…

Java简单实现短信验证登录(Session、Redis)

前端设计 <div class"login-form"><div style"display: flex; justify-content: space-between"><el-input style"width: 60%" placeholder"请输入手机号" v-model"form.phone" ></el-input><e…

Vue与Node.js实现手机短信验证登录

手机短信使用的第三方平台是联容云&#xff0c;注册就送8块钱体验费&#xff0c;足够自己用用了&#xff0c;注册完自己建一个应用就能拿到需要使用的配置了&#xff0c;如图 注册完之后1就可以使用了。 Node.js后端使用了Express框架 "js-base64": "^3.7.2&qu…

【青龙面板+诺兰2.0 网页短信验证登录+bot查询】

看这个之前&#xff0c;如果是没搭建过的先看下面这篇哈&#xff0c;如果是跟着下面的搭建完了&#xff0c;出现了机器人5次获取验证码失败&#xff0c;让你用Cookie方式登录的情况&#xff0c;看这篇哈。 前提&#xff1a;自己有服务器&#xff01;这里用的Centos7.6做演示&am…

Springboot实现短信登录验证

Springboot学习笔记——Java实现短信登录验证功能--Servlet/SSM/SpringBoot都可以用 小白记录一下短信验证登入的实现&#xff0c;方便以后可以拿来直接用。 发短信平台&#xff1a;互亿无线 官网地址 登入注册啥的就不说了&#xff0c;新人注册会送十条短信验证&#xff0c;想…

java WEB调用秒嘀科技短信验证接口(实现短信验证登录)

java WEB调用秒嘀科技短信验证接口&#xff08;实现短信验证登录&#xff09; 前言注册秒嘀云账号登录秒嘀云官网 代码 前言 短信验证登咱就不多说了&#xff0c;为什么推荐用秒嘀的呢&#xff0c;应为他会送你10元钱&#xff0c;对于新手来说10元钱&#xff0c;足够你玩了。但…

Android利用mob实现短信验证登录

首先要去官网申请一个应用&#xff0c;拿到对应的APPKEY以及APPSECRET 附上直通车链接MobTech 申请应用基本是秒批&#xff0c;然后就可以得到应用的APPKEY以及APPSECRET 然后就是查看官方的文档 直接跟着步骤走&#xff0c;可以不用手动下载sdk&#xff0c;导入这些它自动会帮…

微信小程序短信验证登录

首先小程序wxml页面 <!--pages/logins/logins.wxml--> <view class"container"><view class"title">登录</view><form catchsubmit"login"><view class"inputView"><input class"inputT…

Springboot实现短信验证登录

一、介绍 使用短信验证登录也是现在实际项目中普遍使用的一种登录, 二、实际的操作流程 1.用户在前端页面输入手机号码之后,点击发送验证码 2.前端将手机号传给后端 3.后端生成一个6为的随机数通过短信发送给用户,之后将手机号设为key,验证码设为value存入redis缓存中…

html短信验证登录

最近在做项目中遇到了一个问题&#xff0c;做的是一个后台管理。都知道&#xff0c;后台是需要一定安全性的&#xff0c;所以一定要有登录这个功能的。然而登录这个功能&#xff0c;又有一个不可或缺的因素&#xff0c;那就是验证登录。不论是图形验证也好&#xff0c;还是什么…

spring security——短信验证码登录(四)

一、导读 短信登录和用户名密码登录的逻辑是不同的&#xff0c;Spring Security 框架中实现的是用户名密码的登录方式。现在我们就模仿它的原理来加入短信登录的认证&#xff08;注意不是验证&#xff09;&#xff0c;实现右边的。 之前写的图形验证码是在 UsernamePasswordAut…

雅克比矩阵求导推导

首先&#xff0c;引入雅克比矩阵公式&#xff1a; J [ u 1 u 2 ⋯ u n e 1 e 2 ⋯ e n ] \mathbf{J}\left[\begin{array}{c} \begin{array}{lll}\mathbf{u}_{1} & \mathbf{u}_{2} & \cdots & \mathbf{u}_{n}\end{array} \\ \begin{array}{lll}\mathbf{e}_{1} &…

矩阵求导公式

转自&#xff1a;http://blog.sina.com.cn/s/blog_4a033b090100pwjq.html 求导公式(撇号为转置&#xff09;&#xff1a; Y A * X --> DY/DX A Y X * A --> DY/DX A Y A * X * B --> DY/DX A * B Y A * X * B --> DY/DX B * A 乘积的导数 d(f*g)/dx(df/dx)…

矩阵求导法则的总结

最重要的写在前面&#xff1a; 进行更新&#xff1a;随着理解的加深&#xff0c;我发现之前写的有些问题&#xff0c;重新写一下吧 矩阵求导要分成两类&#xff0c;第一类是用在向量函数f(x)里&#xff0c;比如要在x0处展开&#xff0c;所以需要计算该点处的雅可比这些&#…

-矩阵求导-

一、为什么需要矩阵求导 参数的向量化&#xff1a; 向量化会使代码、式子更加简洁&#xff1b; 使用向量化代替for循环&#xff0c;向量化能够加速你的代码&#xff1b;求导在优化算法中的广泛应用&#xff1a; 优化算法需要反向传播&#xff0c;需要对参数矩阵进行求导 二、…

矩阵求导简析

大家好&#xff0c;这是我的第一篇博客。 矩阵求导&#xff08;Matrix Derivation&#xff0c;或者Matrix Differential&#xff09;&#xff0c;在机器学习、图像处理、最优化领域经常会遇到。其本质是多元变量的微积分&#xff0c;只是把求导应用在了矩阵上&#xff0c;不同在…

矩阵求导法则

转载自&#xff1a;https://blog.csdn.net/dinkwad/article/details/72819832 矩阵求导的技术&#xff0c;在统计学、控制论、机器学习等领域有广泛的应用。鉴于我看过的一些资料或言之不详、或繁乱无绪&#xff0c;本文来做个科普&#xff0c;分作两篇&#xff0c;上篇讲标量…

矩阵求导总结

文章目录 1.分子为标量1.1 ∂ 标量 ∂ 标量 \frac{\partial 标量}{\partial标量} ∂标量∂标量​1.2 ∂ 标量 ∂ 向量 \frac{\partial 标量}{\partial向量} ∂向量∂标量​1.3 ∂ 标量 ∂ 矩阵 \frac{\partial 标量}{\partial矩阵} ∂矩阵∂标量​ 2.分子为向量2.1 ∂ 向量…

矩阵求导方法

矩阵求导方法 在机器学习过程中&#xff0c;我们经常会对矩阵进行相关的操作&#xff0c;现对矩阵求导方法进行概况与推导。 首先总结矩阵求导的本质&#xff0c;即矩阵A中每个元素对矩阵B中每个元素求导。我们先从宏观上理解这个公式&#xff0c;即从求导后元素的个数来理解…