Spring Boot圖片驗證碼功能實現詳解 - 從零開始到完美運行
📖 前言
大家好!今天我要和大家分享一個非常實用的功能: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;/*** 驗證碼工具類* 用于生成數學表達式驗證碼圖片*/
public class CaptchaUtil {/*** 生成隨機數學表達式和答案* @return MathExpression 包含表達式和答案的對象*/public static MathExpression generateMathExpression() {Random random = new Random();int a = random.nextInt(10) + 1; // 1-10int 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 + " = ?";return new 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);}/*** 內部類用于存儲表達式和結果*/public static class MathExpression {private String expression;private int result;public MathExpression(String expression, int result) {this.expression = expression;this.result = result;}public String getExpression() {return expression;}public int getResult() {return result;}}
}
代碼解釋:
-
generateMathExpression()
方法:- 生成兩個1-10的隨機數
- 隨機選擇加法或減法
- 確保減法結果不為負數
- 返回表達式字符串和正確答案
-
generateCaptchaImage()
方法:- 創建120x40像素的圖片
- 設置白色背景
- 繪制5條隨機顏色的干擾線
- 在圖片上繪制數學表達式
- 將圖片轉換為Base64字符串返回
-
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
public class 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("驗證成功");return true;}System.out.println("驗證失敗");return false;} catch (NumberFormatException e) {System.out.println("數字格式錯誤: " + userAnswer);return false;}}
}
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
public class MemoryCaptchaService {// 使用ConcurrentHashMap存儲驗證碼,key為驗證碼ID,value為答案private final 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());// 生成唯一驗證碼IDString captchaId = UUID.randomUUID().toString();// 存儲答案到內存中captchaStorage.put(captchaId, mathExpression.getResult());System.out.println("生成驗證碼ID: " + captchaId + ", 答案: " + mathExpression.getResult());return new 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("驗證成功");return true;}System.out.println("驗證失敗");return false;} catch (NumberFormatException e) {System.out.println("數字格式錯誤: " + userAnswer);return false;}}/*** 驗證碼響應類*/public static class 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
public class CaptchaController {@Autowiredprivate CaptchaService captchaService;@Autowiredprivate MemoryCaptchaService memoryCaptchaService;/*** 顯示驗證頁面* @param model 模型對象* @return 頁面名稱*/@GetMapping("/")public String index(Model model) {return "index";}/*** 獲取驗證碼圖片(基于Session)* @return Base64編碼的驗證碼圖片*/@GetMapping("/captcha")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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: 0 0 10px rgba(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: 10px 15px;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');// 加載驗證碼function loadCaptcha() {fetch('http://localhost:8080/memory-captcha').then(response => {if (!response.ok) {throw new Error('網絡響應不正常');}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 {throw new Error('驗證失敗');}}).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
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("*").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").allowCredentials(true).maxAge(3600);}@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.addAllowedOriginPattern("*");configuration.addAllowedMethod("*");configuration.addAllowedHeader("*");configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}
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;
import static org.junit.jupiter.api.Assertions.*;/*** 驗證碼工具類測試*/
public class CaptchaUtilTest {@Testpublic void testGenerateMathExpression() {// 測試生成數學表達式CaptchaUtil.MathExpression expression = CaptchaUtil.generateMathExpression();assertNotNull(expression);assertNotNull(expression.getExpression());assertTrue(expression.getExpression().contains("="));assertTrue(expression.getExpression().contains("?"));assertTrue(expression.getResult() >= 0); // 結果應該非負}@Testpublic 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字符串應該有一定長度}@Testpublic 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-20
3. 添加驗證碼過期機制
// 可以添加定時清理過期驗證碼
@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項目中,有效防止惡意攻擊。希望這個教程對你有幫助!
如果你有任何問題或建議,歡迎在評論區留言討論!
項目源碼:所有代碼都已經在文章中完整提供,可以直接復制使用。
運行環境:Java 17 + Spring Boot 3.5.5 + Maven
測試地址:http://localhost:8080
祝學習愉快!🚀