应使用graphics2d类,它支持抗锯齿、透明度和旋转文字,而graphics不适用于bufferedimage验证码生成,且需设置渲染提示并调用dispose()防止内存泄漏。

Java生成验证码图片要用哪个类?Graphics还是Graphics2D?
直接用 Graphics2D,别碰老掉牙的 Graphics。前者支持抗锯齿、透明度、旋转文字,后者连中文乱码都难处理。很多教程还在教 getGraphics(),那是 Swing 组件内部绘图用的,不适合生成 BufferedImage 验证码。
实操建议:
- 用
BufferedImage创建画布,再调createGraphics()拿到Graphics2D实例 - 务必调
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON),否则字母“a”“o”边缘发虚,用户识别困难 - 记得最后调
g2d.dispose(),不然频繁生成会内存泄漏——这是线上服务最常被忽略的一点
随机字符怎么选才不容易看错?
别用全字母数字(0O1lI),用户手机上根本分不清。真实场景下,输入错误率飙升的根源就在这儿。
推荐字符集:"23456789ABCDEFGHJKMNPQRSTUVWXYZ"(去掉 0、1、O、l、I)
立即学习“Java免费学习笔记(深入)”;
常见错误现象:用户反复提交“识别失败”,后台日志里全是 IllegalArgumentException: char not in charset,其实是前端传了空字符串或非法字符回来校验。
使用场景注意点:
- 服务端生成时存进 session 或 Redis,key 建议带时间戳前缀,避免并发覆盖
- 字符长度固定 4–5 位,太短不安全,太长用户放弃识别
- 如果要做大小写混合,必须禁用小写
l和i,哪怕牺牲一点熵值
为什么验证码图片总被浏览器缓存?
不是代码问题,是 HTTP 响应头没设对。浏览器看到相同 URL 就直接读缓存,用户刷半天还是同一张图。
关键操作在 Servlet 或 Spring MVC 的响应设置:
- 加响应头:
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") - 加
response.setDateHeader("Expires", 0),IE8 以下也认这个 - URL 后面拼个时间戳参数(如
?t=1715823492)只是掩耳盗铃,真正起作用的是响应头
性能影响:这些头不会拖慢生成速度,但漏设会导致大量无效请求打到后端,压测时容易误判为“验证码模块瓶颈”。
Base64 直接嵌入 HTML 安不安全?
不安全,且没必要。把整个图片转成 Base64 字符串塞进 <img src="data:image/png;base64,..." alt="怎么用Java实现简单的验证码生成器_图形处理与随机字符" >,看似省了一次请求,实则埋了三个坑:
- Base64 编码后体积膨胀约 33%,移动端加载更慢
- 无法利用浏览器缓存机制,每次刷新都重传整张图
- 验证码字符串若也随 Base64 一起暴露在 HTML 源码里(比如藏在注释或 data-* 属性中),等于白做
正确做法:接口返回 JSON,含 image_url(指向一个带唯一 token 的临时图片地址)和 captcha_id(用于后续校验),两者分离,各司其职。
容易被忽略的地方:token 过期时间要短(建议 2–3 分钟),且验证成功后立即失效——有人截包重放同一个 token 多次校验,就是卡在这儿。










