验证码图片生成必须使用 BufferedImage 而非字符串拼接,因其支持抗 OCR 的图形化输出、无需 GUI 环境、可精确控制字体/颜色/偏移,并需配合 SecureRandom、合理字符集、正确响应头与 Session 规范校验。
很多人误以为生成验证码就是随机选几个字符再转成字符串,但实际生产环境必须输出图片(防止 OCR 直接识别纯文本)。BufferedImage 是 Java AWT 中最轻量、无需 GUI 环境也能工作的图像操作类。不依赖 Swing 组件,适合 Web 后端部署。
BufferedImage.TYPE_INT_RGB 类型,避免透明通道引发的渲染异常new Font("Arial", Font.BOLD, 24),否则默认字体在 Linux 服务器上可能缺失,导致空白图Math.random() 是伪随机、可预测,攻击者若知道生成时间或种子,能复现验证码序列。登录页、注
册页等关键入口必须用密码学安全的随机源。
SecureRandom.getInstanceStrong() 获取强随机实例(JDK 8+)'0'、'O'、'l'、'I',推荐用 "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
secureRandom.nextInt(charset.length()),不要用 random.ints() 批量生成后截断——可能引入偏差浏览器不显示图片?大概率是响应头没设对。验证码是临时一次性资源,必须禁用缓存,否则用户刷新页面时看到旧图,或 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());
用户输入常带首尾空格,或用小写提交,而生成时用了大写字母集。前后端不一致就永远校验失败。
HttpSession 前,执行 captchaText.trim().toUpperCase()
request.getParameter("code").trim().toUpperCase()
"captcha_token_" + System.currentTimeMillis(),避免多标签页覆盖