news 2026/7/17 5:15:16

【项目扩展实战|第2篇】Spring Boot + Redis 实现验证码登录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【项目扩展实战|第2篇】Spring Boot + Redis 实现验证码登录

`

前言

上一篇我们实现了 Spring Boot + JWT 登录认证,用户通过用户名和密码登录,登录成功后后端生成 token 返回给前端。

这一篇继续扩展登录功能,实现一个更常见的业务场景:验证码登录

验证码登录在项目中非常常见,比如:

  • 手机号短信验证码登录
  • 邮箱验证码登录
  • 忘记密码校验验证码
  • 注册账号校验验证码
  • 修改手机号校验验证码

验证码这种数据有一个特点:

有效时间短,用完就可以删除。

所以它非常适合保存到 Redis 中。

这一篇我们就用 Spring Boot + Redis 实现验证码发送和验证码校验。

一、功能需求

本篇要实现两个接口:

  1. 发送验证码
  2. 验证码登录

整体流程:

用户输入手机号 ↓ 请求发送验证码 ↓ 后端生成 6 位验证码 ↓ 验证码保存到 Redis,设置 5 分钟过期 ↓ 用户输入手机号和验证码登录 ↓ 后端从 Redis 查询验证码 ↓ 验证码正确:登录成功,生成 token ↓ 验证码错误或过期:登录失败

二、引入 Redis 依赖

pom.xml中添加 Redis 依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

如果项目中已经有这个依赖,就不用重复添加。

三、配置 Redis

application.yml中配置 Redis:

spring:redis:host:localhostport:6379database:0

如果 Redis 有密码:

spring:redis:host:localhostport:6379password:你的密码database:0

如果是较新版本 Spring Boot,也可能使用:

spring:data:redis:host:localhostport:6379database:0

具体根据项目版本调整。

四、验证码 key 设计

Redis key 命名要清楚。

本篇验证码 key 设计为:

login:code:手机号

比如手机号是:

13800000000

那么 Redis key 就是:

login:code:13800000000

value 保存验证码:

952713

过期时间设置 5 分钟。

五、准备登录参数类

验证码登录时,前端传手机号和验证码。

publicclassCodeLoginDTO{privateStringphone;privateStringcode;// getter、setter 省略}

发送验证码时,可以直接用@RequestParam接收手机号。

六、Mapper 层实现

验证码登录成功后,需要根据手机号查询用户。

@MapperpublicinterfaceUserMapper{@Select("select id, username, password, name, phone, create_time, update_time "+"from user where phone = #{phone}")UsergetByPhone(Stringphone);@Insert("insert into user(phone, name, create_time, update_time) "+"values(#{phone}, #{name}, #{createTime}, #{updateTime})")voidinsert(Useruser);}

这里提供两个方法:

  1. 根据手机号查询用户
  2. 新增用户

如果手机号第一次登录,可以自动注册一个用户。

七、Service 层接口

publicinterfaceLoginService{voidsendCode(Stringphone);StringloginByCode(CodeLoginDTOcodeLoginDTO);}

这里:

sendCode

负责生成验证码并保存到 Redis。

loginByCode

负责校验验证码,登录成功后返回 token。

八、Service 层实现

@ServicepublicclassLoginServiceImplimplementsLoginService{@AutowiredprivateStringRedisTemplatestringRedisTemplate;@AutowiredprivateUserMapperuserMapper;@OverridepublicvoidsendCode(Stringphone){if(phone==null||phone.length()!=11){thrownewRuntimeException("手机号格式不正确");}Stringcode=String.valueOf(newRandom().nextInt(900000)+100000);Stringkey="login:code:"+phone;stringRedisTemplate.opsForValue().set(key,code,5,TimeUnit.MINUTES);System.out.println("验证码:"+code);}@OverridepublicStringloginByCode(CodeLoginDTOcodeLoginDTO){Stringphone=codeLoginDTO.getPhone();Stringcode=codeLoginDTO.getCode();Stringkey="login:code:"+phone;StringredisCode=stringRedisTemplate.opsForValue().get(key);if(redisCode==null){thrownewRuntimeException("验证码已过期");}if(!redisCode.equals(code)){thrownewRuntimeException("验证码错误");}Useruser=userMapper.getByPhone(phone);if(user==null){user=newUser();user.setPhone(phone);user.setName("用户"+phone.substring(7));user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userMapper.insert(user);}Map<String,Object>claims=newHashMap<>();claims.put("id",user.getId());claims.put("phone",user.getPhone());claims.put("name",user.getName());Stringtoken=JwtUtils.generateJwt(claims);stringRedisTemplate.delete(key);returntoken;}}

九、Controller 层实现

@RestController@RequestMapping("/codeLogin")publicclassCodeLoginController{@AutowiredprivateLoginServiceloginService;@PostMapping("/send")publicResultsendCode(@RequestParamStringphone){loginService.sendCode(phone);returnResult.success();}@PostMappingpublicResultlogin(@RequestBodyCodeLoginDTOcodeLoginDTO){Stringtoken=loginService.loginByCode(codeLoginDTO);returnResult.success(token);}}

文字说明

发送验证码接口:

POST /codeLogin/send?phone=13800000000

验证码登录接口:

POST /codeLogin

请求体:

{"phone":"13800000000","code":"952713"}

登录成功后返回 token。

十、验证码登录流程说明

验证码登录的核心逻辑在 Service 层。

1. 发送验证码

Stringcode=String.valueOf(newRandom().nextInt(900000)+100000);

生成 6 位验证码。

stringRedisTemplate.opsForValue().set(key,code,5,TimeUnit.MINUTES);

保存到 Redis,并设置 5 分钟过期。

2. 校验验证码

StringredisCode=stringRedisTemplate.opsForValue().get(key);

从 Redis 中取验证码。

如果取不到:

thrownewRuntimeException("验证码已过期");

如果不一致:

thrownewRuntimeException("验证码错误");

3. 查询或创建用户

Useruser=userMapper.getByPhone(phone);

如果用户不存在,就自动创建一个新用户。

4. 生成 token

Stringtoken=JwtUtils.generateJwt(claims);

登录成功后生成 JWT 返回给前端。

5. 删除验证码

stringRedisTemplate.delete(key);

验证码使用成功后删除,避免重复使用。

十一、涉及知识点

1. Redis 过期时间

验证码是临时数据,非常适合设置过期时间。

.set(key,code,5,TimeUnit.MINUTES)

5 分钟后 Redis 会自动删除。

2. StringRedisTemplate

这里使用:

StringRedisTemplate

操作 Redis 字符串。

验证码本身就是字符串,所以用它非常合适。

3. JWT

验证码登录成功后,仍然需要生成 token。

因为后续接口依旧需要通过 token 判断用户是否登录。

4. 自动注册

如果手机号第一次登录,可以自动创建用户。

这在很多移动端项目中很常见。

十二、验证码接口限流

发送验证码接口不能无限调用。

可以加一个简单限流。

privatevoidcheckSendCodeLimit(Stringphone){Stringkey="limit:sendCode:"+phone;Longcount=stringRedisTemplate.opsForValue().increment(key);if(count!=null&&count==1){stringRedisTemplate.expire(key,60,TimeUnit.SECONDS);}if(count!=null&&count>5){thrownewRuntimeException("验证码发送过于频繁,请稍后再试");}}

然后在发送验证码前调用:

@OverridepublicvoidsendCode(Stringphone){checkSendCodeLimit(phone);// 生成并保存验证码}

这样可以限制同一个手机号 60 秒内最多发送 5 次验证码。

十三、实际开发建议

验证码登录开发时,可以注意下面几点:

  1. 验证码一定要设置过期时间
  2. 验证码校验成功后建议删除
  3. 发送验证码接口要限流
  4. 手机号格式要校验
  5. 验证码不要返回给前端,真实项目应该通过短信或邮箱发送
  6. 登录成功后仍然返回 token
  7. token 中不要保存敏感信息
  8. Redis key 命名要清楚
  9. 异常建议用自定义业务异常
  10. 密码登录和验证码登录可以共用 token 认证逻辑

十四、总结

这一篇主要实现了 Spring Boot + Redis 验证码登录。

整体流程是:用户请求发送验证码,后端生成 6 位验证码并保存到 Redis,设置 5 分钟过期;用户提交手机号和验证码登录,后端从 Redis 查询验证码并校验;校验成功后查询或创建用户,最后生成 JWT token 返回给前端。

这个功能把 Redis、JWT、Controller、Service、Mapper 都串起来了,比单独学知识点更接近真实项目开发。

下一篇我们继续写统一异常处理和业务异常封装,让项目中的错误返回更加规范。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 5:13:05

Ubuntu安装搜狗输入法全指南与优化技巧

1. 为什么要在Ubuntu上安装搜狗输入法作为一名长期使用Linux系统的开发者&#xff0c;我深刻理解中文输入在Ubuntu环境下的痛点。系统自带的ibus输入法虽然稳定&#xff0c;但在词库丰富度、云输入体验和用户习惯适配方面&#xff0c;与国内主流输入法存在明显差距。搜狗输入法…

作者头像 李华
网站建设 2026/7/17 5:13:04

2026最新口碑筛选 | 鼓楼区学生会议记录使用场景实用选择建议

这里写自定义目录标题 "针对鼓楼区学生群体常用的会议记录使用场景&#xff0c;结合2026年学生群体真实使用口碑&#xff0c;整理了这份实用选择建议&#xff0c;核心思路是不同学习场景选对应工具&#xff0c;不用盲目跟风追求全功能&#xff0c;主打够用就好、看完就能用…

作者头像 李华
网站建设 2026/7/17 5:11:53

【真实经验分享】Oracle 软件应用补丁中断后数据库启动异常

节点1数据库软件顺利打完补丁&#xff0c;但是节点2在打软件补丁的过程中&#xff0c;由于虚拟机异常被迫重启&#xff0c;导致应用补丁中断&#xff0c;开机后检查节点2数据库软件补丁已应用&#xff0c;但无法正常开启节点2数据库实例注&#xff1a;打补丁前&#xff0c;一定…

作者头像 李华
网站建设 2026/7/17 5:11:44

AI驱动Python单元测试:从手动编写到智能生成的完整实践指南

1. 项目概述&#xff1a;从“人肉”测试到AI驱动的范式转变如果你和我一样&#xff0c;写过几年Python代码&#xff0c;那对单元测试一定又爱又恨。爱的是&#xff0c;它确实是保证代码质量、防止“改A坏B”的利器&#xff1b;恨的是&#xff0c;写测试用例这事儿&#xff0c;太…

作者头像 李华
网站建设 2026/7/17 5:11:40

OpenClaw架构解析:AI Agent调度系统的四层设计

1. OpenClaw架构全景&#xff1a;当AI遇上机场调度模型第一次接触OpenClaw时&#xff0c;很多人会误以为这又是一个套着AI外壳的聊天机器人。但当你拆开它的技术内核&#xff0c;会发现这实际上是一个革命性的AI Agent操作系统——就像用乐高积木搭建的智能城市&#xff0c;每个…

作者头像 李华
网站建设 2026/7/17 5:10:29

Unity AR Foundation官方示例项目全解析:从零到一掌握AR开发核心

1. 项目概述&#xff1a;为什么你需要这个AR Foundation示例项目&#xff1f;如果你正在用Unity捣鼓AR应用&#xff0c;或者刚被老板或客户丢过来一个AR需求&#xff0c;那你大概率听说过AR Foundation。这玩意儿是Unity官方推出的跨平台AR开发框架&#xff0c;理论上能让你写一…

作者头像 李华