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

JSP 驗證碼生成工具類完整實現

     編輯:關於JSP

需求分析

生成包含隨機字符的圖片驗證碼,支持配置寬高、字符集、干擾線、字體、顏色,輸出到 OutputStream(通常為 response.getOutputStream()),並返回驗證碼字符串供 Session 存儲校驗。

完整工具類代碼

package com.example.util;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;

public class CaptchaUtil {

    /** 字符集:數字 + 大小寫字母,排除易混淆字符 */
    private static final char[] CHAR_SET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz".toCharArray();
    private static final Random RANDOM = new Random();

    /** 默認配置 */
    private static final int DEFAULT_WIDTH = 100;
    private static final int DEFAULT_HEIGHT = 36;
    private static final int DEFAULT_LENGTH = 4;
    private static final int DEFAULT_FONT_SIZE = 24;
    private static final Color DEFAULT_BG_COLOR = new Color(0xF0, 0xF0, 0xF0);
    private static final Color DEFAULT_BORDER_COLOR = Color.BLACK;

    /**
     * 生成驗證碼圖片並寫入輸出流
     * @param os 輸出流(如 response.getOutputStream())
     * @param width 圖片寬度
     * @param height 圖片高度
     * @param length 驗證碼字符長度
     * @return 生成的驗證碼字符串
     */
    public static String generate(OutputStream os, int width, int height, int length) {
        if (width <= 0) width = DEFAULT_WIDTH;
        if (height <= 0) height = DEFAULT_HEIGHT;
        if (length <= 0) length = DEFAULT_LENGTH;

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

        // 抗鋸齒
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // 背景
        g.setColor(DEFAULT_BG_COLOR);
        g.fillRect(0, 0, width, height);

        // 邊框
        g.setColor(DEFAULT_BORDER_COLOR);
        g.drawRect(0, 0, width - 1, height - 1);

        // 干擾線
        drawInterferenceLines(g, width, height, 3);

        // 生成隨機字符
        StringBuilder code = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            code.append(CHAR_SET[RANDOM.nextInt(CHAR_SET.length)]);
        }

        // 繪製字符(隨機顏色、字體、旋轉、位置)
        drawChars(g, code.toString(), width, height);

        g.dispose();

        try {
            ImageIO.write(image, "JPEG", os);
        } catch (IOException e) {
            throw new RuntimeException("驗證碼圖片輸出失敗", e);
        }
        return code.toString();
    }

    /** 重載方法:使用默認尺寸 */
    public static String generate(OutputStream os) {
        return generate(os, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_LENGTH);
    }

    private static void drawInterferenceLines(Graphics2D g, int w, int h, int count) {
        g.setStroke(new BasicStroke(1.5f));
        for (int i = 0; i < count; i++) {
            g.setColor(randomColor(160, 200));
            int x1 = RANDOM.nextInt(w);
            int y1 = RANDOM.nextInt(h);
            int x2 = RANDOM.nextInt(w);
            int y2 = RANDOM.nextInt(h);
            g.drawLine(x1, y1, x2, y2);
        }
    }

    private static void drawChars(Graphics2D g, String code, int w, int h) {
        int charWidth = w / code.length();
        for (int i = 0; i < code.length(); i++) {
            char c = code.charAt(i);
            // 隨機字體
            Font font = new Font(getRandomFontName(), Font.BOLD | Font.ITALIC, DEFAULT_FONT_SIZE);
            g.setFont(font);
            // 隨機顏色
            g.setColor(randomColor(20, 130));
            // 隨機旋轉角度 (-30° ~ +30°)
            double theta = (RANDOM.nextDouble() - 0.5) * Math.PI / 3;
            // 字符中心坐標
            int x = i * charWidth + charWidth / 2;
            int y = h / 2 + DEFAULT_FONT_SIZE / 3;
            // 仿射變換:平移→旋轉→平移回
            g.translate(x, y);
            g.rotate(theta);
            g.drawString(String.valueOf(c), -g.getFontMetrics().stringWidth(String.valueOf(c)) / 2, 0);
            g.rotate(-theta);
            g.translate(-x, -y);
        }
    }

    private static String getRandomFontName() {
        String[] fonts = {"Arial", "Verdana", "Tahoma", "Courier New", "Georgia", "Comic Sans MS"};
        return fonts[RANDOM.nextInt(fonts.length)];
    }

    private static Color randomColor(int min, int max) {
        int r = min + RANDOM.nextInt(max - min);
        int g = min + RANDOM.nextInt(max - min);
        int b = min + RANDOM.nextInt(max - min);
        return new Color(r, g, b);
    }
}

JSP 調用示例

<%@ page contentType="image/jpeg" pageEncoding="UTF-8" import="com.example.util.CaptchaUtil" %>
<%
    response.setHeader("Pragma", "No-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);
    String code = CaptchaUtil.generate(response.getOutputStream());
    session.setAttribute("captcha_code", code.toLowerCase()); // 忽略大小寫存儲
%>

前端校驗邏輯

// 提交時
String input = request.getParameter("captcha");
String real = (String) session.getAttribute("captcha_code");
if (input != null && real != null && input.toLowerCase().equals(real)) {
    // 通過
}

進階擴展建議

  • 中文驗證碼:字符集改為常用汉字,字體需支持中文(如「微軟雅黑」)。
  • 算術驗證碼:生成「12+3=?」類型,驗證邏輯改為計算結果比對。
  • 點選/滑塊驗證:前端 Canvas 交互,後端驗證軌跡/坐標,安全性更高。
  • 分布式部署:驗證碼存入 Redis,Key 為 UUID,前端攜帶 Key 請求圖片與校驗。
  • 性能優化:使用 BufferedImage.TYPE_INT_ARGB、預生成字體緩存、關閉抗鋸齒(低端機)。

總結

封裝可配置、可擴展的 CaptchaUtil 類,配合無狀態設計(不依賴 Servlet API),便於單元測試與多項目復用。現代項目建議接入成熟驗證碼服務(如 Google reCAPTCHA、hCaptcha、阿里云驗證碼)以應對機器學習破解。

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