前言
大家好!今天我要和大家分享一个非常实用的功能:Spring Boot图片验证码。这个功能可以防止恶意攻击,比如暴力破解、刷票等。我们实现的是一个带有加减法运算的图片验证码,用户需要正确计算结果才能通过验证。
适合人群:Java初学者、Spring Boot新手、想要学习验证码实现的朋友
技术栈:Spring Boot 3.5.5 + Java 17 + Thymeleaf + Maven
功能预览
最终实现的效果:
生成随机数学表达式(如:5 + 3 = ?)
将表达式绘制成图片
用户输入计算结果
验证答案是否正确
支持刷新验证码
环境准备
1. 创建Spring Boot项目
首先,我们需要创建一个Spring Boot项目。我使用的是Spring Initializr创建的项目,包含以下依赖:
Spring Web
Thymeleaf
Spring Data Redis
Spring Session Data Redis
2. 项目结构
src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── demo/ │ │ ├── Demo5Application.java │ │ ├── config/ │ │ │ └── CorsConfig.java │ │ ├── controller/ │ │ │ └── CaptchaController.java │ │ ├── service/ │ │ │ ├── CaptchaService.java │ │ │ └── MemoryCaptchaService.java │ │ └── util/ │ │ └── CaptchaUtil.java │ └── resources/ │ ├── application.properties │ └── templates/ │ └── index.html └── test/ └── java/ └── com/ └── example/ └── demo/ └── CaptchaUtilTest.java第一步:配置Maven依赖
首先,我们需要在pom.xml中添加必要的依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.5</version> <relativePath/><!-- lookup parent from repository --> </parent> <groupId>org.example</groupId> <artifactId>demo5</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo5</name> <description>demo5</description> <properties> <java.version>17</java.version> </properties> <dependencies> <!-- Spring Boot Web Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Thymeleaf模板引擎 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Redis支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Spring Session Redis --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <!-- 测试依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>解释:
spring-boot-starter-web:提供Web开发基础功能spring-boot-starter-thymeleaf:模板引擎,用于渲染HTML页面spring-boot-starter-data-redis:Redis数据库支持spring-session-data-redis:将Session存储到Redis中
第二步:创建验证码工具类
这是整个功能的核心!我们创建一个工具类来生成数学表达式和绘制图片:
package com.example.demo.util; 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; /** * 验证码工具类 * 用于生成数学表达式验证码图片 */ publicclass CaptchaUtil { /** * 生成随机数学表达式和答案 * @return MathExpression 包含表达式和答案的对象 */ public static MathExpression generateMathExpression() { Random random = new Random(); int a = random.nextInt(10) + 1; // 1-10 int b = random.nextInt(10) + 1; // 1-10 // 随机决定是加法还是减法 String operator; int result; if (random.nextBoolean()) { operator = "+"; result = a + b; } else { operator = "-"; // 确保结果不为负数 if (a < b) { int temp = a; a = b; b = temp; } result = a - b; } String expression = a + " " + operator + " " + b + " = ?"; returnnew MathExpression(expression, result); } /** * 生成验证码图片 * @param expression 数学表达式 * @return Base64编码的图片字符串 * @throws IOException IO异常 */ public static String generateCaptchaImage(String expression) throws IOException { int width = 120; int height = 40; // 创建图片对象 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.setFont(new Font("Arial", Font.BOLD, 16)); // 绘制干扰线(让验证码更难被机器识别) Random random = new Random(); for (int i = 0; i < 5; i++) { int x1 = random.nextInt(width); int y1 = random.nextInt(height); int x2 = random.nextInt(width); int y2 = random.nextInt(height); g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256))); g.drawLine(x1, y1, x2, y2); } // 绘制表达式文字 g.setColor(Color.BLACK); g.drawString(expression, 10, 25); g.dispose(); // 将图片转换为Base64字符串 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); byte[] bytes = baos.toByteArray(); return"data:image/png;base64," + Base64.getEncoder().encodeToString(bytes); } /** * 内部类用于存储表达式和结果 */ publicstaticclass MathExpression { private String expression; privateint result; public MathExpression(String expression, int result) { this.expression = expression; this.result = result; } public String getExpression() { return expression; } public int getResult() { return result; } } }代码解释:
1.generateMathExpression()方法:
生成两个1-10的随机数
随机选择加法或减法
确保减法结果不为负数
返回表达式字符串和正确答案
2.generateCaptchaImage()方法:
创建120x40像素的图片
设置白色背景
绘制5条随机颜色的干扰线
在图片上绘制数学表达式
将图片转换为Base64字符串返回
3.MathExpression内部类:
封装表达式和答案
提供getter方法
第三步:创建验证码服务类
我们需要两个服务类:一个基于Session,一个基于内存存储。
3.1 基于Session的验证码服务
package com.example.demo.service; import com.example.demo.util.CaptchaUtil; import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import jakarta.servlet.http.HttpSession; import java.io.IOException; /** * 验证码服务类 * 处理验证码的生成和验证逻辑 */ @Service publicclass CaptchaService { /** * 生成验证码并存入session * @return Base64编码的验证码图片 * @throws IOException IO异常 */ public String generateCaptcha() throws IOException { CaptchaUtil.MathExpression mathExpression = CaptchaUtil.generateMathExpression(); String imageBase64 = CaptchaUtil.generateCaptchaImage(mathExpression.getExpression()); // 获取当前session并存储答案 ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpSession session = attr.getRequest().getSession(); session.setAttribute("captchaAnswer", mathExpression.getResult()); session.setMaxInactiveInterval(300); // 5分钟有效期 return imageBase64; } /** * 验证用户输入的答案 * @param userAnswer 用户输入的答案 * @return 验证是否成功 */ public boolean validateCaptcha(String userAnswer) { try { int answer = Integer.parseInt(userAnswer); // 获取当前session中的答案 ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpSession session = attr.getRequest().getSession(); Integer correctAnswer = (Integer) session.getAttribute("captchaAnswer"); // 添加调试信息 System.out.println("用户输入答案: " + answer); System.out.println("正确答案: " + correctAnswer); System.out.println("Session ID: " + session.getId()); if (correctAnswer != null && answer == correctAnswer) { // 验证成功后移除答案 session.removeAttribute("captchaAnswer"); System.out.println("验证成功"); returntrue; } System.out.println("验证失败"); returnfalse; } catch (NumberFormatException e) { System.out.println("数字格式错误: " + userAnswer); returnfalse; } } }3.2 基于内存的验证码服务(解决跨域问题)
package com.example.demo.service; import com.example.demo.util.CaptchaUtil; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * 基于内存的验证码服务 * 解决跨域Session问题 */ @Service publicclass MemoryCaptchaService { // 使用ConcurrentHashMap存储验证码,key为验证码ID,value为答案 privatefinal Map<String, Integer> captchaStorage = new ConcurrentHashMap<>(); /** * 生成验证码并返回验证码ID和图片 * @return CaptchaResponse 包含验证码ID和Base64图片 * @throws IOException IO异常 */ public CaptchaResponse generateCaptcha() throws IOException { CaptchaUtil.MathExpression mathExpression = CaptchaUtil.generateMathExpression(); String imageBase64 = CaptchaUtil.generateCaptchaImage(mathExpression.getExpression()); // 生成唯一验证码ID String captchaId = UUID.randomUUID().toString(); // 存储答案到内存中 captchaStorage.put(captchaId, mathExpression.getResult()); System.out.println("生成验证码ID: " + captchaId + ", 答案: " + mathExpression.getResult()); returnnew CaptchaResponse(captchaId, imageBase64); } /** * 验证用户输入的答案 * @param captchaId 验证码ID * @param userAnswer 用户输入的答案 * @return 验证是否成功 */ public boolean validateCaptcha(String captchaId, String userAnswer) { try { int answer = Integer.parseInt(userAnswer); Integer correctAnswer = captchaStorage.get(captchaId); System.out.println("验证码ID: " + captchaId); System.out.println("用户输入答案: " + answer); System.out.println("正确答案: " + correctAnswer); if (correctAnswer != null && answer == correctAnswer) { // 验证成功后移除验证码 captchaStorage.remove(captchaId); System.out.println("验证成功"); returntrue; } System.out.println("验证失败"); returnfalse; } catch (NumberFormatException e) { System.out.println("数字格式错误: " + userAnswer); returnfalse; } } /** * 验证码响应类 */ publicstaticclass CaptchaResponse { private String captchaId; private String imageBase64; public CaptchaResponse(String captchaId, String imageBase64) { this.captchaId = captchaId; this.imageBase64 = imageBase64; } public String getCaptchaId() { return captchaId; } public String getImageBase64() { return imageBase64; } } }两种服务类的区别:
Session服务:将答案存储在HttpSession中,适合同域访问
内存服务:将答案存储在内存Map中,通过唯一ID关联,解决跨域问题
第四步:创建控制器
控制器负责处理HTTP请求,连接前端和后端:
package com.example.demo.controller; import com.example.demo.service.CaptchaService; import com.example.demo.service.MemoryCaptchaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; 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.ResponseBody; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * 验证码控制器 * 处理验证码相关的HTTP请求 */ @Controller publicclass CaptchaController { @Autowired private CaptchaService captchaService; @Autowired private MemoryCaptchaService memoryCaptchaService; /** * 显示验证页面 * @param model 模型对象 * @return 页面名称 */ @GetMapping("/") public String index(Model model) { return"index"; } /** * 获取验证码图片(基于Session) * @return Base64编码的验证码图片 */ @GetMapping("/captcha") @ResponseBody public ResponseEntity<String> getCaptcha() { try { String imageBase64 = captchaService.generateCaptcha(); return ResponseEntity.ok(imageBase64); } catch (IOException e) { return ResponseEntity.status(500).body("生成验证码失败"); } } /** * 验证答案(基于Session) * @param answer 用户输入的答案 * @return 验证结果 */ @PostMapping("/validate") @ResponseBody public ResponseEntity<String> validateCaptcha(@RequestParam String answer) { System.out.println("收到验证请求,答案: " + answer); boolean isValid = captchaService.validateCaptcha(answer); if (isValid) { return ResponseEntity.ok("验证成功"); } else { return ResponseEntity.badRequest().body("验证失败"); } } /** * 获取基于内存的验证码(解决跨域Session问题) * @return 包含验证码ID和图片的响应 */ @GetMapping("/memory-captcha") @ResponseBody public ResponseEntity<Map<String, String>> getMemoryCaptcha() { try { MemoryCaptchaService.CaptchaResponse response = memoryCaptchaService.generateCaptcha(); Map<String, String> result = new HashMap<>(); result.put("captchaId", response.getCaptchaId()); result.put("imageData", response.getImageBase64()); return ResponseEntity.ok(result); } catch (IOException e) { return ResponseEntity.status(500).build(); } } /** * 验证基于内存的验证码 * @param captchaId 验证码ID * @param answer 用户输入的答案 * @return 验证结果 */ @PostMapping("/memory-validate") @ResponseBody public ResponseEntity<String> validateMemoryCaptcha( @RequestParam String captchaId, @RequestParam String answer) { System.out.println("收到内存验证码验证请求,ID: " + captchaId + ", 答案: " + answer); boolean isValid = memoryCaptchaService.validateCaptcha(captchaId, answer); if (isValid) { return ResponseEntity.ok("验证成功"); } else { return ResponseEntity.badRequest().body("验证失败"); } } }控制器解释:
@GetMapping("/"):显示主页面@GetMapping("/captcha"):获取Session验证码@PostMapping("/validate"):验证Session验证码@GetMapping("/memory-captcha"):获取内存验证码@PostMapping("/memory-validate"):验证内存验证码
第五步:创建前端页面
创建一个美观的HTML页面:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>验证码演示</title> <style> body { font-family: Arial, sans-serif; margin: 50px; background-color: #f5f5f5; } .container { max-width: 400px; margin: 0 auto; padding: 20px; background-color: white; border-radius: 5px; box-shadow: 0010pxrgba(0,0,0,0.1); } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; } input[type="text"] { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; } button { padding: 10px15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #45a049; } #captchaImage { margin-bottom: 10px; border: 1px solid #ddd; } #refreshCaptcha { margin-left: 10px; background-color: #2196F3; } #refreshCaptcha:hover { background-color: #1976D2; } .message { margin-top: 15px; padding: 10px; border-radius: 4px; display: none; } .success { background-color: #dff0d8; color: #3c763d; border: 1px solid #d6e9c6; } .error { background-color: #f2dede; color: #a94442; border: 1px solid #ebccd1; } .captcha-container { display: flex; align-items: center; margin-bottom: 10px; } </style> </head> <body> <div class="container"> <h2>验证码验证</h2> <div class="form-group"> <label>验证码:</label> <div class="captcha-container"> <img id="captchaImage" src="" alt="验证码"> <button id="refreshCaptcha">刷新</button> </div> </div> <div class="form-group"> <label for="answer">请输入计算结果:</label> <input type="text" id="answer" placeholder="请输入计算结果"> </div> <!-- 隐藏的验证码ID字段 --> <input type="hidden" id="captchaId" value=""> <button id="submitBtn">提交验证</button> <div id="message" class="message"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const captchaImage = document.getElementById('captchaImage'); const refreshBtn = document.getElementById('refreshCaptcha'); const answerInput = document.getElementById('answer'); const submitBtn = document.getElementById('submitBtn'); const messageDiv = document.getElementById('message'); const captchaIdInput = document.getElementById('captchaId'); // 加载验证码 functionloadCaptcha() { fetch('http://localhost:8080/memory-captcha') .then(response => { if (!response.ok) { thrownewError('网络响应不正常'); } return response.json(); }) .then(data => { captchaImage.src = data.imageData; captchaIdInput.value = data.captchaId; console.log('验证码ID:', data.captchaId); }) .catch(error => { console.error('加载验证码失败:', error); showMessage('加载验证码失败,请重试', 'error'); }); } // 初始化加载验证码 loadCaptcha(); // 刷新验证码 refreshBtn.addEventListener('click', function() { loadCaptcha(); answerInput.value = ''; messageDiv.style.display = 'none'; }); // 提交验证 submitBtn.addEventListener('click', function() { const answer = answerInput.value.trim(); const captchaId = captchaIdInput.value; if (!answer) { showMessage('请输入计算结果', 'error'); return; } if (!captchaId) { showMessage('验证码已过期,请刷新', 'error'); loadCaptcha(); return; } // 发送验证请求 const formData = new FormData(); formData.append('captchaId', captchaId); formData.append('answer', answer); fetch('http://localhost:8080/memory-validate', { method: 'POST', body: formData }) .then(response => { if (response.ok) { return response.text(); } else { thrownewError('验证失败'); } }) .then(result => { showMessage('验证成功!', 'success'); }) .catch(error => { showMessage('验证失败,请重试', 'error'); loadCaptcha(); // 刷新验证码 answerInput.value = ''; }); }); // 显示消息 function showMessage(message, type) { messageDiv.textContent = message; messageDiv.className = 'message ' + type; messageDiv.style.display = 'block'; } }); </script> </body> </html>前端功能解释:
页面加载时:自动获取验证码图片
刷新按钮:重新获取新的验证码
提交验证:发送用户输入的答案到后端验证
消息提示:显示验证成功或失败的消息
第六步:配置CORS和应用程序
6.1 CORS配置(解决跨域问题)
package com.example.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * CORS配置类 * 解决跨域请求问题 */ @Configuration publicclass CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.addAllowedOriginPattern("*"); configuration.addAllowedMethod("*"); configuration.addAllowedHeader("*"); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); returnsource; } }6.2 应用程序配置
# application.properties spring.application.name=demo5 # 服务器配置 server.port=8080 # Session配置 server.servlet.session.timeout=300s # Redis配置(可选,如果不使用Redis可以注释掉) # spring.redis.host=localhost # spring.redis.port=6379 # spring.redis.password= # spring.redis.database=0 # 使用Redis存储Session(可选) # spring.session.store-type=redis # 日志配置 logging.level.com.example.demo=DEBUG第七步:创建测试类
为了确保我们的代码正常工作,我们创建一个测试类:
package com.example.demo; import com.example.demo.util.CaptchaUtil; import org.junit.jupiter.api.Test; importstatic org.junit.jupiter.api.Assertions.*; /** * 验证码工具类测试 */ publicclass CaptchaUtilTest { @Test public void testGenerateMathExpression() { // 测试生成数学表达式 CaptchaUtil.MathExpression expression = CaptchaUtil.generateMathExpression(); assertNotNull(expression); assertNotNull(expression.getExpression()); assertTrue(expression.getExpression().contains("=")); assertTrue(expression.getExpression().contains("?")); assertTrue(expression.getResult() >= 0); // 结果应该非负 } @Test public void testGenerateCaptchaImage() throws Exception { // 测试生成验证码图片 String expression = "5 + 3 = ?"; String imageBase64 = CaptchaUtil.generateCaptchaImage(expression); assertNotNull(imageBase64); assertTrue(imageBase64.startsWith("data:image/png;base64,")); assertTrue(imageBase64.length() > 100); // Base64字符串应该有一定长度 } @Test public void testMathExpressionClass() { // 测试MathExpression内部类 String expression = "2 + 3 = ?"; int result = 5; CaptchaUtil.MathExpression mathExpression = new CaptchaUtil.MathExpression(expression, result); assertEquals(expression, mathExpression.getExpression()); assertEquals(result, mathExpression.getResult()); } }第八步:运行和测试
8.1 启动应用
在IDE中运行
Demo5Application.java或者使用命令行:
mvn spring-boot:run应用启动后访问:
http://localhost:8080
8.2 测试功能
页面加载:自动显示验证码图片
计算答案:根据图片中的数学表达式计算答案
输入答案:在输入框中输入计算结果
提交验证:点击"提交验证"按钮
刷新验证码:点击"刷新"按钮获取新的验证码
遇到的问题和解决方案
问题1:HttpSession无法解析
错误信息:
无法解析符号 'HttpSession'原因:Spring Boot 3.x使用Jakarta EE,javax.servlet包被替换为jakarta.servlet
解决方案:
// 错误的导入 import javax.servlet.http.HttpSession; // 正确的导入 import jakarta.servlet.http.HttpSession;问题2:CORS跨域问题
错误信息:
Access to fetch at 'http://localhost:8080/captcha' from origin 'null' has been blocked by CORS policy原因:当通过IDE预览页面时,页面运行在不同端口,浏览器阻止跨域请求
解决方案:
创建CORS配置类
使用内存验证码服务替代Session验证码
前端使用绝对URL请求
问题3:Session无法跨域共享
错误信息:
验证总是失败,Session中的答案为空原因:跨域请求时Session无法正确共享
解决方案:
创建基于内存的验证码服务
使用唯一ID关联验证码和答案
前端传递验证码ID进行验证
问题4:Maven命令无法识别
错误信息:
mvn : 无法将"mvn"项识别为 cmdlet、函数、脚本文件或可运行程序的名称原因:Maven没有安装或没有配置环境变量
解决方案:
安装Maven并配置环境变量
或者直接使用IDE运行应用
或者使用项目自带的
Maven Wrapper:./mvnw spring-boot:run
功能特点总结
已实现的功能
数学表达式验证码:比传统字符验证码更友好
图片生成:自动绘制验证码图片
双重存储方案:支持Session和内存两种存储方式
跨域支持:解决了IDE预览时的跨域问题
美观界面:现代化的UI设计
调试信息:控制台输出详细的验证过程
自动刷新:验证失败后自动刷新验证码
技术亮点
Base64图片编码:直接在前端显示图片
ConcurrentHashMap:线程安全的内存存储
UUID唯一标识:确保验证码ID的唯一性
CORS配置:完整的跨域解决方案
异常处理:完善的错误处理机制
扩展功能建议
1. 添加Redis存储
// 可以扩展为使用Redis存储验证码 @Autowired private StringRedisTemplate redisTemplate; public void storeCaptcha(String captchaId, int answer) { redisTemplate.opsForValue().set("captcha:" + captchaId, String.valueOf(answer), 5, TimeUnit.MINUTES); }2. 增加验证码复杂度
// 可以添加更多运算符 String[] operators = {"+", "-", "*", "/"}; // 可以增加数字范围 int a = random.nextInt(20) + 1; // 1-203. 添加验证码过期机制
// 可以添加定时清理过期验证码 @Scheduled(fixedRate = 60000) // 每分钟执行一次 public void cleanExpiredCaptchas() { // 清理逻辑 }4. 增加验证码样式
// 可以添加更多干扰元素 g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256))); g.fillOval(random.nextInt(width), random.nextInt(height), 10, 10);学习总结
通过这个项目,我们学会了:
Spring Boot基础:如何创建Web应用
图片处理:使用Java Graphics API绘制图片
Base64编码:图片的编码和解码
Session管理:HttpSession的使用
跨域问题:CORS的配置和解决
前端交互:JavaScript与后端API的交互
异常处理:完善的错误处理机制
测试驱动:编写单元测试验证功能
结语
恭喜你!你已经成功实现了一个完整的Spring Boot图片验证码功能。这个项目涵盖了:
后端API开发
图片生成和处理
前端页面开发
跨域问题解决
异常处理
单元测试
这个验证码功能可以应用到实际的Web项目中,有效防止恶意攻击。希望这个教程对你有帮助!