jeecgboot/JeecgBoot · error · IllegalArgumentException

Sign签名校验失败,请求参数不完整!

Error message

Sign签名校验失败,请求参数不完整!

What it means

This error is thrown when the X-TIMESTAMP header is missing or empty from the incoming HTTP request. The SignAuthInterceptor requires both X-SIGN and X-TIMESTAMP headers to validate request authenticity; if the timestamp is absent, the request is considered incomplete and rejected before any cryptographic validation occurs.

Source

Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java:85

     * @param request HTTP请求
     * @throws IllegalArgumentException 验证失败时抛出异常
     */
    public void validateSignature(HttpServletRequest request, Object bodyParam) throws IllegalArgumentException {
        try {
            log.debug("开始签名验证: {} {}", request.getMethod(), request.getRequestURI());
            
            HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
            //获取全部参数(包括URL和body上的)
            SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper, bodyParam);
            log.debug("提取参数: {}", allParams);
            
            //对参数进行签名验证
            String headerSign = request.getHeader(CommonConstant.X_SIGN);
            String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
            
            if(oConvertUtils.isEmpty(xTimestamp)){
                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("签名验证失败,请求时效性验证失败!");
                }

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Ensure the client sends both X-SIGN and X-TIMESTAMP headers on requests to sign-protected endpoints.
  2. Verify the X-TIMESTAMP format: either yyyyMMddHHmmss (14 digits) or Unix epoch milliseconds (13 digits).
  3. Check that no proxy/load balancer strips X-* custom headers.
  4. If the endpoint should not require signing, remove it from the sign interceptor's path patterns or remove the @SignatureCheck annotation.

Example fix

// before (client omits X-TIMESTAMP)
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("X-SIGN", sign);

// after
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("X-SIGN", sign);
conn.setRequestProperty("X-TIMESTAMP", String.valueOf(System.currentTimeMillis()));
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: ensure headers are set before sending
String timestamp = String.valueOf(System.currentTimeMillis());
String sign = computeSign(params, secret);
if (timestamp == null || timestamp.trim().isEmpty()) {
    throw new IllegalStateException("X-TIMESTAMP must not be empty");
}
request.setHeader("X-SIGN", sign);
request.setHeader("X-TIMESTAMP", timestamp);

Type guard

// Server-side: validate header presence before parsing
String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
if (xTimestamp == null || xTimestamp.trim().isEmpty()) {
    response.setStatus(400);
    response.getWriter().write("{\"success\":false,\"message\":\"Missing X-TIMESTAMP header\"}");
    return false;
}

Try / catch

try {
    // ... request execution
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("请求参数不完整")) {
        // Retry with proper headers
        addSignHeaders(request);
        return retry(request);
    }
    throw e;
}

Prevention

When it happens

Trigger: A client sends a request to a sign-protected endpoint (intercepted by SignAuthInterceptor or annotated with @SignatureCheck) without the X-TIMESTAMP header, or with an empty value. The check at line 83 uses oConvertUtils.isEmpty(xTimestamp) which covers null, empty string, and whitespace.

Common situations: Client SDK was updated and stopped sending the X-TIMESTAMP header. The signature interceptor was newly registered for a path that was previously open. A proxy or gateway strips custom headers. Mobile clients with cached old versions that don't implement signing.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/4591bd70824f2d0f. Report an issue: GitHub.