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

  1. Synchronize client and server clocks using NTP/chrony to minimize clock drift.
  2. Generate the X-TIMESTAMP at the moment of sending the request, not when building the UI or pre-computing parameters.
  3. If legitimate use cases require longer validity, increase MAX_EXPIRE in SignAuthInterceptor (line 32), but be aware this widens the replay window.
  4. 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

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.