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
- Ensure the client sends both X-SIGN and X-TIMESTAMP headers on requests to sign-protected endpoints.
- Verify the X-TIMESTAMP format: either yyyyMMddHHmmss (14 digits) or Unix epoch milliseconds (13 digits).
- Check that no proxy/load balancer strips X-* custom headers.
- 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
- Always send X-SIGN and X-TIMESTAMP headers together on sign-protected endpoints
- Build a centralized HTTP client interceptor that automatically adds signature headers
- Test against the sign interceptor with a unit test that verifies header presence
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.