Java生成验证码核心是用BufferedImage和Graphics2D自制图像:选52个易识别字符生成4–5位码,存入session的"captcha_code";绘图时加错切、噪点、随机灰度色,尺寸120×40;响应头禁用缓存,输出PNG并flush。
Java 中生成简单验证码,核心是「随机选字符 + 绘制成图」,不是靠第三方库硬堆功能,而是用
和
自己画。关键不在“能不能”,而在“怎么避免被轻易识别”和“怎么让
前端
能拿到”。
用
生成 4–6 位字母数字组合
别用
,它不支持设置种子,不利于测试;也别直接拼接
然后
—— 默认会包含易混淆字符(如 0/O、1/l/I),实际部署时容易被用户投诉。
推荐字符集:去掉
,
,
,
,
,剩下约 52 个安全字符
长度固定为 4 或 5 位,太短不安全,太长用户输错率飙升
务必在生成后把验证码字符串存入
,键名统一用
,前端校验时才对得上
用
绘制带干扰的图片
纯白底 + 黑字的图,OCR 一扫一个准。必须加干扰:噪点、斜线、轻微扭曲、字符间距随机化。但注意——
直接旋转会导致字符边缘锯齿严重,且坐标难算;更稳妥的是用
(错切)+
微调位置。
图片尺寸建议设为
,
,留出左右边距,避免字符贴边
每画一个字符前,用
随机灰度色,别全黑
画完字符后,用双重循环在图上随机打 30–50 个单像素点(
),颜色从背景色附近扰动
Servlet 中输出 PNG 并禁用缓存
很多人写完发现
浏览器
反复加载同一张图,或者验证码明明换了但前端还显示旧的——八成是没设响应头。
只管 MIME 类型,不管缓存逻辑。
Comet AI 浏览器
下载 Comet AI 浏览器,体验由 Perplexity AI 驱动的革命性上网方式。内置 AI 助手可实时总结网页、跨标签页对比信息、自动执行任务。告别繁琐操作,让 AI 成为你的浏览副驾,大幅提升研究与工作效率。支持 Windows、macOS、Android 和 iOS。
下载
立即学习
“
Java免费学习笔记(深入)
”;
必须加上:
同时加:
和
输出流要用
,别用
或手动转 Base64
最后务必调用
,否则部分容器(如 Tomcat 9+)可能卡住不返回
}
真正难的不是画图,是让这个图在 Nginx 反向代理后仍不被缓存、在移动端缩放时不糊、在 IE11 下也能正常渲染——这些细节不写进代码里,上线后就会变成凌晨三点的告警。
BufferedImageGraphics2DRandomMath.random()"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"charAt(rand.nextInt(len))'0''O''1''l''I'HttpSession"captcha_code"BufferedImageGraphics2D.rotate()sheartranslatewidth=120height=40g2d.setColor(new Color(50 + rand.nextInt(100), 50 + rand.nextInt(100), 50 + rand.nextInt(100)))g2d.fillRect(x, y, 1, 1)response.setContentType("image/png")response.setHeader("Cache-Control", "no-store")response.setHeader("Pragma", "no-cache")response.setDateHeader("Expires", 0)ImageIO.write(bufferedImage, "png", response.getOutputStream())toString()response.getOutputStream().flush()protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession(true);
String code = generateCaptchaText(); // 自定义方法
session.setAttribute("captcha_code", code);
BufferedImage image = new BufferedImage(120, 40, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, 120, 40);
Random rand = new Random();
for (int i = 0; i < code.length(); i++) {
g2d.setColor(new Color(50 + rand.nextInt(100), 50 + rand.nextInt(100), 50 + rand.nextInt(100)));
g2d.setFont(new Font("Arial", Font.BOLD, 24));
g2d.translate(5 + i * 22, 28);
g2d.shear(0.1 - rand.nextDouble() * 0.2, 0);
g2d.drawString(code.charAt(i) + "", 0, 0);
g2d.shear(0, 0);
g2d.translate(-5 - i * 22, -28);
}
for (int i = 0; i < 40; i++) {
int x = rand.nextInt(120);
int y = rand.nextInt(40);
g2d.setColor(new Color(rand.nextInt(100), rand.nextInt(100), rand.nextInt(100)));
g2d.fillRect(x, y, 1, 1);
}
g2d.dispose();
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
ImageIO.write(image, "png", response.getOutputStream());
response.getOutputStream().flush();