jeecgboot/JeecgBoot · error · IllegalArgumentException
签名验证失败,请求时效性验证失败!
Error message
签名验证失败,请求时效性验证失败!
What it means
This error is thrown when the request timestamp (in yyyyMMddHHmmss format, e.g. 20220308152143) indicates the request is older than the allowed MAX_EXPIRE window of 5 minutes (300 seconds). The interceptor computes the difference between the server's current timestamp and the client timestamp; if timeDiff > MAX_EXPIRE (300 seconds), the request is rejected as expired.
Source
Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java:102
log.error("Sign签名校验失败,时间戳为空!");
throw new IllegalArgumentException("Sign签名校验失败,请求参数不完整!");
}
//客户端时间
Long clientTimestamp = Long.parseLong(xTimestamp);
int length = 14;
int length1000 = 1000;
//1.校验签名时间(兼容X_TIMESTAMP的新老格式)
if (xTimestamp.length() == length) {
//a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子:20220308152143)
long currentTimestamp = DateUtils.getCurrentTimestamp();
long timeDiff = currentTimestamp - clientTimestamp;
log.debug("时间戳验证(yyyyMMddHHmmss): 时间差{}秒", timeDiff);
if (timeDiff > MAX_EXPIRE) {
log.error("时间戳已过期: {}秒 > {}秒", timeDiff, MAX_EXPIRE);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
}
} else {
//b. X_TIMESTAMP格式是 时间戳 (例子:1646552406000)
long currentTime = System.currentTimeMillis();
long timeDiff = currentTime - clientTimestamp;
long maxExpireMs = MAX_EXPIRE * length1000;
log.debug("时间戳验证(Unix): 时间差{}ms", timeDiff);
if (timeDiff > maxExpireMs) {
log.error("时间戳已过期: {}ms > {}ms", timeDiff, maxExpireMs);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
}
}
//2.校验签名
boolean isSigned = SignUtil.verifySign(allParams,headerSign);
if (isSigned) {View on GitHub (pinned to 96fb33f5ec)
Solutions
- Synchronize client and server clocks using NTP/chrony to minimize clock drift.
- Generate the X-TIMESTAMP at the moment of sending the request, not when building the UI or pre-computing parameters.
- If legitimate use cases require longer validity, increase MAX_EXPIRE in SignAuthInterceptor (line 32), but be aware this widens the replay window.
- Debug by logging the server's current timestamp alongside the client timestamp to identify the drift.
Example fix
// before
String timestamp = "20240101120000"; // hardcoded or cached timestamp
// after
// Generate timestamp at send time using server-equivalent format
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
conn.setRequestProperty("X-TIMESTAMP", timestamp); Defensive patterns
Strategy: validation
Validate before calling
// Client-side: validate timestamp freshness before sending
long serverTimeEstimate = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
// Verify timestamp is within the last 4 minutes to allow for network latency
long tsMillis = sdf.parse(timestamp).getTime();
if (System.currentTimeMillis() - tsMillis > 240_000) {
// Regenerate — clock may have drifted
timestamp = sdf.format(new Date());
} Type guard
// Server-side helper: check timestamp validity
public static boolean isTimestampValid(String xTimestamp) {
if (xTimestamp == null || xTimestamp.length() != 14) return false;
try {
long clientTs = Long.parseLong(xTimestamp);
long currentTs = Long.parseLong(DateUtils.getCurrentTimestamp() + "");
return (currentTs - clientTs) <= 300; // MAX_EXPIRE seconds
} catch (NumberFormatException e) {
return false;
}
} Try / catch
// In the client SDK, implement automatic timestamp refresh
int maxRetries = 1;
for (int i = 0; i <= maxRetries; i++) {
request.setHeader("X-TIMESTAMP", generateTimestamp());
request.setHeader("X-SIGN", computeSign(params, secret));
try {
return execute(request);
} catch (SignatureExpiredException e) {
if (i == maxRetries) throw e;
}
} Prevention
- Generate the timestamp immediately before sending the HTTP request
- Synchronize client clock with NTP
- Never cache or reuse timestamps across requests
- Build timestamps at the transport layer (HTTP client interceptor), not the business logic layer
When it happens
Trigger: A client sends a request with X-TIMESTAMP in the 14-digit yyyyMMddHHmmss format where the value is more than 5 minutes old relative to the server's clock. This is detected at line 100: `if (timeDiff > MAX_EXPIRE)`.
Common situations: Client and server clocks are significantly out of sync. The client pre-computed and cached a signed request but delayed sending it past the 5-minute window. Network latency combined with a timestamp generated too early. Replay attempts using old captured requests.
Related errors
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/7739e3a86bb17df9.
Report an issue: GitHub.