{"record":{"id":"4591bd70824f2d0f","repo":"jeecgboot/JeecgBoot","slug":"sign-4591bd","errorCode":null,"errorMessage":"Sign签名校验失败，请求参数不完整！","messagePattern":"Sign签名校验失败，请求参数不完整！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java","lineNumber":85,"sourceCode":"     * @param request HTTP请求\n     * @throws IllegalArgumentException 验证失败时抛出异常\n     */\n    public void validateSignature(HttpServletRequest request, Object bodyParam) throws IllegalArgumentException {\n        try {\n            log.debug(\"开始签名验证: {} {}\", request.getMethod(), request.getRequestURI());\n            \n            HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);\n            //获取全部参数(包括URL和body上的)\n            SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper, bodyParam);\n            log.debug(\"提取参数: {}\", allParams);\n            \n            //对参数进行签名验证\n            String headerSign = request.getHeader(CommonConstant.X_SIGN);\n            String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);\n            \n            if(oConvertUtils.isEmpty(xTimestamp)){\n                log.error(\"Sign签名校验失败，时间戳为空！\");\n                throw new IllegalArgumentException(\"Sign签名校验失败，请求参数不完整！\");\n            }\n\n            //客户端时间\n            Long clientTimestamp = Long.parseLong(xTimestamp);\n\n            int length = 14;\n            int length1000 = 1000;\n            //1.校验签名时间（兼容X_TIMESTAMP的新老格式）\n            if (xTimestamp.length() == length) {\n                //a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子：20220308152143)\n                long currentTimestamp = DateUtils.getCurrentTimestamp();\n                long timeDiff = currentTimestamp - clientTimestamp;\n                log.debug(\"时间戳验证(yyyyMMddHHmmss): 时间差{}秒\", timeDiff);\n                \n                if (timeDiff > MAX_EXPIRE) {\n                    log.error(\"时间戳已过期: {}秒 > {}秒\", timeDiff, MAX_EXPIRE);\n                    throw new IllegalArgumentException(\"签名验证失败，请求时效性验证失败！\");\n                }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (client omits X-TIMESTAMP)\nHttpURLConnection conn = (HttpURLConnection) url.openConnection();\nconn.setRequestProperty(\"X-SIGN\", sign);\n\n// after\nHttpURLConnection conn = (HttpURLConnection) url.openConnection();\nconn.setRequestProperty(\"X-SIGN\", sign);\nconn.setRequestProperty(\"X-TIMESTAMP\", String.valueOf(System.currentTimeMillis()));","handlingStrategy":"validation","validationCode":"// Client-side: ensure headers are set before sending\nString timestamp = String.valueOf(System.currentTimeMillis());\nString sign = computeSign(params, secret);\nif (timestamp == null || timestamp.trim().isEmpty()) {\n    throw new IllegalStateException(\"X-TIMESTAMP must not be empty\");\n}\nrequest.setHeader(\"X-SIGN\", sign);\nrequest.setHeader(\"X-TIMESTAMP\", timestamp);","typeGuard":"// Server-side: validate header presence before parsing\nString xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);\nif (xTimestamp == null || xTimestamp.trim().isEmpty()) {\n    response.setStatus(400);\n    response.getWriter().write(\"{\\\"success\\\":false,\\\"message\\\":\\\"Missing X-TIMESTAMP header\\\"}\");\n    return false;\n}","tryCatchPattern":"try {\n    // ... request execution\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"请求参数不完整\")) {\n        // Retry with proper headers\n        addSignHeaders(request);\n        return retry(request);\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["signature","security","authentication","http-headers"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}