上一篇
验证码的java代码怎么写
- 后端开发
- 2025-08-11
- 42
可定义含数字字母的字符池,用
Random选指定位数字符拼接成字符串,如`String code = “”; for(int i=0;i
以下是关于 Java实现验证码功能 的完整指南,涵盖原理、代码实现、参数调优及常见问题解答,本文采用模块化叙述方式,结合代码注释与表格对比,帮助您快速掌握核心要点。
验证码基础原理
验证码本质是通过程序动态生成「人眼可识别但机器难以解析」的图形化文本,其核心目标在于区分人类用户与自动化脚本(防爬虫/刷票),典型特征包括:
随机性:每次请求返回不同内容
视觉干扰:背景噪点、干扰线、像素扭曲
字符变形:旋转、缩放、颜色渐变
安全边界:限制尝试次数、绑定Session/Token

| 特性 | 作用 | 实现方式 |
|---|---|---|
| 随机字符串 | 基础防伪要素 | RandomStringUtils工具类 |
| 图像渲染 | 可视化载体 | BufferedImage + Graphics2D |
| 干扰元素 | 破坏机器识别能力 | 噪声点、折线、弧线 |
| 字符扭曲 | 阻止光学字符识别(OCR) | 仿射变换、路径偏移 |
| 时效控制 | 防止重放攻击 | 存入HttpSession并设置过期时间 |
完整代码实现(基于Servlet)
核心依赖包
import javax.imageio.ImageIO; import javax.servlet.http.; import java.awt.; import java.awt.image.BufferedImage; import java.util.Random;
主服务类 CaptchaServlet.java
public class CaptchaServlet extends HttpServlet {
private static final int WIDTH = 120; // 图片宽度
private static final int HEIGHT = 40; // 图片高度
private static final String[] CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789"; // 去除非字母数字字符
private static final int LENGTH = 4; // 验证码长度
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 创建内存中的图片缓冲区
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// ======== 第一步:绘制背景 ========
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
// 添加渐变背景增强干扰
int widthRange = WIDTH / 2;
for (int i = 0; i < 10; i++) {
int x = new Random().nextInt(widthRange);
int y = new Random().nextInt(HEIGHT);
g.setColor(new Color(200 + i 10, 200 + i 10, 200 + i 10));
g.drawOval(x, y, 2 + i, 2 + i);
}
// ======== 第二步:生成随机字符 ========
StringBuilder captchaText = new StringBuilder();
Font font = new Font("Arial", Font.BOLD, 28); // 初始字体大小
g.setFont(font);
// 精确计算字符间距
FontMetrics fm = g.getFontMetrics();
int charWidth = fm.charWidth('A');
int totalWidth = (LENGTH 1) charWidth;
int startX = (WIDTH totalWidth) / 2;
for (int i = 0; i < LENGTH; i++) {
// 随机选择字符
char c = CHARS[new Random().nextInt(CHARS.length)];
captchaText.append(c);
// 设置随机颜色(明度>128保证可读性)
g.setColor(new Color(
30 + new Random().nextInt(128),
30 + new Random().nextInt(128),
30 + new Random().nextInt(128)
));
// 应用旋转变换(-30°~30°)
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians((new Random().nextInt(60) 30)), startX + i charWidth, HEIGHT/2);
g.setTransform(transform);
// 绘制字符(带轻微垂直偏移)
g.drawString(String.valueOf(c), startX + i charWidth, HEIGHT/2 + (new Random().nextInt(10) 5));
g.setTransform(new AffineTransform()); // 重置变换矩阵
}
// ======== 第三步:添加干扰元素 ========
// 绘制干扰线
for (int i = 0; i < 8; i++) {
g.setColor(new Color(new Random().nextInt(255), new Random().nextInt(255), new Random().nextInt(255), 150));
int x1 = new Random().nextInt(WIDTH);
int y1 = new Random().nextInt(HEIGHT);
int x2 = new Random().nextInt(WIDTH);
int y2 = new Random().nextInt(HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
// 添加噪点
for (int i = 0; i < 50; i++) {
int x = new Random().nextInt(WIDTH);
int y = new Random().nextInt(HEIGHT);
g.setColor(new Color(new Random().nextInt(255), new Random().nextInt(255), new Random().nextInt(255)));
g.fillRect(x, y, 1, 1);
}
// ======== 第四步:保存到Session并输出 ========
HttpSession session = request.getSession();
session.setAttribute("CAPTCHA_CODE", captchaText.toString());
// 设置响应头
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ImageIO.write(image, "jpeg", response.getOutputStream());
}
}
web.xml配置
<servlet>
<servlet-name>CaptchaServlet</servlet-name>
<servlet-class>com.example.CaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CaptchaServlet</servlet-name>
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
关键参数详解与调优建议
| 参数名 | 默认值 | 作用说明 | 调优方向 |
|---|---|---|---|
WIDTH/HEIGHT |
120×40 | 图片尺寸 | 根据UI设计调整,推荐比例3:1 |
LENGTH |
4 | 验证码位数 | 平衡安全性与用户体验 |
CHARS |
排除易混淆字符 | 可选字符集 | 可根据业务需求增减特殊符号 |
| 干扰线数量 | 8 | 影响机器识别难度 | 5-15条为宜 |
| 噪点密度 | 50 | 背景噪声强度 | 30-80个像素点 |
| 字体大小 | 28px | 字符清晰度 | 需配合FontMetrics自动计算 |
| 有效期 | 无 | 未显式设置会导致永久有效 | 应在Session中设置超时时间 |
最佳实践建议:
- 双重校验机制:除前端展示外,后端仍需验证提交的验证码是否匹配Session中的值
- 频率限制:同一IP短时间内多次失败应触发临时封禁
- 多因子认证:高风险操作建议结合短信/邮箱二次验证
- 移动端适配:减小图片尺寸至80×30px,增大字符间距
前端调用示例(HTML+JS)
<img id="captchaImg" src="/captcha" onclick="refreshCaptcha()" alt="点击刷新验证码">
<input type="text" id="captchaInput" placeholder="请输入验证码">
<script>
function refreshCaptcha() {
document.getElementById('captchaImg').src = '/captcha?' + Math.random();
document.getElementById('captchaInput').value = '';
}
</script>
相关问答FAQs
Q1: 生成的验证码包含中文字符时严重变形怎么办?
A: 中文字符由于笔画复杂,传统仿射变换会导致过度失真,解决方案有两种:

- 专用字体方案:使用支持中文且字形稳定的字体文件(如微软雅黑),禁用旋转仅保留平移和缩放
- 分段渲染:将每个汉字单独渲染到独立图层,再合并到主画布
示例修改代码片段:// 替换原有字符绘制逻辑 if (Character.UnicodeScript.of(c) == UnicodeScript.HAN) { // 中文字符特殊处理 g.setFont(new Font("Microsoft YaHei", Font.PLAIN, 24)); g.drawString(String.valueOf(c), xPos, yPos); } else { // 英文数字正常处理 ... }
Q2: 如何提升验证码的安全性等级?
A: 可通过以下组合策略增强安全性:
| 安全层级 | 实施措施 | 效果评估 |
|———-|————————————————————————–|——————————|
| 基础级 | 现有实现 + Session绑定 | 防御简单机器人 |
| 中级 | 增加行为轨迹记录(鼠标移动路径) | 拦截普通自动化工具 |
| 高级 | 加入语义理解验证(选出包含汽车的图片) | 有效对抗专业打码平台 |
| 专家级 | 设备指纹识别 + IP信誉库校验 | 精准识别反面流量来源 |

实际部署时应遵循「最小必要原则」,根据业务风险等级选择合适的安全策略,例如银行系统建议采用「中级+高级」组合,普通论坛使用基础级即可。
