{"record":{"id":"d5d7335580ba26e1","repo":"dromara/Sa-Token","slug":"code-12202","errorCode":"CODE_12202","errorMessage":"无效签名：{sign}","messagePattern":"无效签名：\\{sign\\}","errorType":"exception","errorClass":"SaSignException","httpStatus":null,"severity":"critical","filePath":"sa-token-plugin/sa-token-sign/src/main/java/cn/dev33/satoken/sign/template/SaSignTemplate.java","lineNumber":301,"sourceCode":"\t/**\n\t * 判断：给定的参数 生成的签名是否为有效签名\n\t * @param paramsMap 参数列表\n\t * @param sign 待验证的签名\n\t * @return 签名是否有效\n\t */\n\tpublic boolean isValidSign(Map<String, ?> paramsMap, String sign) {\n\t\tString theSign = createSign(paramsMap);\n\t\treturn theSign.equals(sign);\n\t}\n\n\t/**\n\t * 校验：给定的参数 生成的签名是否为有效签名，如果签名无效则抛出异常\n\t * @param paramsMap 参数列表\n\t * @param sign 待验证的签名\n\t */\n\tpublic void checkSign(Map<String, ?> paramsMap, String sign) {\n\t\tif( ! isValidSign(paramsMap, sign) )  {\n\t\t\tthrow new SaSignException(\"无效签名：\" + sign).setCode(SaSignErrorCode.CODE_12202);\n\t\t}\n\t}\n\n\t/**\n\t * 判断：参数列表中的 nonce、timestamp、sign 是否均为合法的\n\t * @param paramMap 待校验的请求参数集合\n\t * @return 是否合法\n\t */\n\t@SuppressWarnings(\"all\")\n\tpublic boolean isValidParamMap(Map<String, String> paramMap) {\n\t\t// 获取必须的三个参数\n\t\tString timestampValue = paramMap.get(timestamp);\n\t\tString nonceValue = paramMap.get(nonce);\n\t\tString signValue = paramMap.get(sign);\n\n\t\t// 参数非空校验\n\t\t// 配置isCheckNonce=false时，可以不传 nonce\n\t\tif(SaFoxUtil.isEmpty(timestampValue) || SaFoxUtil.isEmpty(signValue)) {","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-sign/src/main/java/cn/dev33/satoken/sign/template/SaSignTemplate.java#L283-L319","documentation":"Thrown by SaSignTemplate.checkSign when createSign(paramsMap) does not equal the provided sign value (code 12202). The server re-computes the digest over the sorted, spliced parameters plus the app's secret key; any mismatch in parameters, key, or algorithm yields '无效签名'.","triggerScenarios":"Signature computed with a different secret key, different parameter set (extra/missing params, different sort or exclusion rules like timestamp/nonce/appid handling), different digest algorithm, or wrong string splicing on the client versus the server.","commonSituations":"Secret key mismatch between caller and server (or per-appid config wrong in SaSignMany); client signs business params but omits/incorrectly orders timestamp & nonce; hyphenated algo name ('sha-256') failing algo dispatch; params transformed in transit (charset, URL decoding, trailing whitespace); GET vs POST parameter merge differences.","solutions":["Verify the secret key and (for multi-app) the appid→config mapping are identical on both sides","Reproduce the server's splicing exactly: sort params by key, include timestamp/nonce/appid per config, use the configured digest algo","Confirm digest-algo spelling is one of md5/sha1/sha256/sha384/sha512 and identical on both sides","Log the fullStr the server builds (or mirror createSign client-side) and diff it against the client's signed string to find the diverging parameter"],"exampleFix":"// before (client forgets timestamp & nonce in the signed string)\nString sign = DigestUtils.md5Hex(bizParamsOnly);\n// after\nTreeMap<String,String> p = new TreeMap<>(bizParams);\np.put(\"timestamp\", ts); p.put(\"nonce\", nonce);\nString sign = SaSignTemplateUtil.createSign(p, secretKey);","handlingStrategy":"try-catch","validationCode":"String expected = saSignTemplate.createSign(paramsMap);\nif (!expected.equals(sign)) throw new SecurityException(\"signature mismatch — check key, algo, and param splicing\");","typeGuard":null,"tryCatchPattern":"try { saSignTemplate.checkSign(paramsMap, sign); } catch (SaSignException e) { if (e.getCode() == 12202) { log.warn(\"sign mismatch, params={}\", paramsMap); return status(401, \"invalid signature\"); } throw e; }","preventionTips":["Mirror the server's createSign logic in the client and unit-test both against the same fixture","Keep secret keys in synced secret storage; rotate on both sides simultaneously","Log the spliced fullStr on failure once to diff client vs server parameter sets"],"tags":["api-sign","signature","secret-key","security"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}