news 2026/5/1 21:48:02

Spring Boot 使用 Hutool快速集成验证码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot 使用 Hutool快速集成验证码

还在手撸验证码?Spring Boot 从自定义实现到 Hutool,一篇带你彻底搞定

验证码这件“小事”,其实一点也不小

在登录、注册、找回密码这些看似普通的业务场景里,验证码往往是系统安全的第一道防线。如果没有它,爬虫、撞库脚本、暴力请求可以轻松把你的接口打穿;但如果验证码实现得不好,又会带来代码冗余、维护困难、样式僵硬等一系列工程问题。

很多项目都会经历这样一个过程:

  • 一开始:“不就是个验证码吗?我自己画!”
  • 后来:干扰线、字体、随机数、Session、缓存控制……越写越多
  • 最终:发现成熟工具库早就帮你把坑填平了

这篇文章就围绕一个核心问题展开:Spring Boot 项目里,验证码到底该怎么实现,才是又稳又省事?

我们会完整对比两种方案:

  1. 完全自定义实现验证码生成逻辑(适合高度定制化需求)
  2. 基于 Hutool 快速集成多种验证码类型(绝大多数项目的最优解)

为什么图形验证码几乎不可或缺?

验证码的本质不是“折磨用户”,而是区分人和程序

在以下场景中,它几乎是必选项:

  • 登录接口防止暴力破解
  • 注册接口防止批量刷号
  • 密码重置防止恶意撞库
  • 重要操作前的人机确认

通过随机字符 + 干扰元素的方式,验证码能显著提高脚本攻击成本,是最基础、也是最有效的安全措施之一。

实现方式上,常见只有两条路:

  1. 自己写一套验证码生成逻辑
  2. 直接集成成熟工具库

下面我们一条一条拆。


方案一:完全手写一个验证码工具类

如果你的项目对验证码样式、字体、干扰规则有非常强的定制需求,那么手写一套逻辑是不可避免的。

验证码工具类设计思路

我们需要完成几件事:

  1. 随机生成验证码字符
  2. 绘制背景
  3. 绘制干扰线
  4. 绘制字符
  5. 将验证码结果存入 Session
  6. 将图片输出到响应流

项目目录结构

src/main/java └── com └── icoderoad └── common └── util └── CaptchaCodeUtil.java

自定义验证码工具类实现

packagecom.icoderoad.common.util;importjavax.imageio.ImageIO;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importjava.awt.*;importjava.awt.image.BufferedImage;importjava.util.Random;publicclassCaptchaCodeUtil{publicstaticfinalStringSESSION_KEY="CAPTCHA_CODE";privatefinalRandomrandom=newRandom();privatefinalStringcodeSource="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";privateintwidth=80;privateintheight=26;privateintcodeLength=4;privateintlineCount=40;privateFontgetFont(){returnnewFont("Fixedsys",Font.PLAIN,18);}privateColorrandomColor(intmin,intmax){min=Math.min(min,255);max=Math.min(max,255);intr=min+random.nextInt(max-min);intg=min+random.nextInt(max-min);intb=min+random.nextInt(max-min);returnnewColor(r,g,b);}privatevoiddrawLine(Graphicsg){intx=random.nextInt(width);inty=random.nextInt(height);intxl=random.nextInt(12);intyl=random.nextInt(12);g.drawLine(x,y,x+xl,y+yl);}privateStringdrawChar(Graphicsg,intindex){g.setFont(getFont());g.setColor(randomColor(50,160));Stringch=String.valueOf(codeSource.charAt(random.nextInt(codeSource.length())));g.drawString(ch,15*index,18);returnch;}publicvoidgenerate(HttpServletRequestrequest,HttpServletResponseresponse){HttpSessionsession=request.getSession();BufferedImageimage=newBufferedImage(width,height,BufferedImage.TYPE_INT_RGB);Graphicsg=image.getGraphics();g.setColor(Color.WHITE);g.fillRect(0,0,width,height);for(inti=0;i<lineCount;i++){g.setColor(randomColor(120,200));drawLine(g);}StringBuildercode=newStringBuilder();for(inti=1;i<=codeLength;i++){code.append(drawChar(g,i));}session.setAttribute(SESSION_KEY,code.toString());g.dispose();try{ImageIO.write(image,"JPEG",response.getOutputStream());}catch(Exceptione){thrownewRuntimeException("生成验证码失败",e);}}}

Controller 中调用验证码工具类

packagecom.icoderoad.web.controller;importcom.icoderoad.common.util.CaptchaCodeUtil;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;@RestControllerpublicclassCaptchaController{@GetMapping("/captcha/custom")publicvoidcustomCaptcha(HttpServletRequestrequest,HttpServletResponseresponse){response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-cache");response.setHeader("Pragma","no-cache");response.setDateHeader("Expires",0);newCaptchaCodeUtil().generate(request,response);}}

⚠️这种方式完全可控,但维护成本高、扩展困难


方案二:使用 Hutool 快速集成验证码(强烈推荐)

如果你不想把时间浪费在“重复造轮子”上,Hutool 基本就是最优解。

引入依赖

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-captcha</artifactId><version>5.8.6</version></dependency>

Hutool 四种验证码实战示例

统一 Controller 路径示例:com/icoderoad/web/controller/HutoolCaptchaController.java

1. 线条干扰验证码(LineCaptcha)
@GetMapping("/captcha/line")publicvoidline(HttpServletResponseresponse)throwsIOException{LineCaptchacaptcha=CaptchaUtil.createLineCaptcha(130,38,5,5);response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-cache");captcha.write(response.getOutputStream());}
2. 圆圈干扰验证码(CircleCaptcha)
@GetMapping("/captcha/circle")publicvoidcircle(HttpServletResponseresponse)throwsIOException{CircleCaptchacaptcha=CaptchaUtil.createCircleCaptcha(130,38,5,20);response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-cache");captcha.write(response.getOutputStream());}
3. 扭曲验证码(ShearCaptcha)
@GetMapping("/captcha/shear")publicvoidshear(HttpServletResponseresponse)throwsIOException{ShearCaptchacaptcha=CaptchaUtil.createShearCaptcha(130,38,5,5);response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-cache");captcha.write(response.getOutputStream());}
4. GIF 动态验证码(GifCaptcha)
@GetMapping("/captcha/gif")publicvoidgif(HttpServletResponseresponse)throwsIOException{GifCaptchacaptcha=CaptchaUtil.createGifCaptcha(130,38,5);response.setContentType("image/jpeg");response.setHeader("Cache-Control","no-cache");captcha.write(response.getOutputStream());}

自定义验证码内容(数字 / 算术)

纯数字验证码
@GetMapping("/captcha/number")publicvoidnumber(HttpServletResponseresponse)throwsIOException{RandomGeneratorgenerator=newRandomGenerator("0123456789",4);LineCaptchacaptcha=CaptchaUtil.createLineCaptcha(200,80);captcha.setGenerator(generator);captcha.createCode();captcha.write(response.getOutputStream());}
算术验证码(推荐)
@GetMapping("/captcha/math")publicvoidmath(HttpServletResponseresponse,HttpSessionsession)throwsIOException{ShearCaptchacaptcha=CaptchaUtil.createShearCaptcha(200,45,4,4);captcha.setGenerator(newMathGenerator());captcha.createCode();session.setAttribute("MATH_CODE",captcha.getCode());captcha.write(response.getOutputStream());}

接口测试建议(Apifox)

  • 使用 GET 请求访问验证码接口
  • 响应应为图片而非乱码
  • 多次刷新验证码应变化
  • 若不变化,优先检查缓存头设置

关键实践建议

  1. 验证码值必须存 Session / Redis
  2. 验证成功后立即清除
  3. 验证码接口应限流
  4. 高安全场景可叠加短信 / IP 风控

结语:验证码不是炫技,而是工程取舍

  • 需要极致定制→ 自定义实现
  • 追求效率与稳定→ Hutool 一步到位

在真实项目里,99% 的情况你都不需要自己画验证码。把精力留给真正有价值的业务逻辑,才是成熟工程师的选择。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 0:37:45

DeepSeek是如何思考的?一文读懂大模型的工作原理

DeepSeek&#xff08;以及所有类似的大模型&#xff09;在回答你的问题时&#xff0c;并不是像搜索引擎那样去“调取”或“查询”一个后台数据库。 它不会去翻阅它训练时看过的资料&#xff0c;然后找出一段话复制给你。它的工作方式要更加神奇和复杂。 一、DeepSeek是如何“回…

作者头像 李华
网站建设 2026/5/1 3:46:16

从自测到部署:DMXAPI如何为AI编程提供全方位的技术保障?

前言&#xff1a;AI编程的“死亡之谷”与技术护城河2026年&#xff0c;全球AI应用开发成功率出现了一个令人深思的数据&#xff1a;从概念验证到生产部署&#xff0c;仅有18%的AI项目能够成功跨越这个“死亡之谷”。那些失败的82%&#xff0c;并非因为创意不足或技术不先进&…

作者头像 李华
网站建设 2026/5/1 15:30:42

Linux 内存映射与显存操作深度解析

Linux 内存映射与显存操作深度解析 一、内存映射基础概念 1. 什么是内存映射&#xff1f; 内存映射&#xff08;Memory Mapping&#xff09;是一种将文件或设备直接映射到进程地址空间的技术&#xff0c;使得应用程序可以像访问内存一样访问文件或设备。通过 mmap() 系统调用&a…

作者头像 李华
网站建设 2026/5/1 12:17:53

柑橘叶片病害数据集(5类)

该数据集包含健康的柑橘叶和4类柑橘叶病Citrus_Canker_Diseases_Leaf_Orange&#xff1a;拥有11248张照片Citrus_Nutrient_Deficiency_Yellow_Leaf_Orange&#xff1a;拥有12800张照片Healthy_Leaf_Orange&#xff1a;6384张照片Multiple_Diseases_Leaf_Orange:4800张照片Young…

作者头像 李华
网站建设 2026/5/1 17:05:44

程序员有必要参加软考吗?

程序员有必要参加软考吗&#xff1f; 答案是&#xff1a;大多数情况下没有必要&#xff0c;但少数特定情况下非常有必要&#xff0c;甚至是“不得不考”。 下面从不同角度给你一个比较清晰、务实的判断框架&#xff0c;帮助你自己决定要不要考。 一、先看“没必要考”的主要…

作者头像 李华
网站建设 2026/5/1 10:13:56

数字校园气象站 示范校园气象站

问&#xff1a;这款气象站的核心定位是什么&#xff1f;和普通气象站相比&#xff0c;专为校园环境设计的亮点体现在哪里&#xff1f; 答&#xff1a;核心定位是校园专属气象科普与微气候监测设备&#xff0c;主打“安全、坚固、易操作、低维护”&#xff0c;核心区别于普通气象…

作者头像 李华