{"record":{"id":"473dfae685c142e9","repo":"jeecgboot/JeecgBoot","slug":"signature-473dfa","errorCode":null,"errorMessage":"signature签名错误","messagePattern":"signature签名错误","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/filter/ApiAuthFilter.java","lineNumber":231,"sourceCode":"     * 认证信息核验\n     * @param appKey\n     * @param signature\n     * @param timestamp\n     * @param openApiAuth\n     * @return\n     * @throws Exception\n     */\n    protected void checkSignature(String appKey, String signature, String timestamp, OpenApiAuth openApiAuth) {\n        if(openApiAuth==null){\n            throw new JeecgBootException(\"不存在认证信息\");\n        }\n\n        if(!appKey.equals(openApiAuth.getAk())){\n            throw new JeecgBootException(\"appkey错误\");\n        }\n\n        if (!signature.equals(md5(appKey + openApiAuth.getSk() + timestamp))) {\n            throw new JeecgBootException(\"signature签名错误\");\n        }\n    }\n\n    protected void checkPermission(OpenApi openApi, OpenApiAuth openApiAuth) {\n        List<OpenApiPermission> permissionList = openApiPermissionService.findByAuthId(openApiAuth.getId());\n\n        boolean hasPermission = false;\n        for (OpenApiPermission permission : permissionList) {\n            if (permission.getApiId().equals(openApi.getId())) {\n                hasPermission = true;\n                break;\n            }\n        }\n\n        if (!hasPermission) {\n            throw new JeecgBootException(\"该appKey未授权当前接口\");\n        }\n    }","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/filter/ApiAuthFilter.java#L213-L249","documentation":"Thrown by checkSignature when the supplied signature does not equal md5(appKey + openApiAuth.getSk() + timestamp). The MD5 is computed over the concatenation of appkey, the stored secret key (sk), and the timestamp - in that exact order, no delimiters. Any mismatch in value, order, encoding, or the sk itself produces this error.","triggerScenarios":"Client uses the wrong sk, wrong concatenation order, wrong encoding (non-UTF-8), a different timestamp than the one sent, or computes a different hash algorithm (e.g. SHA-256); the stored sk was rotated and the client has the old value.","commonSituations":"Secret rotation not propagated to the client; client builds signature with a different field order; the md5 helper in the codebase (ApiAuthFilter.md5) lowercases hex while the client uppercases; locale-dependent byte encoding.","solutions":["Recompute signature exactly as md5(appkey + sk + timestamp) using UTF-8 bytes and lowercase 32-char hex output, matching ApiAuthFilter.md5.","Verify the sk in open_api_auth matches the client's secret; re-issue if rotated.","Ensure the timestamp string used in signing is byte-identical to the one sent in the request.","Confirm there are no hidden delimiters or trailing characters in any of the three inputs."],"exampleFix":"// before: signature = md5(sk + appkey + ts)  // wrong order\n// after:  signature = md5(appkey + sk + ts)   // matches server:\n//         MessageDigest.getInstance(\"MD5\"), UTF-8, lowercase 32-hex","handlingStrategy":"validation","validationCode":"// Reproduce the server's exact signature locally and compare before sending\nString expected = md5LowerHex(appkey + sk + timestamp); // UTF-8, 32-char lowercase hex\nif (!expected.equals(signature)) {\n    throw new IllegalStateException(\"signature mismatch before send\");\n}\n// md5LowerHex must match ApiAuthFilter.md5 byte-for-byte","typeGuard":null,"tryCatchPattern":"try {\n    openApi.call(...);\n} catch (JeecgBootException e) {\n    if (e.getMessage().contains(\"signature签名错误\")) {\n        // do NOT retry with same signature; recompute with current sk + ts and resend once\n    }\n}","preventionTips":["Use exact order appkey+sk+timestamp with no delimiters and UTF-8 bytes.","Match the lowercase 32-char hex output of ApiAuthFilter.md5.","Rotate sk on both sides together; never cache signed payloads across rotations."],"tags":["openapi","auth","signature","md5","credentials"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}