public class TokenProcessor {
private long previous;
private static TokenProcessor instance = new TokenProcessor();
public static final String FORM_TOKEN_KEY = "FORM_TOKEN_KEY";
private TokenProcessor() {}
public static TokenProcessor getInstance() { return instance; }
// 驗證 Token (從請求參數取得)
public synchronized boolean isTokenValid(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) return false;
String saved = (String) session.getAttribute(FORM_TOKEN_KEY);
String token = request.getParameter(FORM_TOKEN_KEY);
return saved != null && token != null && saved.equals(token);
}
// 重載:直接傳入 token 字串驗證
public synchronized boolean isTokenValid(HttpServletRequest request, String token) {
HttpSession session = request.getSession(false);
if (session == null) return false;
String saved = (String) session.getAttribute(FORM_TOKEN_KEY);
return saved != null && token != null && saved.equals(token);
}
// 移除 Token
public synchronized void reset(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) session.removeAttribute(FORM_TOKEN_KEY);
}
// 生成並儲存 Token
public synchronized void saveToken(HttpServletRequest request) {
HttpSession session = request.getSession();
try {
byte[] id = session.getId().getBytes();
long current = System.currentTimeMillis();
if (current == previous) current++;
previous = current;
byte[] now = String.valueOf(current).getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(id);
md.update(now);
String token = toHex(md.digest());
session.setAttribute(FORM_TOKEN_KEY, token);
} catch (NoSuchAlgorithmException e) {
// log error
}
}
// 位元組陣列轉十六進制字串
private String toHex(byte[] buffer) {
StringBuffer sb = new StringBuffer(buffer.length * 2);
for (byte b : buffer) {
sb.append(Character.forDigit((b & 0xF0) >> 4, 16));
sb.append(Character.forDigit(b & 0x0F, 16));
}
return sb.toString();
}
}
TokenProcessor.getInstance().saveToken(request);<input type="hidden" name="FORM_TOKEN_KEY" value="${sessionScope.FORM_TOKEN_KEY}">if (!TokenProcessor.getInstance().isTokenValid(request)) { return "repeat_submit"; }TokenProcessor.getInstance().reset(request); // 驗證通過後清除
Token 由 Session ID + 當前時間毫秒數經 MD5 雜湊生成,極難偽造。Session 機制確保每個使用者獨立。驗證後即刻移除,防止重複提交 (Refresh/F5)。