在現代 Web 應用中,驗證碼是防止惡意註冊、暴力破解、垃圾信息發布的重要防線。其核心原理是:伺服器端生成包含隨機字符的圖片,將字符存入 Session,用戶提交時驗證輸入與 Session 中的值是否一致。為了對抗 OCR 識別,通常會添加干擾線、噪點、字符扭曲等手段增加機器識別難度。
通過 web.xml 配置驗證碼圖片的寬度、高度、字符數量等參數,並在 init() 方法中讀取並轉換為整數。
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;
// 驗證碼字符集(去除易混淆字符)
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' };
public void init() throws ServletException {
// 從 web.xml 讀取初始化參數
String strWidth = this.getInitParameter("width");
String strHeight = this.getInitParameter("height");
String strCodeCount = this.getInitParameter("codeCount");
try {
if (strWidth != null && strWidth.length() != 0) {
width = Integer.parseInt(strWidth);
}
if (strHeight != null && strHeight.length() != 0) {
height = Integer.parseInt(strHeight);
}
if (strCodeCount != null && strCodeCount.length() != 0) {
codeCount = Integer.parseInt(strCodeCount);
}
} catch (NumberFormatException e) {
// 使用默認值
}
x = width / (codeCount + 1);
fontHeight = height - 2;
codeY = height - 4;
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {
// 定義圖像緩衝區
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffImg.createGraphics();
// 創建隨機數生成器
Random random = new Random();
// 將圖像填充為白色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 創建字體
Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
g.setFont(font);
// 畫邊框
g.setColor(Color.BLACK);
g.drawRect(0, 0, width - 1, height - 1);
// 隨機生成干擾線
g.setColor(Color.GRAY);
for (int i = 0; i < 160; 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);
}
// 生成隨機驗證碼字符
StringBuilder randomCode = new StringBuilder();
int red, green, blue;
for (int i = 0; i < codeCount; i++) {
String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]);
randomCode.append(strRand);
// 隨機顏色
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
g.setColor(new Color(red, green, blue));
g.drawString(strRand, (i + 1) * x, codeY);
}
// 將驗證碼存入 Session
HttpSession session = req.getSession(true);
session.setAttribute("validateCode", randomCode.toString());
// 禁止緩存
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();
}
}
<servlet>
<servlet-name>ValidateCodeServlet</servlet-name>
<servlet-class>com.servlet.ValidateCodeServlet</servlet-class>
<init-param>
<param-name>width</param-name>
<param-value>120</param-value>
</init-param>
<init-param>
<param-name>height</param-name>
<param-value>40</param-value>
</init-param>
<init-param>
<param-name>codeCount</param-name>
<param-value>4</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ValidateCodeServlet</servlet-name>
<url-pattern>/validateCode</url-pattern>
</servlet-mapping>
<img src="validateCode" id="captchaImg" onclick="this.src='validateCode?'+Math.random()" />
<input type="text" name="validateCode" placeholder="請輸入驗證碼" />