前言上一篇我们实现了 Spring Boot JWT 登录认证用户通过用户名和密码登录登录成功后后端生成 token 返回给前端。这一篇继续扩展登录功能实现一个更常见的业务场景验证码登录。验证码登录在项目中非常常见比如手机号短信验证码登录邮箱验证码登录忘记密码校验验证码注册账号校验验证码修改手机号校验验证码验证码这种数据有一个特点有效时间短用完就可以删除。所以它非常适合保存到 Redis 中。这一篇我们就用 Spring Boot Redis 实现验证码发送和验证码校验。一、功能需求本篇要实现两个接口发送验证码验证码登录整体流程用户输入手机号 ↓ 请求发送验证码 ↓ 后端生成 6 位验证码 ↓ 验证码保存到 Redis设置 5 分钟过期 ↓ 用户输入手机号和验证码登录 ↓ 后端从 Redis 查询验证码 ↓ 验证码正确登录成功生成 token ↓ 验证码错误或过期登录失败二、引入 Redis 依赖在pom.xml中添加 Redis 依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency如果项目中已经有这个依赖就不用重复添加。三、配置 Redis在application.yml中配置 Redisspring: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:13800000000value 保存验证码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);}这里提供两个方法根据手机号查询用户新增用户如果手机号第一次登录可以自动注册一个用户。七、Service 层接口publicinterfaceLoginService{voidsendCode(Stringphone);StringloginByCode(CodeLoginDTOcodeLoginDTO);}这里sendCode负责生成验证码并保存到 Redis。loginByCode负责校验验证码登录成功后返回 token。八、Service 层实现ServicepublicclassLoginServiceImplimplementsLoginService{AutowiredprivateStringRedisTemplatestringRedisTemplate;AutowiredprivateUserMapperuserMapper;OverridepublicvoidsendCode(Stringphone){if(phonenull||phone.length()!11){thrownewRuntimeException(手机号格式不正确);}StringcodeString.valueOf(newRandom().nextInt(900000)100000);Stringkeylogin:code:phone;stringRedisTemplate.opsForValue().set(key,code,5,TimeUnit.MINUTES);System.out.println(验证码code);}OverridepublicStringloginByCode(CodeLoginDTOcodeLoginDTO){StringphonecodeLoginDTO.getPhone();StringcodecodeLoginDTO.getCode();Stringkeylogin:code:phone;StringredisCodestringRedisTemplate.opsForValue().get(key);if(redisCodenull){thrownewRuntimeException(验证码已过期);}if(!redisCode.equals(code)){thrownewRuntimeException(验证码错误);}UseruseruserMapper.getByPhone(phone);if(usernull){usernewUser();user.setPhone(phone);user.setName(用户phone.substring(7));user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userMapper.insert(user);}MapString,ObjectclaimsnewHashMap();claims.put(id,user.getId());claims.put(phone,user.getPhone());claims.put(name,user.getName());StringtokenJwtUtils.generateJwt(claims);stringRedisTemplate.delete(key);returntoken;}}九、Controller 层实现RestControllerRequestMapping(/codeLogin)publicclassCodeLoginController{AutowiredprivateLoginServiceloginService;PostMapping(/send)publicResultsendCode(RequestParamStringphone){loginService.sendCode(phone);returnResult.success();}PostMappingpublicResultlogin(RequestBodyCodeLoginDTOcodeLoginDTO){StringtokenloginService.loginByCode(codeLoginDTO);returnResult.success(token);}}文字说明发送验证码接口POST /codeLogin/send?phone13800000000验证码登录接口POST /codeLogin请求体{phone:13800000000,code:952713}登录成功后返回 token。十、验证码登录流程说明验证码登录的核心逻辑在 Service 层。1. 发送验证码StringcodeString.valueOf(newRandom().nextInt(900000)100000);生成 6 位验证码。stringRedisTemplate.opsForValue().set(key,code,5,TimeUnit.MINUTES);保存到 Redis并设置 5 分钟过期。2. 校验验证码StringredisCodestringRedisTemplate.opsForValue().get(key);从 Redis 中取验证码。如果取不到thrownewRuntimeException(验证码已过期);如果不一致thrownewRuntimeException(验证码错误);3. 查询或创建用户UseruseruserMapper.getByPhone(phone);如果用户不存在就自动创建一个新用户。4. 生成 tokenStringtokenJwtUtils.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){Stringkeylimit:sendCode:phone;LongcountstringRedisTemplate.opsForValue().increment(key);if(count!nullcount1){stringRedisTemplate.expire(key,60,TimeUnit.SECONDS);}if(count!nullcount5){thrownewRuntimeException(验证码发送过于频繁请稍后再试);}}然后在发送验证码前调用OverridepublicvoidsendCode(Stringphone){checkSendCodeLimit(phone);// 生成并保存验证码}这样可以限制同一个手机号 60 秒内最多发送 5 次验证码。十三、实际开发建议验证码登录开发时可以注意下面几点验证码一定要设置过期时间验证码校验成功后建议删除发送验证码接口要限流手机号格式要校验验证码不要返回给前端真实项目应该通过短信或邮箱发送登录成功后仍然返回 tokentoken 中不要保存敏感信息Redis key 命名要清楚异常建议用自定义业务异常密码登录和验证码登录可以共用 token 认证逻辑十四、总结这一篇主要实现了 Spring Boot Redis 验证码登录。整体流程是用户请求发送验证码后端生成 6 位验证码并保存到 Redis设置 5 分钟过期用户提交手机号和验证码登录后端从 Redis 查询验证码并校验校验成功后查询或创建用户最后生成 JWT token 返回给前端。这个功能把 Redis、JWT、Controller、Service、Mapper 都串起来了比单独学知识点更接近真实项目开发。下一篇我们继续写统一异常处理和业务异常封装让项目中的错误返回更加规范。