Spring Boot圖片驗證碼功能實現詳解 - 從零開始到完美運行

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;}}
}

代碼解釋

  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
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;}}
}

兩種服務類的區別

  1. Session服務:將答案存儲在HttpSession中,適合同域訪問
  2. 內存服務:將答案存儲在內存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>

前端功能解釋

  1. 頁面加載時:自動獲取驗證碼圖片
  2. 刷新按鈕:重新獲取新的驗證碼
  3. 提交驗證:發送用戶輸入的答案到后端驗證
  4. 消息提示:顯示驗證成功或失敗的消息

?? 第六步:配置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 啟動應用

  1. 在IDE中運行Demo5Application.java
  2. 或者使用命令行:mvn spring-boot:run
  3. 應用啟動后訪問:http://localhost:8080

8.2 測試功能

  1. 頁面加載:自動顯示驗證碼圖片
  2. 計算答案:根據圖片中的數學表達式計算答案
  3. 輸入答案:在輸入框中輸入計算結果
  4. 提交驗證:點擊"提交驗證"按鈕
  5. 刷新驗證碼:點擊"刷新"按鈕獲取新的驗證碼

遇到的問題和解決方案

問題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預覽頁面時,頁面運行在不同端口,瀏覽器阻止跨域請求

解決方案

  1. 創建CORS配置類
  2. 使用內存驗證碼服務替代Session驗證碼
  3. 前端使用絕對URL請求

問題3:Session無法跨域共享

錯誤信息

驗證總是失敗,Session中的答案為空

原因:跨域請求時Session無法正確共享

解決方案

  1. 創建基于內存的驗證碼服務
  2. 使用唯一ID關聯驗證碼和答案
  3. 前端傳遞驗證碼ID進行驗證

問題4:Maven命令無法識別

錯誤信息

mvn : 無法將"mvn"項識別為 cmdlet、函數、腳本文件或可運行程序的名稱

原因:Maven沒有安裝或沒有配置環境變量

解決方案

  1. 安裝Maven并配置環境變量
  2. 或者直接使用IDE運行應用
  3. 或者使用項目自帶的Maven Wrapper:./mvnw spring-boot:run

📊 功能特點總結

? 已實現的功能

  1. 數學表達式驗證碼:比傳統字符驗證碼更友好
  2. 圖片生成:自動繪制驗證碼圖片
  3. 雙重存儲方案:支持Session和內存兩種存儲方式
  4. 跨域支持:解決了IDE預覽時的跨域問題
  5. 美觀界面:現代化的UI設計
  6. 調試信息:控制臺輸出詳細的驗證過程
  7. 自動刷新:驗證失敗后自動刷新驗證碼

🎯 技術亮點

  1. Base64圖片編碼:直接在前端顯示圖片
  2. ConcurrentHashMap:線程安全的內存存儲
  3. UUID唯一標識:確保驗證碼ID的唯一性
  4. CORS配置:完整的跨域解決方案
  5. 異常處理:完善的錯誤處理機制

🔮 擴展功能建議

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);

學習總結

通過這個項目,我們學會了:

  1. Spring Boot基礎:如何創建Web應用
  2. 圖片處理:使用Java Graphics API繪制圖片
  3. Base64編碼:圖片的編碼和解碼
  4. Session管理:HttpSession的使用
  5. 跨域問題:CORS的配置和解決
  6. 前端交互:JavaScript與后端API的交互
  7. 異常處理:完善的錯誤處理機制
  8. 測試驅動:編寫單元測試驗證功能

結語

恭喜你!你已經成功實現了一個完整的Spring Boot圖片驗證碼功能。這個項目涵蓋了:

  • ? 后端API開發
  • ? 圖片生成和處理
  • ? 前端頁面開發
  • ? 跨域問題解決
  • ? 異常處理
  • ? 單元測試

這個驗證碼功能可以應用到實際的Web項目中,有效防止惡意攻擊。希望這個教程對你有幫助!

如果你有任何問題或建議,歡迎在評論區留言討論!


項目源碼:所有代碼都已經在文章中完整提供,可以直接復制使用。

運行環境:Java 17 + Spring Boot 3.5.5 + Maven

測試地址:http://localhost:8080

祝學習愉快!🚀

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98537.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98537.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98537.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HarmonyOS實現快遞APP自動識別地址

? 大家好&#xff0c;我是潘Sir&#xff0c;持續分享IT技術&#xff0c;幫你少走彎路。《鴻蒙應用開發從入門到項目實戰》系列文章持續更新中&#xff0c;歡迎關注&#xff01; 隨著鴻蒙&#xff08;HarmonyOS&#xff09;生態發展&#xff0c;越來越多的APP需要進行鴻蒙適…

CUDA編程13 - 測量每個Block的執行時間

一:概述 GPU 程序性能不是靠 CPU 那樣的“順序執行”來衡量的,而是靠線程塊(block)和多處理器(SM)利用率。每個 block 在 GPU 的不同多處理器上執行,順序不確定。傳統的 kernel 總體計時(比如 cudaEvent 計時整個 kernel)只能知道總時間,無法分析哪個 block 慢,為什…

敏捷開發-Scrum(下)

Scrum 核心構成&#xff1a;團隊、事件與工件的協同價值體系 在 Scrum 框架中&#xff0c;“團隊、事件、工件” 并非孤立的模塊&#xff0c;而是相互咬合的有機整體&#xff1a;Scrum 團隊是價值交付的執行核心&#xff0c;Scrum 事件是節奏把控與反饋調整的機制載體&#xff…

LeetCode 單調棧 739. 每日溫度

739. 每日溫度給定一個整數數組 temperatures &#xff0c;表示每天的溫度&#xff0c;返回一個數組 answer &#xff0c;其中 answer[i] 是指對于第 i 天&#xff0c;下一個更高溫度出現在幾天后。如果氣溫在這之后都不會升高&#xff0c;請在該位置用 0 來代替。 示例 1: 輸入…

Java-面試八股文-JVM篇

JVM篇 一.在JVM中&#xff0c;什么是程序計數器? 在 JVM&#xff08;Java Virtual Machine&#xff09; 中&#xff0c;程序計數器&#xff08;Program Counter Register&#xff0c;簡稱 PC 寄存器&#xff09; 是一塊較小的內存空間&#xff0c;用于記錄 當前線程所執行的字…

微算法科技(NASDAQ: MLGO)采用量子相位估計(QPE)方法,增強量子神經網絡訓練

隨著量子計算技術的迅猛發展&#xff0c;傳統計算機在處理復雜問題時所遇到的算力瓶頸日益凸顯。量子計算以其獨特的并行計算能力和指數級增長的計算潛力&#xff0c;為解決這些問題提供了新的途徑。微算法科技&#xff08;NASDAQ: MLGO&#xff09;探索量子技術在各種應用場景…

MySQL 備份的方法和最佳實踐

MySQL 是一種流行的開源關系數據庫管理系統&#xff0c;用于在線應用程序和數據倉庫。它以可靠性、有效性和簡單性而聞名。然而&#xff0c;與任何計算機系統一樣&#xff0c;由于硬件故障、軟件缺陷或其他不可預見的情況&#xff0c;存在數據丟失的可能性。因此&#xff0c;保…

應用層自定義協議、序列化和反序列化

1.自定義協議開發者根據特定應用場景的需要&#xff0c;自行設計和制定的通信規則和數據格式 1.1 核心組成部分一個典型的自定義協議通常包含以下幾個關鍵部分&#xff1a;?幀/報文格式 (Frame/Packet Format)??&#xff1a;定義了數據是如何打包的。這通常包括&#xff1a…

Excel VBA 中可用的工作表函數

Visual Basic for Applications (VBA) 中可用的工作表函數。可以在 VBA 中通過 Application.WorksheetFunction 對象調用。 下面我將按照字母分組&#xff0c;對每個函數進行簡要解釋&#xff0c;并給出在 VBA 中使用的示例。A 組Acos: 返回數字的反余弦值。 result Applicati…

OpenWrt + Docker 完整部署方案:CFnat + Cloudflared 一體化集成

AI生成&#xff08;可能是AI幻覺&#xff09; 項目架構概述 基于您現有的網絡配置&#xff08;IP: 192.168.1.1&#xff09;&#xff0c;本方案將CFnat服務作為網絡優化層整合到現有的Cloudflare隧道架構中&#xff0c;實現完整的網絡加速解決方案。 優化后的流量路徑 用戶訪問…

蒼穹外賣項目實戰(day7-1)-緩存菜品和緩存套餐功能-記錄實戰教程、問題的解決方法以及完整代碼

完整資料下載 通過網盤分享的文件&#xff1a;蒼穹外賣 鏈接: https://pan.baidu.com/s/1JJaFOodXOF_lNJSUiZ6qtw?pwdps2t 提取碼: ps2t 目錄 1、緩存菜品 &#xff08;1&#xff09;問題說明 &#xff08;2&#xff09;使用redis緩存部分數據 1-2、代碼完善 &#xff…

計算機畢業設計 基于Python+Django的醫療數據分析系統

精彩專欄推薦訂閱&#xff1a;在 下方專欄&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主頁&#xff1a;計算機畢設木哥&#x1f525; &#x1f496; 文章目錄 一、項目介紹二…

使用 chromedp 高效爬取 Bing 搜索結果

在數據采集領域&#xff0c;搜索引擎結果是重要的信息來源。但傳統爬蟲面對現代瀏覽器渲染的頁面時&#xff0c;常因 JavaScript 動態加載、跳轉鏈接加密等問題束手無策。本文將詳細介紹如何使用 Go 語言的chromedp庫&#xff0c;模擬真實瀏覽器行為爬取 Bing 搜索結果&#xf…

遺漏的需求

“編寫執行者的目的&#xff0c;僅用別名來表達需要傳遞的數據”&#xff0c;就如客戶信息用名字和地址表示一樣&#xff0c;這是一個很好的建議。然而&#xff0c;對程序員來說&#xff0c;這沒有提供軟件開發所必需的詳細信息。程序設計人員和用戶界面設計者需要準確地知道地…

《云原生故障診療指南:從假活到配置漂移的根治方案》

當云原生架構成為企業數字化轉型的標配,系統故障的形態也隨之發生了根本性變化。曾經那些“一目了然”的報錯信息逐漸消失,取而代之的是“指標正常卻服務不可用”“偶發故障無規律可循”等隱性問題。這些故障如同架構中的“暗物質”,看不見卻持續影響著系統的穩定性,其排查…

“從零到一:使用GitLab和Jenkins實現自動化CI/CD流水線”

GitLab倉庫 簡單的來說就是開發人員提交代碼的倉庫&#xff0c;用于團隊開發&#xff0c;GitLab 上托管的倉庫通常作為遠程倉庫使用&#xff0c;開發人員可以將本地的 Git 倉庫推送到 GitLab 上&#xff0c;也可以從 GitLab 克隆倉庫到本地進行開發。 Jenkins Jenkins 是一個開…

3D開發工具HOOPS助力造船業數字化轉型,打造更高效、更智能的船舶設計與協作!

造船業是一個高度復雜且競爭激烈的行業&#xff0c;涵蓋船體設計、結構分析、生產制造到運維管理的完整生命周期。面對龐大的CAD數據、多方協作的復雜流程以及數字化轉型的迫切需求&#xff0c;傳統工具往往顯得力不從心。 Tech Soft 3D的HOOPS SDK系列&#xff0c;正以其卓越…

Python調用MCP:無需重構,快速為現有應用注入AI與外部服務能力!

文章目錄 ?? 介紹 ?? ?? 演示環境 ?? ? MCP核心概念:AI世界的“USB-C” ? ??? MCP安裝與基礎使用 ??? ?? 安裝模塊 ?? 創建第一個MCP服務端 ?? Python中MCP客戶端的調用方案 ?? ?? 概述 ?? 深度解析 ?? 參數詳情 ?? 常用方法 ?? 不同傳輸協…

【鏈表】3.重排鏈表(medium)

重排鏈表&#xff08;medium&#xff09;題?描述&#xff1a;解法&#xff1a;算法思路&#xff1a;算法代碼&#xff1a;題?鏈接&#xff1a;143. 重排鏈表 題?描述&#xff1a; 給定?個單鏈表 L 的頭節點 head &#xff0c;單鏈表 L 表?為&#xff1a; L(0) → L(1) →…

蜜罐平臺-Hfish部署

Hfish簡介&#xff1a; HFish是一款社區型免費蜜罐&#xff0c;側重企業安全場景&#xff0c;從內網失陷檢測、外網威脅感知、威脅情報生產三個場景出發&#xff0c;為用戶提供可獨立操作且實用的功能&#xff0c;通過安全、敏捷、可靠的中低交互蜜罐增加用戶在失陷感知和威脅…