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

Java 圖形驗證碼(CAPTCHA)完整實作:干擾線、旋轉、中文字符、Session 存儲

     編輯:Java 程式設計入門

功能特色

  • 支援中文字符(Unicode 範圍)
  • 字符隨機旋轉角度(±30°)
  • 干擾線、邊框、背景色
  • 輸出 JPEG,設定無快取 Header
  • 驗證碼存入 Session,前端驗證即比對

完整 Servlet 程式碼

package cn.response;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class ValidatePicture extends HttpServlet {

    private static final int WIDTH  = 120;
    private static final int HEIGHT = 25;
    private static final int CODE_COUNT = 4;          // 驗證碼長度
    private static final String CHARS =               // 可自行擴充字符池
            "\u4e00\u4e09\u4e50\u4e89\u4f10\u5e00" + // 一三樂爭仰干
            "ABCDEFGHJKLMNPQRSTUVWXYZ" +              // 排除易混淆 I/O
            "23456789";                               // 排除 0/1

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        generateCaptcha(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }

    private void generateCaptcha(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

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

        // 2. 邊框
        g.setColor(Color.BLUE);
        g.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        // 3. 干擾線(3 條)
        g.setColor(Color.GREEN);
        Random rand = new Random();
        for (int i = 0; i < 3; i++) {
            int x1 = rand.nextInt(WIDTH);
            int y1 = rand.nextInt(HEIGHT);
            int x2 = rand.nextInt(WIDTH);
            int y2 = rand.nextInt(HEIGHT);
            g.drawLine(x1, y1, x2, y2);
        }

        // 4. 繪製驗證碼字符
        g.setFont(new Font("微軟雅黑", Font.BOLD, 20));
        g.setColor(Color.RED);

        StringBuilder code = new StringBuilder();
        int x = 5;
        for (int i = 0; i < CODE_COUNT; i++) {
            char ch = CHARS.charAt(rand.nextInt(CHARS.length()));
            code.append(ch);

            int degree = rand.nextInt(30) - 15; // -15° ~ +15°
            g.rotate(Math.toRadians(degree), x, 20);
            g.drawString(String.valueOf(ch), x, 20);
            g.rotate(-Math.toRadians(degree), x, 20);

            x += 28; // 字符間距
        }

        // 5. 存入 Session
        req.getSession().setAttribute("captchaCode", code.toString());

        // 6. 輸出圖片
        resp.setContentType("image/jpeg");
        resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        resp.setHeader("Pragma", "no-cache");
        resp.setDateHeader("Expires", 0);
        ImageIO.write(image, "jpg", resp.getOutputStream());
        g.dispose();
    }
}

web.xml 註冊(或用 @WebServlet)

<servlet>
    <servlet-name>captcha</servlet-name>
    <servlet-class>cn.response.ValidatePicture</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>captcha</servlet-name>
    <url-pattern>/captcha.jpg</url-pattern>
</servlet-mapping>

前端使用範例

<img id="captchaImg" src="/captcha.jpg" onclick="this.src='/captcha.jpg?t=' + Date.now()" style="cursor:pointer" />
<input type="text" name="captchaInput" placeholder="輸入驗證碼" />

點擊圖片即可刷新(加時間戳防快取)。

後端驗證邏輯

@PostMapping("/login")
public Result login(@RequestParam String username,
                    @RequestParam String password,
                    @RequestParam String captchaInput,
                    HttpSession session) {
    String realCode = (String) session.getAttribute("captchaCode");
    if (realCode == null || !realCode.equalsIgnoreCase(captchaInput)) {
        return Result.error("驗證碼錯誤");
    }
    session.removeAttribute("captchaCode"); // 一次性驗證碼
    // 後續登入邏輯...
}

進階優化建議

  1. 字體載入:Linux 伺服器需安裝中文字體(如 wqy-zenhei),否則中文會顯示方塊。
  2. 干擾策略:可加入貝茲曲線、噪點、波浪扭曲,提高機器識別難度。
  3. 效能:高併發下建議使用 BufferedImage 對象池,或改用現成庫(kaptcha、hutool-captcha、simple-captcha)。
  4. 無障礙:提供語音驗證碼或點選圖片替代方案。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved