Spring Boot使用Hutool快速集成验证码的两种方案

 更新时间:2026年02月10日 09:13:23   作者:❀͜͡傀儡师  
验证码作为一种简单而有效的身份验证手段,被广泛应用于各种在线服务中,这篇文章主要介绍了Spring Boot使用Hutool快速集成验证码的两种方案,文中通过代码介绍的非常详细,需要的朋友可以参考下

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

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

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

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

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

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

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

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

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

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

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

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

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

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

下面我们一条一条拆。

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

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

验证码工具类设计思路

我们需要完成几件事:

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

项目目录结构

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

自定义验证码工具类实现

package com.icoderoad.common.util;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

public class CaptchaCodeUtil {
    
    public static final String SESSION_KEY = "CAPTCHA_CODE";
    
    private final Random random = new Random();
    private final String codeSource = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    private int width = 80;
    private int height = 26;
    private int codeLength = 4;
    private int lineCount = 40;
    
    private Font getFont() {
        return new Font("Fixedsys", Font.PLAIN, 18);
    }
    
    private Color randomColor(int min, int max) {
        min = Math.min(min, 255);
        max = Math.min(max, 255);
        int r = min + random.nextInt(max - min);
        int g = min + random.nextInt(max - min);
        int b = min + random.nextInt(max - min);
        return new Color(r, g, b);
    }
    
    private void drawLine(Graphics g) {
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        int xl = random.nextInt(12);
        int yl = random.nextInt(12);
        g.drawLine(x, y, x + xl, y + yl);
    }
    
    private String drawChar(Graphics g, int index) {
        g.setFont(getFont());
        g.setColor(randomColor(50, 160));
        String ch = String.valueOf(codeSource.charAt(random.nextInt(codeSource.length())));
        g.drawString(ch, 15 * index, 18);
        return ch;
    }
    
    public void generate(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        
        for (int i = 0; i < lineCount; i++) {
            g.setColor(randomColor(120, 200));
            drawLine(g);
        }
        
        StringBuilder code = new StringBuilder();
        for (int i = 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 (Exception e) {
            throw new RuntimeException("生成验证码失败", e);
        }
    }
}

Controller 中调用验证码工具类

package com.icoderoad.web.controller;

import com.icoderoad.common.util.CaptchaCodeUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
public class CaptchaController {
    
    @GetMapping("/captcha/custom")
    public void customCaptcha(HttpServletRequest request, HttpServletResponse response) {
        response.setContentType("image/jpeg");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
        
        new CaptchaCodeUtil().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")
public void line(HttpServletResponse response) throws IOException {
    LineCaptcha captcha = 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")
public void circle(HttpServletResponse response) throws IOException {
    CircleCaptcha captcha = 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")
public void shear(HttpServletResponse response) throws IOException {
    ShearCaptcha captcha = 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")
public void gif(HttpServletResponse response) throws IOException {
    GifCaptcha captcha = CaptchaUtil.createGifCaptcha(130, 38, 5);
    response.setContentType("image/jpeg");
    response.setHeader("Cache-Control", "no-cache");
    captcha.write(response.getOutputStream());
}

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

纯数字验证码

@GetMapping("/captcha/number")
public void number(HttpServletResponse response) throws IOException {
    RandomGenerator generator = new RandomGenerator("0123456789", 4);
    LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 80);
    captcha.setGenerator(generator);
    captcha.createCode();
    captcha.write(response.getOutputStream());
}

算术验证码(推荐)

@GetMapping("/captcha/math")
public void math(HttpServletResponse response, HttpSession session) throws IOException {
    ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 45, 4, 4);
    captcha.setGenerator(new MathGenerator());
    captcha.createCode();
    session.setAttribute("MATH_CODE", captcha.getCode());
    captcha.write(response.getOutputStream());
}

接口测试建议(Apifox)

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

关键实践建议

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

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

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

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

到此这篇关于Spring Boot使用Hutool快速集成验证码的文章就介绍到这了,更多相关SpringBoot Hutool集成验证码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Cloud Feign实例讲解学习

    Spring Cloud Feign实例讲解学习

    这篇文章主要介绍了Spring Cloud Feign实例讲解学习,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • springBoot mybatis 包扫描实例

    springBoot mybatis 包扫描实例

    这篇文章主要介绍了springBoot mybatis 包扫描实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java设计模式之抽像工厂详解

    java设计模式之抽像工厂详解

    这篇文章主要为大家详细介绍了java设计模式之抽像工厂的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Java中synchronized实现原理详解

    Java中synchronized实现原理详解

    这篇文章主要介绍了Java中synchronized实现原理详解,涉及synchronized实现同步的基础,Java对象头,Monitor,Mark Word,锁优化,自旋锁等相关内容,具有一定借鉴价值,需要的朋友可以参考下。
    2017-11-11
  • 一文搞懂Mybatis-plus的分页查询操作

    一文搞懂Mybatis-plus的分页查询操作

    说起分页机制,相信我们程序员都不陌生,今天,我就给大家分享一下Mybatis-plus的分页机制,供大家学习和Copy,感兴趣的可以了解一下
    2022-06-06
  • 详解Java两种方式简单实现:爬取网页并且保存

    详解Java两种方式简单实现:爬取网页并且保存

    本篇文章主要介绍了Java两种方式简单实现:爬取网页并且保存 ,主要用UrlConnection、HttpClient爬取实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-12-12
  • 利用JDBC的PrepareStatement打印真实SQL的方法详解

    利用JDBC的PrepareStatement打印真实SQL的方法详解

    PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程,下面这篇文章主要给大家介绍了关于利用JDBC的PrepareStatement打印真实SQL的方法,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07
  • SpringBoot整合Web开发之文件上传与@ControllerAdvice

    SpringBoot整合Web开发之文件上传与@ControllerAdvice

    @ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此
    2022-08-08
  • IDEA找不到jdk该如何解决

    IDEA找不到jdk该如何解决

    这篇文章主要给大家介绍了关于IDEA找不到jdk该如何解决的相关资料,刚安装好IDEA后,我们运行一个项目时候,有时候会遇到显示找不到Java的JDK,需要的朋友可以参考下
    2023-11-11
  • java实现识别二维码图片功能方法详解与实例源码

    java实现识别二维码图片功能方法详解与实例源码

    这篇文章主要介绍了java实现识别二维码图片,java无法识别二维码情况下对二维码图片调优功能方法与实例源码,需要的朋友可以参考下
    2022-12-12

最新评论