使用Redis实现短信登陆
使用Redis实现发送验证码;验证码登陆、注册;登陆校验拦截、登陆状态刷新等一系列问题。
验证码发送和验证登陆注册
思路流程
整体的思路以及流程如题:

代码实现
实体类
User实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_user")
public class User implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO)
private Long id;
private String phone;
private String password;
private String nickName;
private String icon = "";
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
UserDTO
import lombok.Data; @Data
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
发送手机验证码
import cn.hutool.core.util.RandomUtil; @Resource
private StringRedisTemplate stringRedisTemplate; public static final String LOGIN_CODE_KEY = "login:code:";
public static final Long LOGIN_CODE_TTL = 2L; /**
* 发送手机验证码
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone) {
// 校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
// 不符合,返回错误信息
return Result.fail("手机号格式错误");
}
// 符合,生成验证码
String code = RandomUtil.randomNumbers(6);
String key = LOGIN_CODE_KEY + phone; // 保存验证码, redis
stringRedisTemplate.opsForValue().set(key, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
// 发送验证码,模拟
log.debug("发送短信验证码成功,验证码:{}", code);
return Result.ok();
}
短信验证登陆注册
import cn.hutool.core.lang.UUID; @Resource
private IUserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate; public static final String LOGIN_CODE_KEY = "login:code:";
public static final String LOGIN_USER_KEY = "login:token:";
public static final Long LOGIN_USER_TTL = 30L;
/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm){
String phone = loginForm.getPhone();
// 校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
// 不符合,返回错误信息
return Result.fail("手机号格式错误");
}
// 校验验证码
String key = LOGIN_CODE_KEY + phone;
String cacheCode = stringRedisTemplate.opsForValue().get(key);
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.equals(code)) {
// 不一致,返回错误信息
return Result.fail("验证码错误");
}
// 一致,根据手机号查询用户
User user = userService.queryUser(phone);
if (user == null) {
// 不存在,创建新用户并保存
user = createUserWithPhone(phone);
userService.save(user);
}
// 保存用户信息到 redis
String token = UUID.randomUUID().toString(true);
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()
));
String tokenKey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 返回token
return Result.ok(token);
} /**
* 创建用户
* @param phone 手机号
*/
private User createUserWithPhone(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
return user;
}
登陆校验拦截
思路流程
- 第一个拦截器是为了刷新token的有效期,防止用户在访问不需要登陆操作页面时间过长而导致token失效。
- 第二个拦截器是拦截未登陆用户,或者长时间未操作页面导致登陆失效。

代码实现
第一个拦截器
public class RefreshTokenInterceptor implements HandlerInterceptor { private StringRedisTemplate stringRedisTemplate; public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
} @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取请求头中的token
String token = request.getHeader("authorization");
if (StringUtils.isBlank(token)) {
return true;
}
// 获取redis中的用户
String key = RedisConstants.LOGIN_USER_KEY + token;
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(key); if (entries.isEmpty()) {
return true;
}
UserDTO userDTO = BeanUtil.fillBeanWithMap(entries, new UserDTO(), false);
// 存在,保存到ThreadLocal
UserHolder.saveUser(userDTO);
// 刷新token有效期
stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
return true;
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
第二个拦截器
public class LoginInterceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (UserHolder.getUser() == null) {
// 不存在,拦截,返回401状态码
response.setStatus(401);
return false;
}
return true;
}
}
注册拦截器
注意拦截顺序,默认按照添加顺序,也可手动指定。
@Configuration
public class MvcConfig implements WebMvcConfigurer { @Resource
private StringRedisTemplate stringRedisTemplate; @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/voucher/**",
"/shop-type/**",
"/shop/**",
"/user/code",
"/user/login",
"/blog/hot"
).order(1);
}
}
上一篇文章:Redis快速入门
下一篇文章:
使用Redis实现短信登陆的更多相关文章
- 青龙+Nvjdc短信登陆对接Xdd-plus推送+Ninja CK登陆教程(11.23更新)
一.准备工作 1.shh工具(powshell.gitbash等等) 2.购买一台云服务器(阿里云.腾讯云都可以) 3.安装宝塔面板 宝塔Linux面板安装教程 - 2021年8月18日更新 - 7. ...
- 2.Redis 控制短信重发
需要两个缓存 key名称 phone-busy,缓存1分钟key名称 phone-send-count,缓存1天,每成功发送一条+1 发送的时候流程如下: 判断phone-busy是否存在,存在直接报 ...
- PHP redis 群发短信
$redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $list = M('Sms')->field('phone')-&g ...
- php 操作Redis发送短信
循环查询redis队列里面的数据 然后提交数据后将反馈信息再写入另一个 redis list里面 代码 <?php /** * System Name: sent message * User: ...
- 循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理
在一些系统中,有时候用户忘记密码,可以通过向自己手机发送动态验证码的方式实现系统登录功能.本篇随笔介绍如何结合后端ABP框架的短信发送和缓存模块的处理,实现手机短信动态码登陆处理. 一般的登录方式,分 ...
- 短信登录与注册接口、前端所有方式登录注册页面、redis数据库介绍与安装
今日内容概要 短信登陆接口 短信注册接口 登陆注册前端 redis介绍和安装 内容详细 1.短信登陆接口 在视图类 user/views.py中修改并添加: from .serializer impo ...
- 发短信的简单实现——C#版
为了验证操作人的身份,界面中通常会有获取验证码的功能.及点击获取验证码就会往你输入的手机号里面发送一条短信进行验证. 最近公司给我的任务中也包含这个功能,那么接下来就让我讲解下. ---------- ...
- SpringSecurity实现短信登录功能
⒈封装短信验证码类 package cn.coreqi.security.validate; import java.time.LocalDateTime; public class Validate ...
- django-实现登录短信验证
功能演示 核心任务 前端功能: 点击按钮Ajax调用发送验证码功能 输完验证码后Ajax调用验证功能 后端功能: 功能1:发送验证码功能 功能2:验证码检查 后台核心逻辑(不需要手写) 功能3:发短信 ...
- SpringSceurity(4)---短信验证码功能实现
SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...
随机推荐
- 数据库SQL(MSSQLSERVER)服务启动错误代码3414
昨天永和客户联系我,说他们的前台系统报错了,给我发了报错的图片.看到错误的第一眼就知道是数据库出问题了,连不上sql Server. 虽然知道是数据库出问题了,但是刚开始的时候没有打开SQL Serv ...
- 干货分享|身为顶尖的Hr,这个Excel插件你不能不知道,用上它事业开挂!
第一季度,老板看了历年不同地区各销售业绩数据表的总结,说想知道新人进来多久才能成为成熟的销售,成长周期有多长? 我们人事被老板这个灵光一现的想法吓到了,大家伙上上下下为这件事情忙了4个日夜. 整整五年 ...
- HarmonyOS多音频播放并发政策及音频管理解析
音频打断策略 多音频并发,即多个音频流同时播放.此场景下,如果系统不加管控,会造成多个音频流混音播放,容易让用户感到嘈杂,造成不好的用户体验.为了解决这个问题,系统预设了音频打断策略,对多音频播放 ...
- HarmonyOS NEXT新能力,一站式高效开发HarmonyOS应用
2023年8月6日华为开发者大会2023(HDC.Together)圆满收官,伴随着HarmonyOS 4的发布,华为向开发者发布了汇聚所有最新开发能力的HarmonyOS NEXT开发者预览版, ...
- HarmonyOS课程体验官招募(第四期),寻找乐于分享,精益求精的伙伴
华为开发者联盟HarmonyOS课程体验官(第四期)活动,开始招募啦! 如果你精益求精.乐于分享:如果你愿意为学堂课程优化改进出谋划策,那就快来加入我们吧!学堂期待与你共同成长.一起进步! [活动 ...
- arp 的基础概念
前言 打算整理网络这一块,先把概念写完. 就是有一个问题,那就是为什么有ip地址还有mac地址呢? 原因是这样子的,我们知道ip协议是第三层,那么有一个问题了,如果只有第三层的ip是否能过识别到主机? ...
- 通过一个非常简单的SSM项目来将SpringMVC配置整理清晰。
所有的文件在Git上面都能找到,由于把代码搞上来看的很不舒服,结构不清晰. 第一步:确定环境 IDEA MySQL 5.7.19 Tomcat 9 Maven 3.6 第二步:创建数据库 参考GIT上 ...
- 图片验证码识别,标签中onehot编码对应多个1怎么做?
一张验证码图片直接做整体识别 也就是图片的最后输出节点有4*26=104个,经过一个softmax,使用交叉熵损失,与真实值4*26=104个标签做计算,然后反向传播 104个onehot编码真实值当 ...
- 网易游戏基于 Flink 的流式 ETL 建设
简介: 网易游戏流式 ETL 建设实践及调优经验分享- 网易游戏资深开发工程师林小铂为大家带来网易游戏基于 Flink 的流式 ETL 建设的介绍.内容包括: 专用 ETL EntryX 通用 ETL ...
- 实操指南 | Resource Queue如何实现对AnalyticDB PostgreSQL的资源管理?
简介: 作者:阿里云数据库OLAP产品部 - 子华 一 背景 AnalyticDB PostgreSQL版(简称ADB PG)是阿里云数据库团队基于PostgreSQL内核(简称PG)打造的一款云原生 ...