SpringBoot 图形验证码实现教程:从自定义开发到 Hutool 工具类实战

0 阅读4分钟

本文详细讲解 SpringBoot 项目中图形验证码的两种实现方案,包括手写自定义验证码工具类和基于 Hutool 工具库快速集成线段、圆形、扭曲、GIF 四种验证码,附带完整代码示例与接口测试步骤,帮助开发者解决登录、注册等场景的人机验证需求。

一、为什么需要图形验证码?

在登录、注册、密码重置等用户交互场景中,图形验证码是防御恶意脚本、暴力破解的重要手段。它通过将随机字符与干扰元素结合,确保操作由真实用户完成,而非自动化程序。

传统实现方式有两种:一是手动编写验证码生成逻辑,二是使用成熟工具库快速集成。下面分别讲解这两种方案的具体操作,你可以根据项目需求选择合适的方式。

二、方案一:手写自定义验证码工具类

如果需要高度定制验证码样式(如特定字体、干扰线密度),可以手动开发工具类。以下是完整实现步骤。

2.1 新建验证码工具类

在 SpringBoot 项目的 util 包下创建 Code 类,核心逻辑包括生成随机字符、绘制干扰线、输出图片到响应流,并将验证码存入 Session 用于后续验证。

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 Code {
    // 存储验证码到 Session 的 key
    public static final String RANDOMCODEKEY = "ValidateCode";
    // 随机数生成器
    private final Random random = new Random();
    // 验证码字符库(数字 + 大写字母)private final String randomString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    // 验证码图片宽高与干扰线配置
    private int width = 80;
    private int height = 26;
    private int lineSize = 40;
    private int stringNum = 4;
 
    /**
     * 获取验证码字体(固定为 Fixedsys,大小 18)*/
    private Font getFont() {return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
    }
 
    /**
     * 生成随机颜色(避免颜色过深或过浅)*/
    private Color getRandColor(int fc, int bc) {if (fc > 255) fc = 255;
        if (bc > 255) bc = 255;
        int r = fc + random.nextInt(bc - fc - 16);
        int g = fc + random.nextInt(bc - fc - 14);
        int b = fc + random.nextInt(bc - fc - 18);
        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(13);
        int yl = random.nextInt(15);
        g.drawLine(x, y, x + xl, y + yl);
    }
 
    /**
     * 绘制随机字符(随机颜色、轻微偏移)*/
    private String drawString(Graphics g, String randomStr, int i) {g.setFont(getFont());
        g.setColor(new Color(random.nextInt(101), random.nextInt(111), random.nextInt(121)));
        String charStr = String.valueOf(randomString.charAt(random.nextInt(randomString.length())));
        randomStr += charStr;
        // 字符位置轻微偏移,增加识别难度
        g.translate(random.nextInt(3), random.nextInt(3));
        g.drawString(charStr, 13 * i, 16);
        return randomStr;
    }
 
    /**
     * 核心方法:生成验证码并输出到响应流
     */
    public void getValidateCode(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession();
        // 1. 创建图片缓冲区
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics g = image.getGraphics();
 
        // 2. 绘制图片背景
        g.fillRect(0, 0, width, height);
        g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
        g.setColor(getRandColor(110, 133));
 
        // 3. 绘制干扰线
        for (int i = 0; i <= lineSize; i++) {drawLine(g);
        }
 
        // 4. 绘制验证码字符
        String randomStr = "";
        for (int i = 1; i <= stringNum; i++) {randomStr = drawString(g, randomStr, i);
        }
 
        // 5. 存储验证码到 Session(覆盖旧值)session.removeAttribute(RANDOMCODEKEY);
        session.setAttribute(RANDOMCODEKEY, randomStr);
 
        // 6. 关闭资源并输出图片
        g.dispose();
        try {ImageIO.write(image, "JPEG", response.getOutputStream());
        } catch (Exception e) {e.printStackTrace();
        }
    }
}

2.2 在 Controller 中调用工具类

创建CaptchaController,定义接口/checkCode2,设置响应格式为图片,并禁用浏览器缓存(避免验证码重复加载)。

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("/checkCode2")
    public void checkCode2(HttpServletRequest request, HttpServletResponse response) {
        // 1. 设置响应格式为 JPEG 图片
        response.setContentType("image/jpeg");
        // 2. 禁用浏览器缓存(关键:避免验证码复用)response.setDateHeader("Expires", 0);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        // 3. 调用工具类生成验证码
        Code code = new Code();
        code.getValidateCode(request, response);
    }
}

三、方案二:使用 Hutool 快速集成验证码

Hutool 是 Java 生态中常用的工具库,其 hutool-captcha 模块已封装好四种验证码,无需重复开发,推荐项目中优先使用。

3.1 引入 Hutool 依赖

在 pom.xml 中添加依赖(Maven),如果是 Gradle 项目,可参考 Hutool 官网 调整配置。

<!-- 方式 1:仅引入图形验证码模块(轻量)-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-captcha</artifactId>
    <version>5.8.6</version>
</dependency>
 
<!-- 方式 2:引入 Hutool 所有模块(适合需其他工具类的场景)-->
<!--
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>
-->

3.2 四种验证码实现示例

Hutool 提供LineCaptcha(线段干扰)、CircleCaptcha(圆形干扰)、ShearCaptcha(扭曲干扰)、GifCaptcha(动态 GIF),接口调用逻辑类似,仅需修改验证码创建方式。

3.2.1 线段干扰验证码(LineCaptcha)

最基础的验证码类型,通过线段干扰提高安全性。

继续阅读全文:SpringBoot 图形验证码实现教程:从自定义开发到 Hutool 工具类实战