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

J2EE 驗證碼生成與點擊刷新完整實現 (Servlet + AJAX)

     編輯:Java 相關知識

驗證碼生成核心邏輯

使用 java.awt.Graphics2D 繪製干擾線、噪點、隨機字符,輸出 PNG 流。關鍵參數可配置化。

完整工具類


public class CaptchaUtil {
    private static final String CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // 排除易混淆字符
    private static final Random RANDOM = new Random();
    
    // 生成驗證碼圖片
    public static BufferedImage generateImage(int width, int height, String code, int lineCount, double noiseRate) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        
        // 1. 背景
        g.setColor(randomColor(200, 255));
        g.fillRect(0, 0, width, height);
        
        // 2. 干擾線
        for (int i = 0; i < lineCount; i++) {
            g.setColor(randomColor(100, 200));
            g.drawLine(RANDOM.nextInt(width), RANDOM.nextInt(height), 
                       RANDOM.nextInt(width), RANDOM.nextInt(height));
        }
        
        // 3. 噪點
        int noiseArea = (int) (width * height * noiseRate);
        for (int i = 0; i < noiseArea; i++) {
            image.setRGB(RANDOM.nextInt(width), RANDOM.nextInt(height), randomColor(0, 255).getRGB());
        }
        
        // 4. 字符繪製
        int fontSize = Math.min(width * 4 / 5 / code.length(), height * 7 / 10);
        g.setFont(new Font("Arial", Font.BOLD, fontSize));
        FontMetrics fm = g.getFontMetrics();
        int x = (width - fm.stringWidth(code)) / 2;
        int y = (height - fm.getDescent()) / 2 + fm.getAscent();
        
        for (int i = 0; i < code.length(); i++) {
            g.setColor(randomColor(20, 130));
            g.drawString(code.substring(i, i+1), x + i * fm.charWidth(code.charAt(i)), y);
        }
        
        g.dispose();
        return image;
    }
    
    // 生成隨機驗證碼字符串
    public static String generateCode(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(CHARS.charAt(RANDOM.nextInt(CHARS.length())));
        }
        return sb.toString();
    }
    
    private static Color randomColor(int min, int max) {
        int r = clamp(RANDOM.nextInt(max - min + 1) + min);
        int g = clamp(RANDOM.nextInt(max - min + 1) + min);
        int b = clamp(RANDOM.nextInt(max - min + 1) + min);
        return new Color(r, g, b);
    }
    
    private static int clamp(int v) { return Math.max(0, Math.min(255, v)); }
    
    // 圖片轉 byte[]
    public static byte[] toBytes(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        return baos.toByteArray();
    }
}

Servlet 處理器


@WebServlet("/captcha/image")
public class CaptchaImageServlet extends HttpServlet {
    private static final int WIDTH = 120, HEIGHT = 40, LENGTH = 4;
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1. 生成驗證碼
        String code = CaptchaUtil.generateCode(LENGTH);
        
        // 2. 存入 Session (大小寫不敏感驗證時統一轉大寫)
        req.getSession().setAttribute("CAPTCHA_CODE", code.toUpperCase());
        
        // 3. 生成圖片
        BufferedImage image = CaptchaUtil.generateImage(WIDTH, HEIGHT, code, 8, 0.02);
        
        // 4. 輸出響應
        resp.setContentType("image/png");
        resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        resp.setHeader("Pragma", "no-cache");
        resp.setDateHeader("Expires", 0);
        
        try (OutputStream out = resp.getOutputStream()) {
            out.write(CaptchaUtil.toBytes(image));
        }
    }
}

前端頁面 (JSP/HTML)


<form action="/captcha/verify" method="post">
    驗證碼: <input type="text" name="captchaInput" maxlength="4" required>
    <img id="captchaImg" src="/captcha/image" alt="點擊刷新" title="看不清?點擊刷新" style="cursor:pointer; vertical-align:middle;">
    <br>
    <input type="submit" value="提交">
</form>

<script>
document.getElementById('captchaImg').onclick = function() {
    // 加隨機參數防瀏覽器緩存
    this.src = '/captcha/image?' + Date.now();
};
</script>

驗證 Servlet


@WebServlet("/captcha/verify")
public class CaptchaVerifyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String sessionCode = (String) req.getSession().getAttribute("CAPTCHA_CODE");
        String inputCode = req.getParameter("captchaInput");
        
        resp.setContentType("application/json;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        
        boolean valid = sessionCode != null && inputCode != null 
                     && sessionCode.equals(inputCode.toUpperCase());
        
        // 驗證後立即移除,防止重放攻擊
        req.getSession().removeAttribute("CAPTCHA_CODE");
        
        out.write("{\"success\":" + valid + ",\"message\":\"" + (valid ? "驗證通過" : "驗證碼錯誤") + "\"}");
    }
}

Spring Boot 整合版 (REST + Base64)


@RestController
@RequestMapping("/api/captcha")
public class CaptchaController {
    
    @GetMapping("/image")
    public Map getCaptcha(HttpSession session) {
        String code = CaptchaUtil.generateCode(4);
        session.setAttribute("CAPTCHA_CODE", code.toUpperCase());
        
        BufferedImage image = CaptchaUtil.generateImage(120, 40, code, 8, 0.02);
        String base64 = Base64.getEncoder().encodeToString(CaptchaUtil.toBytes(image));
        
        return Map.of("imageBase64", "data:image/png;base64," + base64);
    }
    
    @PostMapping("/verify")
    public Map verify(@RequestParam String code, HttpSession session) {
        String sessionCode = (String) session.getAttribute("CAPTCHA_CODE");
        session.removeAttribute("CAPTCHA_CODE");
        
        boolean valid = sessionCode != null && sessionCode.equals(code.toUpperCase());
        return Map.of("success", valid, "message", valid ? "驗證通過" : "驗證碼錯誤");
    }
}

前端直接 `` 即可,無緩存煩惱。

安全加固建議

  • 限流:同 IP 分鐘請求上限 (如 10 次),防暴力破解。
  • 失效時間:Session 存儲設置 5-10 分鐘 TTL。
  • 字符集優化:排除 0/O、1/I/l、2/Z、8/B 等易混淆字符。
  • 幹擾強度自適應:失敗次數增加 → 加大干擾線/噪點密度。
  • 考慮第三方:Google reCAPTCHA、hCaptcha、滑動驗證 (更友好、更安全)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved