本文目录导读:

我将为您提供一个完整的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验证码实现方案,您可以根据实际需求进行扩展和修改。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播