Java 绘制图形验证码

在 Spring Boot 中生成图形验证码并校验其正确性,通常包括以下步骤:

  1. 生成验证码图片和对应的验证码值。
  2. 将验证码值存储到 Session 或其他存储中(如 Redis)。
  3. 将验证码图片返回给客户端。
  4. 客户端提交表单时,校验用户输入的验证码是否正确。

一、添加依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

二、生成验证码工具类

复制代码
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;

public class CaptchaUtil {
    // 验证码字符集
    private static final String CHAR_SET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz";
    private static final int WIDTH = 120; // 图片宽度
    private static final int HEIGHT = 40; // 图片高度
    private static final int FONT_SIZE = 30; // 字体大小
    private static final int CODE_LENGTH = 4; // 验证码长度
    private static final int LINE_COUNT = 5; // 干扰线数量
    private static final int NOISE_COUNT = 30; // 噪点数量
    // 生成随机验证码
    public static String generateCaptchaCode() {
        Random random = new Random();
        StringBuilder captcha = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            captcha.append(CHAR_SET.charAt(random.nextInt(CHAR_SET.length())));
        }
        return captcha.toString();
    }
    // 生成验证码图片
    public static BufferedImage generateCaptchaImage(String captchaCode) {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        // 设置背景色(随机浅色)
        g.setColor(getRandomLightColor());
        g.fillRect(0, 0, WIDTH, HEIGHT);
        // 设置字体(随机选择字体)
        g.setFont(getRandomFont());
        g.setColor(getRandomDarkColor());
        // 绘制验证码字符
        for (int i = 0; i < CODE_LENGTH; i++) {
            // 随机旋转字符角度
            double theta = Math.toRadians(new Random().nextInt(30) - 15);
            g.rotate(theta, i * (WIDTH / CODE_LENGTH) + 10, HEIGHT / 2 + 10);
            g.drawString(String.valueOf(captchaCode.charAt(i)), i * (WIDTH / CODE_LENGTH) + 10, HEIGHT / 2 + 10);
            g.rotate(-theta, i * (WIDTH / CODE_LENGTH) + 10, HEIGHT / 2 + 10);
        }
        // 绘制干扰线
        g.setColor(getRandomDarkColor());
        Random random = new Random();
        for (int i = 0; i < LINE_COUNT; i++) {
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            g.drawLine(x1, y1, x2, y2);
        }
        // 绘制噪点
        for (int i = 0; i < NOISE_COUNT; i++) {
            int x = random.nextInt(WIDTH);
            int y = random.nextInt(HEIGHT);
            image.setRGB(x, y, getRandomDarkColor().getRGB());
        }
        g.dispose();
        return image;
    }
    // 将图片转换为字节数组
    public static byte[] imageToBytes(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "PNG", baos);
        return baos.toByteArray();
    }
    // 获取随机浅色
    private static Color getRandomLightColor() {
        Random random = new Random();
        return new Color(random.nextInt(100) + 155, random.nextInt(100) + 155, random.nextInt(100) + 155);
    }
    // 获取随机深色
    private static Color getRandomDarkColor() {
        Random random = new Random();
        return new Color(random.nextInt(100), random.nextInt(100), random.nextInt(100));
    }
    // 获取随机字体
    private static Font getRandomFont() {
        String[] fontNames = { "Arial", "Verdana", "Georgia", "Times New Roman", "Courier New" };
        Random random = new Random();
        return new Font(fontNames[random.nextInt(fontNames.length)], Font.BOLD, FONT_SIZE);
    }
}

三、验证码控制器

复制代码
import com.example.demo.util.CaptchaUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
@RestController
public class CaptchaController {
    // 生成验证码图片
    @GetMapping("/captcha")
    public ResponseEntity<byte[]> generateCaptcha(HttpSession session) throws IOException {
        // 生成验证码
        String captchaCode = CaptchaUtil.generateCaptchaCode();
        // 将验证码存储到Session
        session.setAttribute("captcha", captchaCode);
        // 生成图片
        BufferedImage image = CaptchaUtil.generateCaptchaImage(captchaCode);
        // 将图片转换为字节数组
        byte[] imageBytes = CaptchaUtil.imageToBytes(image);
        // 返回图片
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE)
                .body(imageBytes);
    }
    // 校验验证码
    @PostMapping("/verify")
    public String verifyCaptcha(@RequestParam String userCaptcha, HttpSession session) {
        // 获取Session中存储的验证码
        String storedCaptcha = (String) session.getAttribute("captcha");
        // 清除Session中的验证码,避免重复使用
        session.removeAttribute("captcha");
        // 校验用户输入的验证码
        if (storedCaptcha != null && storedCaptcha.equalsIgnoreCase(userCaptcha)) {
            return "验证码正确";
        } else {
            return "验证码错误";
        }
    }
}

四、测试验证码功能

http://localhost:8080/captcha

实际开发中,验证码通常存储于 Redis 中,设置五分钟后自动过期。或验证通过之后进行删除。实际开发还有更高级的验证码功能(如滑动验证码、点选验证码)可自行研究哈。

相关推荐
普通网友8 小时前
IZT#73193
java·php·程序优化
rechol8 小时前
C++ 继承笔记
java·c++·笔记
fruge9 小时前
2025前端工程化与性能优化实战指南:从构建到监控的全链路方案
前端·性能优化
Han.miracle12 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode
Le1Yu13 小时前
分布式事务以及Seata(XA、AT模式)
java
寒山李白13 小时前
关于Java项目构建/配置工具方式(Gradle-Groovy、Gradle-Kotlin、Maven)的区别于选择
java·kotlin·gradle·maven
无妄无望14 小时前
docker学习(4)容器的生命周期与资源控制
java·学习·docker
MC丶科14 小时前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
千码君201614 小时前
React Native:从react的解构看编程众多语言中的解构
java·javascript·python·react native·react.js·解包·解构
夜白宋16 小时前
【word多文档docx合并】
java·word