技術選型:為什麼選 Patchca?
- 輕量級、零依賴(僅需
patchca.jar)
- 支援多種干擾效果:曲線、大理石、雙波紋、抖動、擴散
- 可自定義字符集、顏色、字體、尺寸
- 線程安全,適合高併發場景
Maven 依賴
<dependency>
<groupId>com.github.bingoohuang</groupId>
<artifactId>patchca</artifactId>
<version>0.0.1</version>
</dependency>
驗證碼 Servlet 完整實現
@WebServlet("/captcha.jpg")
public class CaptchaServlet extends HttpServlet {
private static final ConfigurableCaptchaService CAPTCHA_SERVICE = new ConfigurableCaptchaService();
private static final Random RANDOM = new Random();
static {
// 1. 自定義顏色工廠:隨機生成深色字符
CAPTCHA_SERVICE.setColorFactory(new ColorFactory() {
@Override public Color getColor(int x) {
int[] rgb = new int[3];
int darkIdx = RANDOM.nextInt(3);
for (int i = 0; i < 3; i++) {
rgb[i] = (i == darkIdx) ? RANDOM.nextInt(71) : RANDOM.nextInt(256);
}
return new Color(rgb[0], rgb[1], rgb[2]);
}
});
// 2. 字符工廠:排除易混淆字符 (0/O, 1/I/l 等)
RandomWordFactory wordFactory = new RandomWordFactory();
wordFactory.setCharacters("23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz");
wordFactory.setMinLength(4);
wordFactory.setMaxLength(4);
CAPTCHA_SERVICE.setWordFactory(wordFactory);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 隨機選擇干擾效果
switch (RANDOM.nextInt(5)) {
case 0: CAPTCHA_SERVICE.setFilterFactory(new CurvesRippleFilterFactory(CAPTCHA_SERVICE.getColorFactory())); break;
case 1: CAPTCHA_SERVICE.setFilterFactory(new MarbleRippleFilterFactory()); break;
case 2: CAPTCHA_SERVICE.setFilterFactory(new DoubleRippleFilterFactory()); break;
case 3: CAPTCHA_SERVICE.setFilterFactory(new WobbleRippleFilterFactory()); break;
case 4: CAPTCHA_SERVICE.setFilterFactory(new DiffuseRippleFilterFactory()); break;
}
// 禁止緩存
resp.setHeader("Pragma", "No-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", 0);
resp.setContentType("image/png");
// 生成驗證碼並寫入輸出流
String token = EncoderHelper.getChallangeAndWriteImage(CAPTCHA_SERVICE, "png", resp.getOutputStream());
// 存入 Session
HttpSession session = req.getSession(true);
session.setAttribute("captcha_token", token.toLowerCase()); // 統一小寫比對
}
}
前端整合與驗證
HTML 模板
<img id="captchaImg" src="/captcha.jpg" onclick="this.src='/captcha.jpg?t='+Date.now()" style="cursor:pointer">
<input type="text" name="captcha" placeholder="輸入驗證碼">
後端驗證(Controller/Filter)
@PostMapping("/login")
public Result login(@RequestParam String username, @RequestParam String password,
@RequestParam String captcha, HttpSession session) {
String expected = (String) session.getAttribute("captcha_token");
if (expected == null || !expected.equals(captcha.toLowerCase())) {
return Result.error("驗證碼錯誤或已過期");
}
session.removeAttribute("captcha_token"); // 一次性使用
// 後續業務邏輯...
return Result.success();
}
進階客製化
| 需求 | 實現方式 |
| 自定義字體/字號 | wordFactory.setFont(new Font("Arial", Font.BOLD, 30)) |
| 調整圖片尺寸 | 繼承 ConfigurableCaptchaService 重寫 getWidth()/getHeight() |
| 添加背景圖片 | 自定義 FilterFactory 在 createImage() 中繪製背景 |
| 算術題驗證碼 | 實現 WordFactory 生成 "12+5=" 類似字符串,前端提交計算結果 |
| 分佈式 Session | 配合 Spring Session + Redis 共享驗證碼 Token |
安全性加固建議
- 限流:同一 IP 每分鐘請求驗證碼不超過 N 次
- 有效期:Session 設置 5-10 分鐘過期,或用 Redis 存儲 Token 並設置 TTL
- 防暴力破解:連續驗證失敗 5 次鎖定賬號/IP 15 分鐘
- HTTPS:傳輸層加密防止中間人竊取驗證碼圖片