发布时间:2026-02-28 作者:超级管理员 来源:办公室 点击次数: 次
若依(RuoYi)框架中图片验证码的完整实现逻辑、核心代码和定制化方法,我会从实现原理、核心代码拆解、关键配置、定制化改造四个维度讲解,覆盖验证码生成、存储、校验全流程,让你既能理解原理,也能按需修改验证码样式 / 规则。
若依最新前后端分离源码下载:
https://gitee.com/ruoyieleadmin/ruoyi-ele-admin
一、图片验证码核心实现原理
若依框架的图片验证码基于 Java 图形绘制 + Redis 存储 + 前端交互 实现,核心流程如下:
核心关键点:
-
验证码字符不直接返回前端,仅存储在 Redis,避免前端破解;
-
验证码图片添加干扰线、噪点、扭曲变形,提升安全性;
-
验证码设置短过期时间(默认 2 分钟),避免复用。
二、核心代码拆解(若依源码级解析)
若依的验证码功能集中在 ruoyi-common 模块的 captcha 包下,核心类如下:
1. 验证码生成核心类(CaptchaUtil.java)
负责生成随机字符、绘制验证码图片,是核心工具类:
java
package com.ruoyi.common.utils.captcha;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* 若依框架验证码生成工具类
*/
public class CaptchaUtil {
// 验证码字符集(排除易混淆字符:0/O、1/I等)
private static final char[] CODE_CHAR = { '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
// 验证码宽度/高度
private static final int WIDTH = 110;
private static final int HEIGHT = 38;
// 验证码字符个数
private static final int CODE_COUNT = 4;
// 干扰线数量
private static final int LINE_COUNT = 150;
/**
* 生成验证码图片+字符
* @return CaptchaResult:包含验证码字符、图片BufferedImage
*/
public static CaptchaResult generate() {
// 1. 创建图片缓冲区
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// 2. 设置背景色(白色)
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
// 设置字体(加粗、倾斜,增加识别难度)
g.setFont(new Font("Arial", Font.BOLD | Font.ITALIC, 24));
// 3. 生成随机验证码字符
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < CODE_COUNT; i++) {
char c = CODE_CHAR[random.nextInt(CODE_CHAR.length)];
code.append(c);
// 绘制字符(随机颜色、位置、旋转角度)
g.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)));
// 字符旋转(-30° ~ 30°)
g.rotate(Math.toRadians(random.nextInt(60) - 30), 25 * i + 10, HEIGHT / 2);
g.drawString(String.valueOf(c), 25 * i + 10, HEIGHT / 2);
// 旋转复位
g.rotate(-Math.toRadians(random.nextInt(60) - 30), 25 * i + 10, HEIGHT / 2);
}
// 4. 绘制干扰线(随机颜色、位置)
for (int i = 0; i < LINE_COUNT; i++) {
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
g.drawLine(random.nextInt(WIDTH), random.nextInt(HEIGHT),
random.nextInt(WIDTH), random.nextInt(HEIGHT));
}
// 5. 绘制噪点(增加干扰)
for (int i = 0; i < WIDTH * HEIGHT / 20; i++) {
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
g.fillRect(random.nextInt(WIDTH), random.nextInt(HEIGHT), 1, 1);
}
// 6. 释放资源
g.dispose();
// 返回结果:验证码字符、图片
return new CaptchaResult(code.toString(), image);
}
/**
* 验证码结果封装类
*/
public static class CaptchaResult {
private String code; // 验证码字符
private BufferedImage image; // 验证码图片
// 构造器、getter/setter
public CaptchaResult(String code, BufferedImage image) {
this.code = code;
this.image = image;
}
// ... 省略getter/setter
}
}
2. 验证码接口实现(CaptchaController.java)
提供前端访问的验证码接口,生成图片并存储到 Redis:
java
3. 验证码校验逻辑(登录接口示例)
用户登录时校验验证码,核心代码在 LoginController.java:
java
三、关键配置与注意事项
1. 核心配置(无需额外配置,若依默认支持)
-
Redis 配置:验证码依赖 Redis 存储,需确保 Redis 服务正常,配置正确(参考之前 Redis 配置章节);
-
跨域配置:若前端与后端跨域,需在
WebMvcConfig 中放行 /captchaImage 接口;
-
接口权限:
/captchaImage 接口需加入 Shiro/Spring Security 白名单,无需登录即可访问:
java
2. 安全性注意事项
-
字符集:若依已排除易混淆字符(0/O、1/I),避免用户识别错误;
-
过期时间:默认 2 分钟,不可过长(易被破解),也不可过短(用户来不及输入);
-
防刷新:可添加接口限流(参考 Redis 限流章节),限制
/captchaImage 接口的请求频率,避免恶意刷取验证码;
-
大小写:校验时忽略大小写(
equalsIgnoreCase),提升用户体验。
四、定制化改造(按需调整)
1. 修改验证码样式
-
调整尺寸:修改
CaptchaUtil 中 WIDTH(宽度)、HEIGHT(高度);
-
字符个数:修改
CODE_COUNT(如改为 6 位);
-
干扰强度:调整
LINE_COUNT(干扰线数量)、噪点数量;
-
字体 / 颜色:修改字体样式、字符颜色、背景色;
-
示例:改为 6 位数字验证码(仅保留数字字符):
java
2. 切换验证码类型(如算术验证码、滑块验证码)
若依默认是字符验证码,可扩展为算术验证码(适合低安全场景):
java
3. 对接第三方验证码(如极验、阿里云验证码)
若需更高安全级别的验证码(滑块、点选),可替换原生实现:
-
引入第三方验证码 SDK(如极验:
geetest-sdk-java);
-
修改
CaptchaController,调用第三方接口生成验证码参数;
-
登录接口中调用第三方校验接口,替换 Redis 校验逻辑。
五、常见问题及解决方法
1. 验证码图片无法显示
-
原因:图片 Base64 编码错误、跨域拦截、Redis 未启动;
-
解决:
-
检查 Redis 服务是否正常,验证码 UUID 是否存入 Redis;
-
检查前端
<img> 标签的 src 是否正确(格式:data:image/jpeg;base64,${imgBase64});
-
确认
/captchaImage 接口已加入跨域 / 权限白名单。
2. 验证码校验一直失败
-
原因:大小写未忽略、Redis 中验证码已过期、UUID 传递错误;
-
解决:
-
校验时使用
equalsIgnoreCase 忽略大小写;
-
检查 Redis 验证码过期时间,确保用户输入时未过期;
-
前端确认 UUID 和验证码是否正确传递到后端。
3. 验证码生成性能低
-
原因:图形绘制耗时、无缓存;
-
解决:
-
减少干扰线 / 噪点数量(降低绘制耗时);
-
对验证码图片进行缓存(非核心验证码场景);
-
异步生成验证码(高并发场景)。
总结
若依框架图片验证码的核心实现逻辑:
-
生成:通过 Java 图形 API 绘制带干扰的字符图片,生成随机字符;
-
存储:字符存入 Redis(UUID 为 key,设置短过期时间),图片转 Base64 返回前端;
-
校验:登录时通过 UUID 从 Redis 读取字符,与用户输入对比,校验后删除;
-
定制:可调整样式、切换验证码类型,或对接第三方高安全验证码。
核心优化点:生产环境需添加接口限流、防刷新机制,高并发场景可考虑异步生成或对接第三方验证码服务。
若依最新前后端分离源码下载:
https://gitee.com/ruoyieleadmin/ruoyi-ele-admin