首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 圖片驗證碼生成完整實戰:Servlet + 繪圖 API + 前端刷新機制

     編輯:Java 相關知識

效果預覽

4 位隨機數字 + 干擾線 + 隨機顏色 + 隨機字體大小/位置,點擊「看不清」即時刷新無緩存。

後端核心 Servlet

@WebServlet("/captcha")
public class CaptchaServlet extends HttpServlet {
    private static final int WIDTH = 120;
    private static final int HEIGHT = 40;
    private static final int CODE_COUNT = 4;
    private static final int LINE_COUNT = 8;
    private static final String CHARS = "0123456789"; // 可擴展字母

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 禁止緩存
        resp.setHeader("Pragma", "No-cache");
        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", 0);
        resp.setContentType("image/jpeg");

        // 2. 創建畫布
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

        // 3. 繪製背景
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        // 4. 繪製邊框 (可選)
        g.setColor(Color.LIGHT_GRAY);
        g.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        // 5. 生成驗證碼並繪製文字
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        Font font = new Font("Consolas", Font.BOLD, 24);
        g.setFont(font);

        for (int i = 0; i < CODE_COUNT; i++) {
            char c = CHARS.charAt(random.nextInt(CHARS.length()));
            code.append(c);
            // 隨機顏色
            g.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)));
            // 隨機位置 (x 間距固定,y 隨機)
            int x = i * (WIDTH / CODE_COUNT) + 5;
            int y = 20 + random.nextInt(15);
            g.drawString(String.valueOf(c), x, y);
        }

        // 6. 繪製干擾線
        for (int i = 0; i < LINE_COUNT; i++) {
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            g.drawLine(x1, y1, x2, y2);
        }

        // 7. 釋放資源
        g.dispose();

        // 8. 輸出圖片
        ImageIO.write(image, "JPEG", resp.getOutputStream());

        // 9. 存入 Session 供驗證
        req.getSession().setAttribute("captcha_code", code.toString());
    }
}

前端頁面整合

<form action="/login" method="post">
    用戶名: <input name="username" /><br/>
    密碼: <input type="password" name="password" /><br/>
    驗證碼: <input name="captcha" maxlength="4" />
    <img id="captchaImg" src="/captcha" style="vertical-align:middle; cursor:pointer;" title="點擊刷新" />
    <a href="javascript:void(0)" onclick="refreshCaptcha()">看不清</a><br/>
    <button type="submit">登錄</button>
</form>

<script>
function refreshCaptcha() {
    var img = document.getElementById('captchaImg');
    // 關鍵:加時間戳參數強制瀏覽器重新請求,繞過緩存
    img.src = '/captcha?' + Date.now();
}
$('#captchaImg').click(refreshCaptcha); // 點擊圖片也可刷新
</script>

後端驗證邏輯 (登錄 Controller)

@PostMapping("/login")
public String login(@RequestParam String username,
                    @RequestParam String password,
                    @RequestParam String captcha,
                    HttpSession session,
                    Model model) {
    String realCode = (String) session.getAttribute("captcha_code");
    if (realCode == null || !realCode.equalsIgnoreCase(captcha)) {
        model.addAttribute("error", "驗證碼錯誤");
        return "login";
    }
    // 清除驗證碼,防止重放
    session.removeAttribute("captcha_code");
    
    // 後續業務驗證...
    return "redirect:/home";
}

進階增強方案

需求實現要點
字母+數字混合CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" (去除易混淆字符)
中文驗證碼字庫取常用 3000 字,g.drawString(String.valueOf(ch), x, y) 需確保字體支持中文
幹擾曲線/噪點g.drawArc() 畫貝塞爾曲線;g.fillRect(x,y,1,1) 畫噪點
扭曲變形AffineTransform 仿射變換扭曲文字
GIF 動態碼用 AnimatedGifEncoder 生成多幀 GIF,每幀字符位置微調
分佈式 Session驗證碼存 Redis,Key: captcha:{sessionId},TTL 5 分鐘
防暴力破解同 IP 5 分鐘錯誤 > 5 次封禁 15 分鐘,需配合限流框架

現代替代方案

  • Google reCAPTCHA v3:無感驗證,返回風險分數,前端無需輸入
  • hCaptcha:隱私友好,同類驗證碼服務
  • 滑塊/點選/旋轉驗證:極驗、網易易盾等第三方 SDK,體驗更好、安全性更高
  • Spring Boot + Google Kaptcha:implementation 'com.github.penggle:kaptcha:2.3.2',一行配置即可

總結

手寫驗證碼 Servlet 只有 50 行核心代碼,涵蓋 BufferedImage、Graphics2D、ImageIO、Session 管理、Cache-Control 等核心知識點。但生產環境強烈建議接入成熟第三方驗證服務,自研僅適合學習、內部低風險系統或無法聯網環境。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved