驗證碼是區分人機的第一道防線。本文系統梳理圖形驗證碼生成流程、滑塊拼圖驗證邏輯、行為分析無感驗證架構,提供生產級代碼骨架與安全加固建議。
1. 參數初始化
- 畫布尺寸 (寬×高),字符集、字體、顏色範圍
- 干擾元素:干擾線、噪點、幹擾圓、扭曲變換
2. 圖像緩衝創建
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
3. 背景填充
g.setColor(randomLightColor());
g.fillRect(0, 0, width, height);
4. 干擾線繪製 (5-10 條)
for (int i = 0; i < lineCount; i++) {
g.setColor(randomColor());
g.drawLine(randomX(), randomY(), randomX(), randomY());
}
5. 驗證碼字符繪製 (4-6 位)
StringBuilder code = new StringBuilder();
for (int i = 0; i < length; i++) {
char c = randomChar();
code.append(c);
g.setFont(randomFont());
g.setColor(randomDarkColor());
g.rotate(randomAngle(), x, y); // 字符級旋轉
g.drawString(String.valueOf(c), x, y);
g.rotate(-randomAngle(), x, y); // 恢復
x += charWidth + gap;
}
6. 干擾噪點 (可選)
for (int i = 0; i < noiseCount; i++) {
g.setColor(randomColor());
g.drawRect(randomX(), randomY(), 1, 1);
}
7. 釋放資源 & 輸出
g.dispose();
ImageIO.write(image, "jpeg", outputStream); // 或 PNG
8. 會話存儲
session.setAttribute("captcha_code", code.toString().toLowerCase());
session.setAttribute("captcha_time", System.currentTimeMillis());
public class CaptchaUtil {
private static final String CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"; // 排除易混淆字符
private static final Color[] FONT_COLORS = {Color.BLACK, Color.BLUE, Color.RED, Color.GREEN};
private static final Font[] FONTS = {
new Font("Arial", Font.BOLD, 24),
new Font("Courier New", Font.BOLD | Font.ITALIC, 26),
new Font("Verdana", Font.BOLD, 22)
};
public static BufferedImage generate(StringBuilder codeHolder, int width, int height, int length) {
// 實現上述流程...
}
private static char randomChar() { return CHARS.charAt(random.nextInt(CHARS.length())); }
private static Color randomColor() { return new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)); }
private static Color randomLightColor() { return new Color(200 + random.nextInt(55), 200 + random.nextInt(55), 200 + random.nextInt(55)); }
private static Color randomDarkColor() { return new Color(random.nextInt(100), random.nextInt(100), random.nextInt(100)); }
private static Font randomFont() { return FONTS[random.nextInt(FONTS.length)]; }
private static double randomAngle() { return (random.nextDouble() - 0.5) * 0.6; } // ±0.3 rad
}
@Controller
public class CaptchaController {
@GetMapping("/captcha/image")
public void imageCaptcha(HttpServletResponse response, HttpSession session) throws IOException {
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
StringBuilder code = new StringBuilder();
BufferedImage image = CaptchaUtil.generate(code, 120, 40, 4);
session.setAttribute("captcha_code", code.toString().toLowerCase());
session.setAttribute("captcha_time", System.currentTimeMillis());
ImageIO.write(image, "JPEG", response.getOutputStream());
}
@PostMapping("/captcha/verify")
@ResponseBody
public Result verify(@RequestParam String input, HttpSession session) {
String code = (String) session.getAttribute("captcha_code");
Long time = (Long) session.getAttribute("captcha_time");
if (code == null || time == null) return Result.fail("驗證碼已過期");
if (System.currentTimeMillis() - time > 5 * 60 * 1000) return Result.fail("驗證碼已過期");
if (!code.equals(input.toLowerCase())) return Result.fail("驗證碼錯誤");
session.removeAttribute("captcha_code"); // 一次性使用
return Result.ok();
}
}
| 階段 | 後端職責 | 前端職責 |
|---|---|---|
| 初始化 | 1. 隨機選取背景圖 2. 生成隨機缺口座標 (x, y) 3. 裁剪滑塊圖片 4. 返回 {bgUrl, sliceUrl, token, 期望x座標(加密)} | 1. 請求初始化數據 2. 渲染背景圖 + 滑塊 3. 綁定拖拽事件 |
| 驗證 | 1. 解密期望座標 2. 計算誤差 |用戶x - 期望x| 3. 判定閾值 (通常 ±5px) 4. 記錄軌跡數據供風控分析 | 1. 收集滑動軌跡 (x, y, t) 2. 提交 {token, 用戶x, 軌跡數據} |
// 風控引擎輸入特徵
class RiskFeatures {
// 設備指紋
String deviceId, userAgent, screenResolution, timezone, language;
// 行為序列
List events; // click, scroll, mousemove, keydown, touch
// 環境上下文
String ip, geoLocation, isp, proxyScore;
// 業務上下文
String actionType, referrer, sessionId;
}
// 規則引擎 + 機器學習模型
class RiskEngine {
public RiskResult evaluate(RiskFeatures features) {
// 1. 規則檢查 (白名單、黑名單、頻率限制)
// 2. 模型推理 (XGBoost/LightGBM/神經網絡) -> 風險分 0-100
// 3. 決策邏輯
if (score < 20) return RiskResult.PASS; // 直通
if (score < 60) return RiskResult.CAPTCHA; // 彈出驗證碼
if (score < 90) return RiskResult.SLIDER; // 滑塊驗證
return RiskResult.BLOCK; // 直接攔截
}
}
| 類型 | 開源/商業 | 特點 |
|---|---|---|
| kaptcha / simple-captcha | 開源 | 輕量圖形驗證碼,易整合 |
| Geetest (極驗) | 商業 | 滑塊+行為分析,免部署 SDK |
| hCaptcha / reCAPTCHA v3 | 商業 | 無感行為分析,全球威脅情報 |
| Tencent Cloud Captcha | 商業 | 國內訪問快、合規、多模式 |