验证码图片生成必须使用 BufferedImage 而非字符串拼接,因其支持抗 OCR 的图形化输出、无需 GUI 环境、可精确控制字体/颜色/偏移,并需配合 SecureRandom、合理字符集、正确响应头与 Session 规范校验。

验证码图片生成要用 BufferedImage 而不是普通字符串拼接
很多人误以为生成验证码就是随机选几个字符再转成字符串,但实际生产环境必须输出图片(防止 OCR 直接识别纯文本)。BufferedImage 是 Java AWT 中最轻量、无需 GUI 环境也能工作的图像操作类。不依赖 Swing 组件,适合 Web 后端部署。
- 必须用
BufferedImage.TYPE_INT_RGB类型,避免透明通道引发的渲染异常 - 字体要显式设置,如
new Font("Arial", Font.BOLD, 24),否则默认字体在 Linux 服务器上可能缺失,导致空白图 - 每字符需错位绘制(x 坐标加随机偏移),否则容易被简单模板匹配破解
随机字符生成别直接用 Math.random(),改用 SecureRandom 防 predictable output
Math.random() 是伪随机、可预测,攻击者若知道生成时间或种子,能复现验证码序列。登录页、注册页等关键入口必须用密码学安全的随机源。
- 用
SecureRandom.getInstanceStrong()获取强随机实例(JDK 8+) - 字符集建议排除易混淆字符:去掉
'0'、'O'、'l'、'I',推荐用"23456789ABCDEFGHJKLMNPQRSTUVWXYZ" - 每个字符独立调用
secureRandom.nextInt(charset.length()),不要用random.ints()批量生成后截断——可能引入偏差
Servlet 中输出验证码图片要设对 Content-Type 和缓存头
浏览器不显示图片?大概率是响应头没设对。验证码是临时一次性资源,必须禁用缓存,否则用户刷新页面时看到旧图,或 F5 后仍用同一张图校验。
- 必须设置
response.setContentType("image/png") - 添加禁用缓存头:
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") - 务必调用
response.getOutputStream()写入,别用getWriter()(会报IllegalStateException)
ImageIO.write(bufferedImage, "png", response.getOutputStream());
校验时注意大小写与空格处理,Session 存储值要 trim 并转大写
用户输入常带首尾空格,或用小写提交,而生成时用了大写字母集。前后端不一致就永远校验失败。
立即学习“Java免费学习笔记(深入)”;
真正难的不是画图或随机,是让每次请求都隔离、不可预测、不可重放——SecureRandom 初始化开销、BufferedImage 的内存释放、Session 生命周期管理,这些细节出错,验证码就形同虚设。










