まず、WebアプリケーションのWebContent/WEB-INF/ディレクトリ内にnew_words.txtというテキストファイルを作成し、以下のようなijkl化された四字熟語を複数行にわたって記述します:
一唱一和
一呼百應
一乾二乾
一舉兩得
一落千丈
一模>
一暴十寒
一日千里
一五一十
次に、パッケージcom.oracle.web内に以下の ServletException を継承するServletクラスImageCaptchaServletを定義します。このクラスは、起動時に成语リストを読み込み、GET/POSTリクエストに対してランダムな画像として验证码を動的生成します。
package com.oracle.web;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
public class ImageCaptchaServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private List<String> phraseList = new ArrayList<>();
@Override
public void init() throws ServletException {
String filePath = getServletContext().getRealPath("/WEB-INF/new_words.txt");
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
phraseList.add(line.trim());
}
} catch (IOException e) {
throw new ServletException("成语ファイルの読み込みに失敗しました: " + filePath, e);
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", 0);
resp.setContentType("image/jpeg");
int imgWidth = 120;
int imgHeight = 30;
BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// ベース背景描画
g2d.setColor(randomColor(220, 250));
g2d.fillRect(0, 0, imgWidth, imgHeight);
// 枠線描画
g2d.setColor(Color.WHITE);
g2d.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
g2d.drawRect(0, 0, imgWidth - 2, imgHeight - 2);
// 文字列の選択と描画
g2d.setFont(new Font("MS PGothic", Font.BOLD, 20));
String phrase = phraseList.get((int)(Math.random() * phraseList.size()));
req.getSession().setAttribute("captcha_solution", phrase);
int offsetX = 12;
Random rand = new Random();
for (int i = 0; i < phrase.length(); i++) {
char ch = phrase.charAt(i);
g2d.setColor(randomColor(20, 100)); // 文字色は暗めで
// 角度のランダム変換(±30度)
double angle = (rand.nextInt(61) - 30) * Math.PI / 180.0;
g2d.rotate(angle, offsetX + 15, 18);
g2d.drawString(String.valueOf(ch), offsetX, 22);
g2d.rotate(-angle, offsetX + 15, 18);
offsetX += 28;
}
// ライン干渉線の追加
g2d.setColor(randomColor(140, 200));
for (int i = 0; i < 25; i++) {
int x1 = rand.nextInt(imgWidth);
int y1 = rand.nextInt(imgHeight);
int x2 = x1 + rand.nextInt(25);
int y2 = y1 + rand.nextInt(15);
g2d.drawLine(x1, y1, x2, y2);
}
g2d.dispose();
ImageIO.write(image, "JPEG", resp.getOutputStream());
}
private Color randomColor(int min, int max) {
if (min < 0) min = 0; if (min > 255) min = 255;
if (max < 0) max = 0; if (max > 255) max = 255;
if (min > max) { int tmp = min; min = max; max = tmp; }
int r = min + (int)(Math.random() * (max - min + 1));
int g = min + (int)(Math.random() * (max - min + 1));
int b = min + (int)(Math.random() * (max - min + 1));
return new Color(r, g, b);
}
}
最後に、Webルート以下の任意のJSPページ(例:login.jsp)に以下のコードを組み込み、画像リクエストと再読み込み機能を実装します。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>画像验证码テスト</title>
<script>
function reloadCaptcha(imgElement) {
imgElement.src = "${pageContext.request.contextPath}/ImageCaptchaServlet?ts=" + new Date().getTime();
}
</script>
</head>
<body>
<p>以下の画像に表示される漢字四字を覚えてください:</p>
<img id="captchaImg" src="${pageContext.request.contextPath}/ImageCaptchaServlet"
alt="画像認証コード" title="クリックで再読み込み"
onclick="reloadCaptcha(this)" style="cursor:pointer; border-radius:4px;">
<br><br>
<form method="post" action="verifyCaptcha">
<input type="text" name="captchaInput" placeholder="ここに文字列を入力" required>
<button type="submit">送信</button>
</form>
</body>
</html>