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

Java 實作隨機驗證碼功能完整教學:從 Servlet 到部署設定

     編輯:Java 程式設計入門

前言:為什麼需要驗證碼?

在現代 Web 應用中,註冊、登入、發布留言等功能若無防護,極易遭遇自動化腳本惡意灌水或暴力破解。隨機驗證碼(CAPTCHA)透過圖形化呈現亂數字元,利用機器難以辨識圖片中扭曲字符與干擾線的特性,有效阻擋非人為操作。

核心原理

  • 伺服器端隨機生成字元序列,繪製成圖片並輸出至瀏覽器。
  • 同時將正確答案存入 Session。
  • 使用者提交表單時攜帶輸入的驗證碼,伺服器比對 Session 中的值,一致才通過驗證。
  • 為提高破解難度,通常加入干擾線、噪點、字符旋轉與顏色隨機化。

完整實作程式碼解析

以下採用 HttpServlet 搭配 Java 2D API (BufferedImage, Graphics2D) 實作:

package com.servlet;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class ValidateCodeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // 圖片預設尺寸與字元數
    private int width = 60;
    private int height = 20;
    private int codeCount = 4;
    private int x = 0;
    private int fontHeight;
    private int codeY;

    // 驗證碼字元池(大寫字母 + 數字)
    private char[] codeSequence = {
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        '0','1','2','3','4','5','6','7','8','9'
    };

    @Override
    public void init() throws ServletException {
        // 從 web.xml 讀取初始化參數,支援外部配置
        String strWidth = getInitParameter("width");
        String strHeight = getInitParameter("height");
        String strCodeCount = getInitParameter("codeCount");

        try {
            if (strWidth != null && !strWidth.isEmpty()) width = Integer.parseInt(strWidth);
            if (strHeight != null && !strHeight.isEmpty()) height = Integer.parseInt(strHeight);
            if (strCodeCount != null && !strCodeCount.isEmpty()) codeCount = Integer.parseInt(strCodeCount);
        } catch (NumberFormatException ignored) {}

        x = width / (codeCount + 1);
        fontHeight = height - 2;
        codeY = height - 4;
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, java.io.IOException {

        // 1. 建立圖片緩衝區
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffImg.createGraphics();
        Random random = new Random();

        // 2. 填充白色背景
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        // 3. 設定字體與邊框
        Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
        g.setFont(font);
        g.setColor(Color.BLACK);
        g.drawRect(0, 0, width - 1, height - 1);

        // 4. 繪製 160 條隨機干擾線
        g.setColor(Color.BLACK);
        for (int i = 0; i < 160; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            g.drawLine(x1, y1, x1 + xl, y1 + yl);
        }

        // 5. 生成隨機驗證碼並繪製
        StringBuffer randomCode = new StringBuffer();
        for (int i = 0; i < codeCount; i++) {
            String strRand = String.valueOf(codeSequence[random.nextInt(36)]);
            // 隨機顏色增加辨識難度
            int red = random.nextInt(255);
            int green = random.nextInt(255);
            int blue = random.nextInt(255);
            g.setColor(new Color(red, green, blue));
            g.drawString(strRand, (i + 1) * x, codeY);
            randomCode.append(strRand);
        }

        // 6. 存入 Session 供後續驗證
        HttpSession session = req.getSession();
        session.setAttribute("validateCode", randomCode.toString());

        // 7. 禁止快取,輸出 JPEG 圖片
        resp.setHeader("Pragma", "no-cache");
        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", 0);
        resp.setContentType("image/jpeg");

        ServletOutputStream sos = resp.getOutputStream();
        ImageIO.write(buffImg, "jpeg", sos);
        sos.close();
    }
}

web.xml 部署描述檔設定

將 Servlet 註冊並映射 URL,同時透過 <init-param> 客製化圖片尺寸與字元數:

<servlet>
    <servlet-name>ValidateCodeServlet</servlet-name>
    <servlet-class>com.servlet.ValidateCodeServlet</servlet-class>
    <init-param>
        <param-name>width</param-name>
        <param-value>200</param-value>
    </init-param>
    <init-param>
        <param-name>height</param-name>
        <param-value>80</param-value>
    </init-param>
    <init-param>
        <param-name>codeCount</param-name>
        <param-value>5</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>ValidateCodeServlet</servlet-name>
    <url-pattern>/validateCodeServlet</url-pattern>
</servlet-mapping>

前端整合範例

在 JSP 或 HTML 中嵌入圖片並提供「換一張」功能:

<img id="captchaImg" src="/validateCodeServlet" alt="驗證碼" />
<a href="javascript:;" onclick="document.getElementById('captchaImg').src='/validateCodeServlet?t='+Date.now()">看不清,換一張</a>

進階優化建議

  • 字元池擴充:加入小寫字母、特殊符號,或排除易混淆字元 (0/O, 1/I/l)。
  • 干擾策略升級:使用貝茲曲線、噪點、字符旋轉、平移、縮放組合。
  • 安全性加強:驗證碼一次性使用(驗證後立即從 Session 移除)、限制嘗試次數、加入時間戳防重放。
  • 效能考量:高併發場景可改用 Redis 儲存驗證碼,減輕 Session 壓力;或引入 Google reCAPTCHA / hCaptcha 等成熟方案。

總結

本文示範了基於 Servlet 的經典驗證碼實作,涵蓋初始化配置、圖形繪製、Session 管理與部署設定。雖然現代專案多採用前後端分離架構,但理解此基礎機制有助於掌握 Web 安全防護的核心邏輯。實務上建議結合成熟的第三方驗證碼服務,兼顧安全性與使用者體驗。

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