本文目录导读:

我来详细讲解Java验证码的完整开发案例,包括图片验证码和短信验证码两种常见类型。
图片验证码案例
添加依赖(Maven项目)
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis(用于存储验证码) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
验证码生成工具类
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
public class CaptchaUtil {
private static final int WIDTH = 120;
private static final int HEIGHT = 40;
private static final int CODE_LENGTH = 4;
private static final String CHARACTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
/**
* 生成验证码
* @return 包含验证码文本和Base64图片的Map
*/
public static CaptchaResult generateCaptcha() {
Random random = new Random();
// 生成随机验证码
StringBuilder code = new StringBuilder();
for (int i = 0; i < CODE_LENGTH; i++) {
code.append(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
}
// 创建图片
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// 设置背景色
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
// 设置边框
g.setColor(Color.LIGHT_GRAY);
g.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
// 添加干扰线
g.setColor(Color.GRAY);
for (int i = 0; i < 10; 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);
}
// 绘制验证码
g.setFont(new Font("Arial", Font.BOLD, 24));
for (int i = 0; i < CODE_LENGTH; i++) {
// 随机颜色
g.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)));
// 随机旋转角度
double angle = random.nextDouble() * 0.3 - 0.15;
g.rotate(angle, 15 + i * 25, 25);
g.drawString(String.valueOf(code.charAt(i)), 15 + i * 25, 28);
g.rotate(-angle, 15 + i * 25, 25);
}
g.dispose();
// 转换为Base64
String base64Image = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
byte[] imageBytes = baos.toByteArray();
base64Image = Base64.getEncoder().encodeToString(imageBytes);
} catch (IOException e) {
e.printStackTrace();
}
return new CaptchaResult(code.toString(), base64Image);
}
// 验证码结果类
public static class CaptchaResult {
private String code;
private String base64Image;
public CaptchaResult(String code, String base64Image) {
this.code = code;
this.base64Image = base64Image;
}
public String getCode() {
return code;
}
public String getBase64Image() {
return base64Image;
}
}
}
验证码控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/captcha")
public class CaptchaController {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 获取验证码
*/
@GetMapping("/generate")
public Result generateCaptcha() {
// 生成验证码
CaptchaUtil.CaptchaResult captchaResult = CaptchaUtil.generateCaptcha();
// 生成唯一标识
String captchaId = UUID.randomUUID().toString().replace("-", "");
// 存储到Redis,5分钟有效
redisTemplate.opsForValue().set(
"captcha:" + captchaId,
captchaResult.getCode(),
5,
TimeUnit.MINUTES
);
// 返回结果
Map<String, String> data = new HashMap<>();
data.put("captchaId", captchaId);
data.put("captchaImage", "data:image/png;base64," + captchaResult.getBase64Image());
return Result.success(data);
}
/**
* 验证验证码
*/
@PostMapping("/verify")
public Result verifyCaptcha(@RequestBody VerifyRequest request) {
String redisKey = "captcha:" + request.getCaptchaId();
String storedCode = redisTemplate.opsForValue().get(redisKey);
if (storedCode == null) {
return Result.error("验证码已过期");
}
if (!storedCode.equalsIgnoreCase(request.getCaptchaCode())) {
return Result.error("验证码错误");
}
// 验证成功后删除验证码(一次性使用)
redisTemplate.delete(redisKey);
return Result.success("验证成功");
}
// 验证请求体
static class VerifyRequest {
private String captchaId;
private String captchaCode;
// getter和setter方法
public String getCaptchaId() { return captchaId; }
public void setCaptchaId(String captchaId) { this.captchaId = captchaId; }
public String getCaptchaCode() { return captchaCode; }
public void setCaptchaCode(String captchaCode) { this.captchaCode = captchaCode; }
}
}
统一响应类
public class Result {
private int code;
private String message;
private Object data;
public static Result success(Object data) {
Result result = new Result();
result.code = 200;
result.message = "success";
result.data = data;
return result;
}
public static Result error(String message) {
Result result = new Result();
result.code = 400;
result.message = message;
return result;
}
// getter和setter方法
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; }
}
前端HTML示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">验证码示例</title>
</head>
<body>
<div style="margin: 20px;">
<h2>图形验证码示例</h2>
<div>
<input type="text" id="captchaCode" placeholder="请输入验证码">
<img id="captchaImg" src="" onclick="refreshCaptcha()" style="cursor: pointer;">
<button onclick="verifyCaptcha()">验证</button>
</div>
<div id="result" style="margin-top: 10px;"></div>
</div>
<script>
// 获取验证码
function getCaptcha() {
fetch('/api/captcha/generate')
.then(response => response.json())
.then(data => {
if (data.code === 200) {
document.getElementById('captchaImg').src = data.data.captchaImage;
document.getElementById('captchaImg').dataset.captchaId = data.data.captchaId;
}
});
}
// 刷新验证码
function refreshCaptcha() {
getCaptcha();
}
// 验证验证码
function verifyCaptcha() {
const captchaId = document.getElementById('captchaImg').dataset.captchaId;
const captchaCode = document.getElementById('captchaCode').value;
fetch('/api/captcha/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
captchaId: captchaId,
captchaCode: captchaCode
})
})
.then(response => response.json())
.then(data => {
document.getElementById('result').innerHTML =
data.code === 200 ?
'<span style="color: green;">✓ 验证成功</span>' :
'<span style="color: red;">✗ ' + data.message + '</span>';
});
}
// 页面加载时获取验证码
window.onload = getCaptcha;
</script>
</body>
</html>
短信验证码案例
短信验证码服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Service
public class SmsCaptchaService {
@Autowired
private StringRedisTemplate redisTemplate;
// 阿里云短信服务(示例中使用模拟实现)
@Autowired(required = false)
private SmsService smsService;
/**
* 发送短信验证码
*/
public Result sendSmsCaptcha(String phone) {
// 检查发送频率(60秒内不能重复发送)
String rateLimitKey = "sms:rate:" + phone;
if (redisTemplate.hasKey(rateLimitKey)) {
return Result.error("发送过于频繁,请60秒后重试");
}
// 生成6位随机验证码
String captcha = String.format("%06d", new Random().nextInt(999999));
// 存储验证码到Redis(5分钟有效)
String captchaKey = "sms:captcha:" + phone;
redisTemplate.opsForValue().set(captchaKey, captcha, 5, TimeUnit.MINUTES);
// 设置发送频率限制
redisTemplate.opsForValue().set(rateLimitKey, "1", 60, TimeUnit.SECONDS);
// 发送短信(模拟)
boolean sendSuccess = sendSms(phone, captcha);
if (sendSuccess) {
System.out.println("短信验证码发送成功:" + phone + " - " + captcha);
return Result.success("验证码已发送");
} else {
return Result.error("短信发送失败");
}
}
/**
* 验证短信验证码
*/
public Result verifySmsCaptcha(String phone, String captcha) {
String captchaKey = "sms:captcha:" + phone;
String storedCaptcha = redisTemplate.opsForValue().get(captchaKey);
if (storedCaptcha == null) {
return Result.error("验证码已过期");
}
if (!storedCaptcha.equals(captcha)) {
return Result.error("验证码错误");
}
// 验证成功后删除验证码
redisTemplate.delete(captchaKey);
return Result.success("验证成功");
}
/**
* 模拟发送短信
*/
private boolean sendSms(String phone, String captcha) {
// 实际项目中调用短信服务商API
// 阿里云短信、腾讯云短信等
try {
// 模拟发送延迟
Thread.sleep(100);
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
短信验证码控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/sms")
public class SmsCaptchaController {
@Autowired
private SmsCaptchaService smsCaptchaService;
/**
* 发送短信验证码
*/
@PostMapping("/send")
public Result sendSmsCaptcha(@RequestBody SendSmsRequest request) {
// 验证手机号格式
if (!isValidPhone(request.getPhone())) {
return Result.error("手机号格式不正确");
}
return smsCaptchaService.sendSmsCaptcha(request.getPhone());
}
/**
* 验证短信验证码
*/
@PostMapping("/verify")
public Result verifySmsCaptcha(@RequestBody VerifySmsRequest request) {
if (!isValidPhone(request.getPhone())) {
return Result.error("手机号格式不正确");
}
if (request.getCaptcha() == null || request.getCaptcha().length() != 6) {
return Result.error("验证码格式不正确");
}
return smsCaptchaService.verifySmsCaptcha(request.getPhone(), request.getCaptcha());
}
/**
* 简单手机号验证
*/
private boolean isValidPhone(String phone) {
return phone != null && phone.matches("^1[3-9]\\d{9}$");
}
// 请求体类
static class SendSmsRequest {
private String phone;
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
static class VerifySmsRequest {
private String phone;
private String captcha;
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getCaptcha() { return captcha; }
public void setCaptcha(String captcha) { this.captcha = captcha; }
}
}
配置与启动
application.yml 配置
spring:
redis:
host: localhost
port: 6379
timeout: 5000
# 如果使用阿里云短信服务,配置如下
aliyun:
sms:
access-key-id: your-access-key-id
access-key-secret: your-access-key-secret
sign-name: 您的签名
template-code: SMS_123456789
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CaptchaApplication {
public static void main(String[] args) {
SpringApplication.run(CaptchaApplication.class, args);
}
}
使用示例
获取图片验证码
GET /api/captcha/generate
验证图片验证码
POST /api/captcha/verify
Body: {
"captchaId": "生成的captchaId",
"captchaCode": "用户输入的验证码"
}
发送短信验证码
POST /api/sms/send
Body: {
"phone": "13800138000"
}
验证短信验证码
POST /api/sms/verify
Body: {
"phone": "13800138000",
"captcha": "123456"
}
这个案例提供了完整的验证码解决方案,包括:
- 图形验证码的生成与验证
- 短信验证码的发送与验证
- Redis存储验证码(支持过期时间)
- 防刷机制(发送频率限制)
- 前端集成示例
你可以根据实际需求进行扩展,比如添加更复杂的图形验证码(中文、计算题等),或者集成实际的短信服务商API。