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

Java 驗證碼生成完整實作:支援大小寫字母、數字、隨機字體、干擾線與 Servlet/JSP 整合

     編輯:Java 相關知識

Java 圖形驗證碼生成器完整實戰教學

驗證碼是 Web 應用防機器人核心機制。本文提供生產級驗證碼生成工具類,支援字元混合、隨機字體、顏色干擾、干擾線、Session 儲存,並示範 Servlet/JSP/JS 整合部署。

核心工具類:ValidCodeUtils

public class ValidCodeUtils {
    // 尺寸配置
    public static int WIDTH = 60;
    public static int HEIGHT = 20;
    
    // 顏色範圍 (RGB 0-255) - 背景淺、干擾中、前景深
    public static int COLOR_FC_BG = 200, COLOR_BC_BG = 250;   // 背景
    public static int COLOR_FC_LINE = 160, COLOR_BC_LINE = 200; // 干擾線
    public static int COLOR_FC_CODE = 20, COLOR_BC_CODE = 170;  // 驗證碼文字
    
    // 隨機顏色生成器
    private static Color getRandColor(int fc, int bc) {
        Random random = new Random();
        fc = Math.max(0, Math.min(255, fc));
        bc = Math.max(0, Math.min(255, bc));
        if (bc == fc) bc += 10;
        if (bc < fc) { int t = fc; fc = bc; bc = t; }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
    
    // 隨機字元: 大寫/小寫/數字 各 1/3 機率
    public static String getRandomChar() {
        int type = (int) Math.round(Math.random() * 2); // 0,1,2
        switch (type) {
            case 0: return String.valueOf((char) (Math.round(Math.random() * 25) + 65)); // A-Z
            case 1: return String.valueOf((char) (Math.round(Math.random() * 25) + 97)); // a-z
            default: return String.valueOf(Math.round(Math.random() * 9)); // 0-9
        }
    }
    
    // 隨機字體: 字體/大小雙重隨機
    public static Font getRandomFont() {
        String[] fonts = {"Georgia", "Verdana", "Arial", "Tahoma", "Times New Roman", 
                          "Courier New", "Arial Black", "Quantzite"};
        int fontIndex = (int) Math.round(Math.random() * (fonts.length - 1));
        int fontSize = (int) Math.round(Math.random() * 4 + 16); // 16-20px
        return new Font(fonts[fontIndex], Font.PLAIN, fontSize);
    }
    
    // 核心生成方法: 回傳是否成功寫入輸出流
    public static boolean getImage(HttpServletRequest request, HttpServletResponse response) 
            throws Exception {
        response.reset();
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        Random random = new Random();
        
        // 1. 填充背景
        g.setColor(getRandColor(COLOR_FC_BG, COLOR_BC_BG));
        g.fillRect(0, 0, WIDTH, HEIGHT);
        
        // 2. 繪製干擾線 (155 條)
        g.setColor(getRandColor(COLOR_FC_LINE, COLOR_BC_LINE));
        for (int i = 0; i < 155; i++) {
            int x = random.nextInt(WIDTH);
            int y = random.nextInt(HEIGHT);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            g.drawLine(x, y, x + xl, y + yl);
        }
        
        // 3. 生成並繪製 4 位驗證碼
        StringBuilder codeValue = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            String rand = getRandomChar();
            codeValue.append(rand);
            g.setFont(getRandomFont()); // 每字隨機字體
            g.setColor(getRandColor(COLOR_FC_CODE, COLOR_BC_CODE));
            g.drawString(rand, 13 * i + 6, 16); // 位置微調
        }
        
        // 4. 存入 Session
        request.getSession().setAttribute("codeValue", codeValue.toString());
        
        // 5. 輸出圖片
        g.dispose();
        return ImageIO.write(image, "JPEG", response.getOutputStream());
    }
}

Servlet 封裝:ValidCodeServlet

@WebServlet("/validCodeServlet")
public class ValidCodeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        try {
            ValidCodeUtils.getImage(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml 傳統配置 (非 Servlet 3.0+ 環境)

<servlet>
    <servlet-name>validCodeServlet</servlet-name>
    <servlet-class>com.hoo.servlet.ValidCodeServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>validCodeServlet</servlet-name>
    <url-pattern>/validCodeServlet</url-pattern>
</servlet-mapping>

JSP 頁面整合

<img src="${pageContext.request.contextPath}/validCodeServlet" 
     title="看不清,點擊刷新" 
     onclick="reloadValidCode(this)" />

<script>
function reloadValidCode(img) {
    // 加時間戳防止 IE 快取
    img.src = "${pageContext.request.contextPath}/validCodeServlet?t=" + new Date().getTime();
}
</script>

驗證邏輯 (登入/表單處理)

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String userInput = req.getParameter("captcha");
        String sessionCode = (String) req.getSession().getAttribute("codeValue");
        
        if (sessionCode != null && sessionCode.equalsIgnoreCase(userInput)) {
            // 驗證通過
            req.getSession().removeAttribute("codeValue"); // 用完即銷毀
            // 後續業務邏輯...
        } else {
            // 驗證失敗
            req.setAttribute("error", "驗證碼錯誤");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    }
}

安全性與優化建議

項目建議
字元集排除易混淆字元 (0/O, 1/I/l, 2/Z, 5/S, 8/B) → 定義 String CHARS = "23456789ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz"
長度4-6 位平衡安全性與使用者體驗
干擾強度線條/噪點密度需測試 OCR 識別率,避免過度影響人類辨識
儲存安全Session 側邊,避免 URL 參數、Cookie、隱藏欄位傳遞
一次性驗證後立即從 Session 移除,防重放攻擊
無障礙提供語音驗證碼選項、或替代驗證方式 (簡訊、Email)
現代替代考慮 reCAPTCHA v3 (行為分析)、hCaptcha、Turnstile 等無感驗證

效能優化

  • 物件重用:Random、Font 陣列可設為 static final 避免重複建立
  • 圖片快取:高併發可預生成圖片池,或使用 Redis 儲存驗證碼 + 圖片 Base64
  • 非阻塞 I/O:Servlet 3.1+ 非阻塞寫入 response.getOutputStream()

此實作提供完整可部署的驗證碼解決方案,可直接整合至傳統 JSP/Servlet 或 Spring MVC 專案中。

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