本文目录导读:

我将为您提供一个完整的Java图形验证码案例教程,包含从基础到进阶的实现方案。
项目结构
captcha-demo/
├── src/main/java/com/example/captcha/
│ ├── CaptchaGenerator.java // 验证码生成器
│ ├── CaptchaServlet.java // Servlet实现
│ └── VerifyCaptcha.java // 验证逻辑
└── src/main/webapp/
├── index.html // 测试页面
└── WEB-INF/web.xml // 配置
基础版验证码生成器
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
public class CaptchaGenerator {
// 验证码字符集
private static final String CHAR_SET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
private static final int WIDTH = 120;
private static final int HEIGHT = 40;
private static final int CODE_LENGTH = 4;
private static final Random random = new Random();
/**
* 生成验证码图片
* @param code 验证码字符串
* @param output 输出流
*/
public static void generateImage(String code, OutputStream output) throws Exception {
// 创建画布
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 设置背景
g2d.setColor(new Color(240, 240, 240));
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// 绘制干扰线
drawLines(g2d);
// 绘制验证码
drawCode(g2d, code);
// 绘制干扰点
drawNoise(g2d);
g2d.dispose();
// 输出图片
ImageIO.write(image, "png", output);
}
/**
* 生成随机验证码
*/
public static String generateCode() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < CODE_LENGTH; i++) {
sb.append(CHAR_SET.charAt(random.nextInt(CHAR_SET.length())));
}
return sb.toString();
}
/**
* 绘制验证码字符
*/
private static void drawCode(Graphics2D g2d, String code) {
int fontSize = HEIGHT - 8;
int charWidth = WIDTH / CODE_LENGTH;
for (int i = 0; i < code.length(); i++) {
// 随机字体
String[] fonts = {"Arial", "Verdana", "Times New Roman", "Courier New"};
Font font = new Font(fonts[random.nextInt(fonts.length)],
random.nextBoolean() ? Font.BOLD : Font.PLAIN,
fontSize);
g2d.setFont(font);
// 随机颜色
g2d.setColor(new Color(random.nextInt(150), random.nextInt(150), random.nextInt(150)));
// 随机旋转角度
double angle = (random.nextDouble() - 0.5) * 0.4;
g2d.rotate(angle, i * charWidth + charWidth / 2, HEIGHT / 2);
// 绘制字符
g2d.drawString(String.valueOf(code.charAt(i)),
i * charWidth + charWidth / 4,
HEIGHT / 2 + fontSize / 3);
// 恢复旋转
g2d.rotate(-angle, i * charWidth + charWidth / 2, HEIGHT / 2);
}
}
/**
* 绘制干扰线
*/
private static void drawLines(Graphics2D g2d) {
for (int i = 0; i < 5; i++) {
g2d.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)));
g2d.drawLine(0, random.nextInt(HEIGHT), WIDTH, random.nextInt(HEIGHT));
}
}
/**
* 绘制干扰点
*/
private static void drawNoise(Graphics2D g2d) {
for (int i = 0; i < 100; i++) {
g2d.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
g2d.fillOval(random.nextInt(WIDTH), random.nextInt(HEIGHT), 2, 2);
}
}
}
Servlet实现
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class CaptchaServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应头,禁止缓存
response.setHeader("Cache-Control", "no-store, no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
// 生成验证码
String captchaCode = CaptchaGenerator.generateCode();
// 存储到Session(不区分大小写,统一转大写)
HttpSession session = request.getSession();
session.setAttribute("captchaCode", captchaCode.toUpperCase());
session.setMaxInactiveInterval(300); // 5分钟有效期
// 输出图片
response.setContentType("image/png");
try {
CaptchaGenerator.generateImage(captchaCode, response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
验证逻辑
import javax.servlet.http.HttpSession;
public class CaptchaValidator {
/**
* 验证验证码
* @param session HTTP会话
* @param userInput 用户输入的验证码
* @return 验证结果
*/
public static boolean validate(HttpSession session, String userInput) {
if (session == null || userInput == null) {
return false;
}
// 获取Session中的验证码
String captchaCode = (String) session.getAttribute("captchaCode");
if (captchaCode == null) {
return false;
}
// 不区分大小写比较
boolean isValid = captchaCode.equalsIgnoreCase(userInput.trim());
// 验证后立即清除,防止多次使用
if (isValid) {
session.removeAttribute("captchaCode");
}
return isValid;
}
}
高级版本(增强安全性)
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.Random;
public class AdvancedCaptchaGenerator {
private static final int WIDTH = 150;
private static final int HEIGHT = 50;
private static final Random random = new Random();
public static void generateAdvanced(String code, OutputStream output) throws Exception {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
// 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 渐变背景
GradientPaint gradient = new GradientPaint(0, 0,
new Color(200, 220, 250), WIDTH, HEIGHT, new Color(250, 200, 220));
g2d.setPaint(gradient);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// 字符变换
int startX = 15;
int charWidth = (WIDTH - 30) / code.length();
for (int i = 0; i < code.length(); i++) {
CharTransform(g2d, code.charAt(i), startX + i * charWidth, HEIGHT / 2);
}
// 高级干扰效果
addAdvancedNoise(g2d);
g2d.dispose();
ImageIO.write(image, "png", output);
}
private static void CharTransform(Graphics2D g2d, char c, int x, int y) {
// 随机变换参数
int fontSize = 30 + random.nextInt(10);
double rotation = (random.nextDouble() - 0.5) * 0.6;
double scaleX = 0.8 + random.nextDouble() * 0.5;
double scaleY = 0.8 + random.nextDouble() * 0.5;
// 应用变换
AffineTransform transform = new AffineTransform();
transform.translate(x, y);
transform.rotate(rotation);
transform.scale(scaleX, scaleY);
// 随机颜色
float r = 0.1f + random.nextFloat() * 0.5f;
float g = 0.1f + random.nextFloat() * 0.5f;
float b = 0.1f + random.nextFloat() * 0.5f;
g2d.setColor(new Color(r, g, b));
g2d.setFont(new Font("Arial", Font.BOLD, fontSize));
// 绘制字符
FontMetrics metrics = g2d.getFontMetrics();
int cx = -metrics.charWidth(c) / 2;
int cy = (metrics.getAscent() - metrics.getDescent()) / 2;
g2d.drawString(String.valueOf(c), cx, cy);
}
private static void addAdvancedNoise(Graphics2D g2d) {
// 贝塞尔曲线
for (int i = 0; i < 3; i++) {
BezierCurve(g2d);
}
// 弧线干扰
for (int i = 0; i < 2; i++) {
g2d.setColor(new Color(random.nextInt(100), random.nextInt(100), random.nextInt(100)));
int arcX = random.nextInt(WIDTH);
int arcY = random.nextInt(HEIGHT);
g2d.drawArc(arcX, arcY, 60, 30, 0, 180 + random.nextInt(180));
}
// 随机像素点
for (int i = 0; i < 500; i++) {
int x = random.nextInt(WIDTH);
int y = random.nextInt(HEIGHT);
g2d.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
g2d.fillRect(x, y, 1, 1);
}
}
private static void BezierCurve(Graphics2D g2d) {
int x1 = random.nextInt(WIDTH);
int y1 = random.nextInt(HEIGHT);
int x2 = random.nextInt(WIDTH);
int y2 = random.nextInt(HEIGHT);
g2d.setColor(new Color(random.nextInt(150), random.nextInt(150), random.nextInt(150)));
CubicCurve2D curve = new CubicCurve2D.Double();
curve.setCurve(x1, y1, x1 + 20, HEIGHT/2, x2 + 20, HEIGHT/2, x2, y2);
g2d.draw(curve);
}
}
前端HTML页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">验证码测试</title>
<style>
.captcha-container {
display: flex;
align-items: center;
gap: 10px;
}
.captcha-img {
cursor: pointer;
border: 1px solid #ddd;
border-radius: 4px;
}
.refresh-btn {
padding: 5px 10px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.result {
margin-top: 10px;
padding: 10px;
border-radius: 4px;
}
.success { background: #d4edda; color: #155724; }
.error { background: #f8d7da; color: #721c24; }
.expired { background: #fff3cd; color: #856404; }
</style>
<script>
// 刷新验证码
function refreshCaptcha() {
const img = document.getElementById('captchaImg');
img.src = 'captcha?time=' + new Date().getTime();
}
// 验证验证码
function verifyCaptcha() {
const input = document.getElementById('captchaInput').value;
const xhr = new XMLHttpRequest();
xhr.open('POST', 'verify', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const result = document.getElementById('result');
const data = JSON.parse(xhr.responseText);
result.className = 'result ' + data.status;
result.innerHTML = data.message;
}
};
xhr.send('code=' + encodeURIComponent(input));
}
// 自动刷新
window.onload = function() {
refreshCaptcha();
};
</script>
</head>
<body>
<h2>验证码测试</h2>
<div class="captcha-container">
<img id="captchaImg" class="captcha-img"
src="captcha" width="120" height="40"
alt="验证码"
onclick="refreshCaptcha()"
title="点击刷新">
<button class="refresh-btn" onclick="refreshCaptcha()">刷新</button>
</div>
<input type="text" id="captchaInput" placeholder="请输入验证码">
<button onclick="verifyCaptcha()">验证</button>
<div id="result" class="result"></div>
<p style="color: #888; font-size: 12px;">点击图片或刷新按钮可更换验证码</p>
</body>
</html>
验证Servlet
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class VerifyCaptchaServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
String userInput = request.getParameter("code");
PrintWriter out = response.getWriter();
// 检查输入是否为空
if (userInput == null || userInput.trim().isEmpty()) {
out.write("{\"status\":\"error\",\"message\":\"请输入验证码\"}");
return;
}
// 验证验证码
HttpSession session = request.getSession(false);
if (session == null) {
out.write("{\"status\":\"expired\",\"message\":\"会话已过期,请刷新页面\"}");
return;
}
String captchaCode = (String) session.getAttribute("captchaCode");
if (captchaCode == null) {
out.write("{\"status\":\"expired\",\"message\":\"验证码已过期,请刷新\"}");
return;
}
// 验证结果
if (CaptchaValidator.validate(session, userInput)) {
out.write("{\"status\":\"success\",\"message\":\"验证成功!\"}");
} else {
out.write("{\"status\":\"error\",\"message\":\"验证码错误,请重试\"}");
}
}
}
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Captcha Demo</display-name>
<!-- 验证码生成Servlet -->
<servlet>
<servlet-name>captchaServlet</servlet-name>
<servlet-class>com.example.captcha.CaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>captchaServlet</servlet-name>
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
<!-- 验证码验证Servlet -->
<servlet>
<servlet-name>verifyCaptcha</servlet-name>
<servlet-class>com.example.captcha.VerifyCaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>verifyCaptcha</servlet-name>
<url-pattern>/verify</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
Spring/SpringBoot集成
@RestController
@RequestMapping("/api/captcha")
public class SpringCaptchaController {
@GetMapping("/get")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// 生成验证码
String code = CaptchaGenerator.generateCode();
// 存储到Session
request.getSession().setAttribute("captchaCode", code.toUpperCase());
// 设置响应头
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Access-Control-Allow-Origin", "*");
// 输出图片
CaptchaGenerator.generateImage(code, response.getOutputStream());
}
@PostMapping("/verify")
public String verify(HttpServletRequest request,
@RequestParam("code") String userInput) {
boolean isValid = CaptchaValidator.validate(request.getSession(), userInput);
if (isValid) {
return "{\"success\": true, \"message\": \"验证成功\"}";
} else {
return "{\"success\": false, \"message\": \"验证码错误\"}";
}
}
}
Maven依赖
<dependencies>
<!-- 基础Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 如果使用Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
使用说明
-
基本使用:部署项目后访问
/index.html,点击验证码图片刷新,输入验证码进行验证 -
安全建议:
- 验证码有效期建议设置为 5 分钟以内
- 每次验证后立即清除验证码,防止重放攻击
- 失败次数过多时建议加入验证码失效逻辑
- 生产环境建议使用Google reCAPTCHA等更安全的方案
-
扩展建议:
- 可以添加算术验证码("3+5=?")
- 支持中文字符验证码
- 添加滑块验证等交互式验证码
- 集成Redis存储验证码,支持集群部署
这个教程提供了完整的Java验证码实现方案,您可以根据实际需求进行扩展和修改。