验证码生成 + Redis 暂存 + JWT 认证

所用相关接口如上:

没有接口时报错如下:

一、验证码生成与 Redis 暂存

1. Redis 环境准备

(1)Redis 启动与运行
  • 解压 Redis 压缩包后,双击redis-server.exe,弹出运行窗口且提示 "Ready to accept connections" 即启动成功(禁止关闭该窗口);
  • 默认配置:端口 6379,内置 0-15 共 16 个数据库,默认使用第 0 个。
(2)Redis 图形化工具(RedisDesktopManager)
  • 解压双击rdm.exe启动工具,新建连接:
    • 自定义命名,Host 填写 127.0.0.1,端口 6379,默认无密码;
    • 先测试连接,确认可用后保存,可通过工具手动创建 key-value 数据验证 Redis 可用性。

使用redis数据库实现暂存

redis数据库存在0-15个

解压redis压缩包并直接双击redis-server.exe即可运行

弹出该弹窗即算运行成功(注意不要关)

此刻即可通过redis命令在控制台(双击redis-cli.exe)使用

redis的图形化工具是RedisDesktopManager

使用方式同上解压并双击rdm.exe

这就是图形化工具

然后新建连接服务器连接服务,自定义命名,默认无密码,host:127.0.1

若之前没有持续运行redis-server.exe弹窗或创建连接时已关闭,应该先连接测试再确定

这里可以新建key:value型数据,例如:

(3)项目中 Redis 配置

application.yml添加如下配置:

bash 复制代码
redis:
  host: 127.0.0.1  # Redis服务地址
  port: 6379       # 默认端口
  database: 0      # 使用第0个数据库
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    type-aliases-package: com.qcby.communityTest1.entity

2. 验证码生成接口(/captcha)

(1)核心代码
java 复制代码
@GetMapping("/captcha")
public Result getCaptcha(){
    // 1. 生成验证码图片(宽130、高38、4位字符)
    SpecCaptcha specCaptcha= new SpecCaptcha(130,38,4);
    // 2. 获取验证码文本并统一转大写(避免大小写校验问题)
    String code = specCaptcha.text().toUpperCase();
    // 3. 生成唯一UUID作为Redis的Key(无时间/MAC信息,保证唯一性)
    String uuid = IdUtil.simpleUUID();
    // 4. 存入Redis:Key=uuid,Value=code,过期时间120秒(自动失效)
    redisTemplate.opsForValue().set(uuid,code,120, TimeUnit.SECONDS);
    // 5. 封装返回数据(生产环境禁止返回明文code)
    HashMap<String, String> r = new HashMap<>();
    r.put("uuid",uuid);       // 用于后续校验时获取Redis中的验证码
    r.put("code",code);       // 测试用,生产环境删除
    r.put("captcha",specCaptcha.toBase64()); // 验证码图片转Base64,前端直接渲染
    return Result.ok().put("data",r);
}
(2)关键知识点
  • SpecCaptcha:第三方验证码工具,支持自定义尺寸 / 位数,生成的图片可转 Base64 格式;
  • Redis 存储优势:利用过期特性避免验证码长期有效,UUID 作为 Key 防止重复;
  • 注意事项:生产环境仅返回 uuid 和 Base64 图片,不返回明文验证码,降低泄露风险。

3. 验证码二次校验逻辑

前端提交表单(如登录)时携带uuid和用户输入的验证码,后端校验步骤:

  1. 从请求参数中获取uuiduserInputCode
  2. 通过redisTemplate.opsForValue().get(uuid)获取 Redis 中存储的验证码;
  3. 对比userInputCode.toUpperCase()与存储的验证码:
    • 一致:校验通过,删除 Redis 中该 uuid 对应的 Key(防止重复使用);
    • 不一致 / Redis 无数据(过期 / 不存在):校验失败,返回提示信息。

二、JWT 令牌工具类(JwtUtil)

1. 功能说明

基于 JJWT 实现 JWT 令牌的生成与校验,用于用户验证码校验通过后颁发令牌,后续接口通过令牌完成身份校验,配置参数从配置文件解耦读取。

2. 核心配置

application.yml添加 JWT 相关配置:

复制代码
jwt:
  expire: 3600000   # 令牌过期时间(1小时,单位:毫秒)
  secret: abc123456789  # 签名密钥(生产环境建议使用复杂随机串)
  subject: community-user # 令牌主题(标识业务场景)

3. 核心代码

java 复制代码
@ConfigurationProperties(prefix = "jwt") // 读取配置文件中jwt前缀的配置
@Component
public class JwtUtil {
    // 配置属性:过期时间、签名密钥、令牌主题
    private long expire;
    private String secret;
    private String subject;

    /**
     * 生成JWT令牌
     * @param userId 自定义载荷:存储用户ID(可扩展用户名、角色等)
     * @return JWT令牌字符串
     */
    public String createToken(String userId) {
        return Jwts.builder()
                // 自定义载荷:存储业务标识
                .claim("userId", userId)
                // 标准载荷:令牌主题
                .setSubject(subject)
                // 标准载荷:过期时间(当前时间+配置的过期时长)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                // 标准载荷:令牌ID(UUID保证唯一性)
                .setId(UUID.randomUUID().toString())
                // 签名算法:HS256 + 签名密钥(防止令牌篡改)
                .signWith(SignatureAlgorithm.HS256, secret)
                // 组装为JWT字符串
                .compact();
    }

    /**
     * 校验令牌有效性
     * @param token 待校验的令牌
     * @return true-有效,false-无效(过期/篡改/格式错误)
     */
    public boolean checkToken(String token){
        if(StringUtils.isEmpty(token)){
            return false;
        }
        try {
            // 解析令牌:验证签名和过期时间,失败则抛出异常
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        } catch (Exception e) {
            // 捕获所有异常(过期、签名错误等),判定令牌无效
            return false;
        }
        return true;
    }

    // getter/setter:用于@ConfigurationProperties注入配置
    public long getExpire() { return expire; }
    public void setExpire(long expire) { this.expire = expire; }
    public String getSecret() { return secret; }
    public void setSecret(String secret) { this.secret = secret; }
    public String getSubject() { return subject; }
    public void setSubject(String subject) { this.subject = subject; }
}

4. 关键

  • @ConfigurationProperties:实现配置参数解耦,避免硬编码;

  • JWT 结构:由头部(算法)、载荷(自定义 / 标准信息)、签名(防篡改)三部分组成;

  • 令牌校验:解析时捕获所有异常(过期、签名错误、格式非法),统一判定为令牌无效;

  • 扩展建议:新增方法从令牌中解析 userId,示例:

    java 复制代码
    public String getUserIdFromToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return claims.get("userId", String.class);
    }

三、完整流程与注意事项

1. 流程

用户登录场景:

  1. 前端请求/captcha接口,获取 uuid 和 Base64 验证码图片,展示给用户;
  2. 用户输入验证码,前端携带uuid用户输入的验证码账号密码提交登录请求;
  3. 后端通过 uuid 从 Redis 获取存储的验证码,对比用户输入内容,校验通过后调用 JwtUtil 生成令牌;
  4. 后端返回 JWT 令牌给前端,前端存储令牌(如 localStorage);
  5. 后续前端请求需认证的接口时,携带令牌,后端通过 JwtUtil 校验令牌有效性,解析 userId 完成身份认证。

2. 注意事项

  • Redis:确保redis-server.exe持续运行,配置与实际环境一致;
  • 验证码:生产环境禁止返回明文 code,Redis 存储的验证码需及时删除(校验通过后);
  • JWT:签名密钥需保密,过期时间不宜过长,可结合刷新令牌机制优化;
  • 异常排查:验证码加载失败需检查 Redis 是否启动、配置是否正确;JWT 校验失败需检查签名密钥、令牌是否过期。
相关推荐
qq_3345635515 小时前
golang如何实现SSTable持久化_golang SSTable持久化实现要点
jvm·数据库·python
2301_7775993715 小时前
Redis怎样应对大规模集群的重启风暴_分批次重启节点并等待集群状态恢复绿灯后再继续操作
jvm·数据库·python
一只小白00015 小时前
Redis 常用命令总结
数据库·redis·缓存
逻辑驱动的ken15 小时前
Java高频面试考点场景题09
java·开发语言·数据库·算法·oracle·哈希算法·散列表
解救女汉子15 小时前
MySQL并发写入如何避免锁竞争_使用队列缓冲与批量插入优化
jvm·数据库·python
qq_3422958215 小时前
HTML函数开发需要SSD吗_SSD对HTML函数开发效率影响【详解】
jvm·数据库·python
qq_4327036615 小时前
Golang怎么用embed嵌入SQL文件_Golang如何将SQL迁移文件嵌入Go程序统一管理【技巧】
jvm·数据库·python
Han.miracle15 小时前
Redis 全套笔记:基础 API + 三大架构 + 缓存三大问题
java·windows·redis
m0_6403093015 小时前
如何将 sticky 元素精确定位到父容器的右上角
jvm·数据库·python
m0_3776182316 小时前
c++如何将双精度浮点数以科学计数法写入文件_scientific标志【详解】
jvm·数据库·python