Spring?Boot使用Hutool快速集成驗證碼的兩種方案
驗證碼這件“小事”,其實一點也不小
在登錄、注冊、找回密碼這些看似普通的業(yè)務場景里,驗證碼往往是系統(tǒng)安全的第一道防線。如果沒有它,爬蟲、撞庫腳本、暴力請求可以輕松把你的接口打穿;但如果驗證碼實現(xiàn)得不好,又會帶來代碼冗余、維護困難、樣式僵硬等一系列工程問題。
很多項目都會經(jīng)歷這樣一個過程:
- 一開始:“不就是個驗證碼嗎?我自己畫!”
- 后來:干擾線、字體、隨機數(shù)、Session、緩存控制……越寫越多
- 最終:發(fā)現(xiàn)成熟工具庫早就幫你把坑填平了
這篇文章就圍繞一個核心問題展開:Spring Boot 項目里,驗證碼到底該怎么實現(xiàn),才是又穩(wěn)又省事?
我們會完整對比兩種方案:
- 完全自定義實現(xiàn)驗證碼生成邏輯(適合高度定制化需求)
- 基于 Hutool 快速集成多種驗證碼類型(絕大多數(shù)項目的最優(yōu)解)
為什么圖形驗證碼幾乎不可或缺?
驗證碼的本質(zhì)不是“折磨用戶”,而是區(qū)分人和程序。
在以下場景中,它幾乎是必選項:
- 登錄接口防止BL破解
- 注冊接口防止批量刷號
- 密碼重置防止惡意撞庫
- 重要操作前的人機確認
通過 隨機字符 + 干擾元素 的方式,驗證碼能顯著提高腳本攻擊成本,是最基礎、也是最有效的安全措施之一。
實現(xiàn)方式上,常見只有兩條路:
- 自己寫一套驗證碼生成邏輯
- 直接集成成熟工具庫
下面我們一條一條拆。
方案一:完全手寫一個驗證碼工具類
如果你的項目對驗證碼樣式、字體、干擾規(guī)則有非常強的定制需求,那么手寫一套邏輯是不可避免的。
驗證碼工具類設計思路
我們需要完成幾件事:
- 隨機生成驗證碼字符
- 繪制背景
- 繪制干擾線
- 繪制字符
- 將驗證碼結(jié)果存入 Session
- 將圖片輸出到響應流
項目目錄結(jié)構(gòu)
src/main/java
└── com
└── icoderoad
└── common
└── util
└── CaptchaCodeUtil.java
自定義驗證碼工具類實現(xiàn)
package com.icoderoad.common.util;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CaptchaCodeUtil {
public static final String SESSION_KEY = "CAPTCHA_CODE";
private final Random random = new Random();
private final String codeSource = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private int width = 80;
private int height = 26;
private int codeLength = 4;
private int lineCount = 40;
private Font getFont() {
return new Font("Fixedsys", Font.PLAIN, 18);
}
private Color randomColor(int min, int max) {
min = Math.min(min, 255);
max = Math.min(max, 255);
int r = min + random.nextInt(max - min);
int g = min + random.nextInt(max - min);
int b = min + random.nextInt(max - min);
return new Color(r, g, b);
}
private void drawLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
private String drawChar(Graphics g, int index) {
g.setFont(getFont());
g.setColor(randomColor(50, 160));
String ch = String.valueOf(codeSource.charAt(random.nextInt(codeSource.length())));
g.drawString(ch, 15 * index, 18);
return ch;
}
public void generate(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
for (int i = 0; i < lineCount; i++) {
g.setColor(randomColor(120, 200));
drawLine(g);
}
StringBuilder code = new StringBuilder();
for (int i = 1; i <= codeLength; i++) {
code.append(drawChar(g, i));
}
session.setAttribute(SESSION_KEY, code.toString());
g.dispose();
try {
ImageIO.write(image, "JPEG", response.getOutputStream());
} catch (Exception e) {
throw new RuntimeException("生成驗證碼失敗", e);
}
}
}
Controller 中調(diào)用驗證碼工具類
package com.icoderoad.web.controller;
import com.icoderoad.common.util.CaptchaCodeUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
public class CaptchaController {
@GetMapping("/captcha/custom")
public void customCaptcha(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("image/jpeg");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
new CaptchaCodeUtil().generate(request, response);
}
}
?? 這種方式完全可控,但維護成本高、擴展困難
方案二:使用 Hutool 快速集成驗證碼(強烈推薦)
如果你不想把時間浪費在“重復造輪子”上,Hutool 基本就是最優(yōu)解。
引入依賴
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.8.6</version>
</dependency>
Hutool 四種驗證碼實戰(zhàn)示例
統(tǒng)一 Controller 路徑示例:com/icoderoad/web/controller/HutoolCaptchaController.java
1. 線條干擾驗證碼(LineCaptcha)
@GetMapping("/captcha/line")
public void line(HttpServletResponse response) throws IOException {
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(130, 38, 5, 5);
response.setContentType("image/jpeg");
response.setHeader("Cache-Control", "no-cache");
captcha.write(response.getOutputStream());
}
2. 圓圈干擾驗證碼(CircleCaptcha)
@GetMapping("/captcha/circle")
public void circle(HttpServletResponse response) throws IOException {
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(130, 38, 5, 20);
response.setContentType("image/jpeg");
response.setHeader("Cache-Control", "no-cache");
captcha.write(response.getOutputStream());
}
3. 扭曲驗證碼(ShearCaptcha)
@GetMapping("/captcha/shear")
public void shear(HttpServletResponse response) throws IOException {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(130, 38, 5, 5);
response.setContentType("image/jpeg");
response.setHeader("Cache-Control", "no-cache");
captcha.write(response.getOutputStream());
}
4. GIF 動態(tài)驗證碼(GifCaptcha)
@GetMapping("/captcha/gif")
public void gif(HttpServletResponse response) throws IOException {
GifCaptcha captcha = CaptchaUtil.createGifCaptcha(130, 38, 5);
response.setContentType("image/jpeg");
response.setHeader("Cache-Control", "no-cache");
captcha.write(response.getOutputStream());
}
自定義驗證碼內(nèi)容(數(shù)字 / 算術)
純數(shù)字驗證碼
@GetMapping("/captcha/number")
public void number(HttpServletResponse response) throws IOException {
RandomGenerator generator = new RandomGenerator("0123456789", 4);
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 80);
captcha.setGenerator(generator);
captcha.createCode();
captcha.write(response.getOutputStream());
}
算術驗證碼(推薦)
@GetMapping("/captcha/math")
public void math(HttpServletResponse response, HttpSession session) throws IOException {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 45, 4, 4);
captcha.setGenerator(new MathGenerator());
captcha.createCode();
session.setAttribute("MATH_CODE", captcha.getCode());
captcha.write(response.getOutputStream());
}
接口測試建議(Apifox)
- 使用 GET 請求訪問驗證碼接口
- 響應應為圖片而非亂碼
- 多次刷新驗證碼應變化
- 若不變化,優(yōu)先檢查緩存頭設置
關鍵實踐建議
- 驗證碼值必須存 Session / Redis
- 驗證成功后立即清除
- 驗證碼接口應限流
- 高安全場景可疊加短信 / IP 風控
結(jié)語:驗證碼不是炫技,而是工程取舍
- 需要極致定制 → 自定義實現(xiàn)
- 追求效率與穩(wěn)定 → Hutool 一步到位
在真實項目里,99% 的情況你都不需要自己畫驗證碼。把精力留給真正有價值的業(yè)務邏輯,才是成熟工程師的選擇。
到此這篇關于Spring Boot使用Hutool快速集成驗證碼的文章就介紹到這了,更多相關SpringBoot Hutool集成驗證碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
@Scheduled注解不能同時執(zhí)行多個定時任務的解決方案
這篇文章主要介紹了@Scheduled注解不能同時執(zhí)行多個定時任務的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
SpringBoot:JPA + AuditingEntityListener時區(qū)設置方式
這篇文章主要介紹了SpringBoot:JPA + AuditingEntityListener時區(qū)設置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringMVC實現(xiàn)數(shù)據(jù)綁定及表單標簽
這篇文章主要為大家詳細介紹了SpringMVC實現(xiàn)數(shù)據(jù)綁定及表單標簽的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03

