验证码生成 + 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 校验失败需检查签名密钥、令牌是否过期。
相关推荐
电子小白1235 分钟前
第13期PCB layout工程师初级培训-1-EDA软件的通用设置
笔记·嵌入式硬件·学习·pcb·layout
Full Stack Developme1 小时前
数据库索引的原理及类型和应用场景
数据库
clorisqqq2 小时前
人工智能现代方法笔记 第1章 绪论(1/2)
人工智能·笔记
charlie1145141912 小时前
嵌入式现代C++教程: 构造函数优化:初始化列表 vs 成员赋值
开发语言·c++·笔记·学习·嵌入式·现代c++
IDC02_FEIYA2 小时前
SQL Server 2025数据库安装图文教程(附SQL Server2025数据库下载安装包)
数据库·windows
辞砚技术录2 小时前
MySQL面试题——联合索引
数据库·面试
萧曵 丶3 小时前
MySQL 主键不推荐使用 UUID 的深层原因
数据库·mysql·索引
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [fs]seq_file
linux·笔记·学习
小北方城市网3 小时前
分布式锁实战指南:从选型到落地,避开 90% 的坑
java·数据库·redis·分布式·python·缓存
liuchangng3 小时前
Open-AutoGLM部署运行笔记
笔记