驗證碼是表單防刷的基礎機制。本文示範 Spring MVC 後端生成驗證碼存入 Session、前端 jQuery Ajax 非同步驗證的完整流程,含程式碼與關鍵細節說明。
用戶輸入驗證碼 → blur 事件觸發 Ajax → 後端從 Session 取值比對 → 回傳 1/0 → 前端顯示綠勾/紅叉
public class CaptchaUtil {
public static String generate() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 6; i++) {
sb.append((int) (Math.random() * 10)); // 0-9
}
return sb.toString();
}
}
@Controller
public class CaptchaController {
@GetMapping("/captcha")
public void generateCaptcha(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String code = CaptchaUtil.generate();
req.getSession().setAttribute("captcha", code);
// 實際專案通常回傳圖片,此處簡化為文字示範
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().write(code);
}
@PostMapping("/verify")
@ResponseBody
public String verify(@RequestParam("code") String input, HttpSession session) {
String saved = (String) session.getAttribute("captcha");
if (input != null && input.equals(saved)) {
return "1"; // 成功
}
return "0"; // 失敗
}
}
<input type="text" id="captchaInput" placeholder="請輸入驗證碼" />
<span id="captchaMsg"></span>
<button id="refreshCaptcha">換一張</button>
$(function() {
// 離開欄位時自動驗證
$("#captchaInput").blur(function() {
verifyCaptcha($(this).val());
});
// 手動重新整理
$("#refreshCaptcha").click(function() {
loadCaptcha();
});
function loadCaptcha() {
$.get("/captcha?t=" + Date.now(), function(code) {
// 若後端回傳圖片,這裡設定 img src
console.log("新驗證碼:" + code);
});
}
function verifyCaptcha(code) {
if (!code) return;
$.post("/verify", { code: code }, function(result) {
if (result === "1") {
$("#captchaMsg").html("<font color='#339933'>✓ 驗證碼正確</font>");
} else {
$("#captchaMsg").html("<font color='red'>✗ 驗證碼錯誤,請重新輸入</font>");
}
});
}
// 頁面載入時初始化
loadCaptcha();
});
| 項目 | 建議 |
|---|---|
| 驗證碼複雜度 | 生產環境建議:字母+數字、干擾線、扭曲、點擊選字等 |
| Session 並發 | 若使用分散式 Session(Redis),確保序列化正確 |
| 防暴力破解 | 驗證失敗 3-5 次鎖定、要求重新獲取、IP 限流 |
| 一次性原則 | 驗證成功後 session.removeAttribute("captcha"),防重複使用 |
| 圖形驗證碼 | 使用 BufferedImage + Graphics2D 繪製,回傳 image/png |
| 前端體驗 | 輸入框旁顯示圖片、點擊圖片刷新、輸入長度限制 maxlength="6" |
@GetMapping("/captcha/image")
public void captchaImage(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int w = 120, h = 40;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
g.setColor(Color.WHITE); g.fillRect(0, 0, w, h);
g.setFont(new Font("Consolas", Font.BOLD, 28));
g.setColor(Color.BLUE);
String code = CaptchaUtil.generate();
req.getSession().setAttribute("captcha", code);
// 繪製干擾線
for (int i = 0; i < 5; i++) {
g.drawLine(random(0,w), random(0,h), random(0,w), random(0,h));
}
g.drawString(code, 15, 30);
g.dispose();
resp.setContentType("image/png");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", 0);
ImageIO.write(img, "PNG", resp.getOutputStream());
}
核心要點:後端生成 → Session 存儲 → Ajax 比對 → 一次性失效。實務上建議整合 Google reCAPTCHA、hCaptcha 或滑塊驗證等成熟方案,兼顧安全與用戶體驗。