dromara/Sa-Token · critical · SaSignException

CODE_12202

CODE_12202

Error message

无效签名:{sign}

What it means

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 '无效签名'.

Source

Thrown at sa-token-plugin/sa-token-sign/src/main/java/cn/dev33/satoken/sign/template/SaSignTemplate.java:301

	/**
	 * 判断:给定的参数 生成的签名是否为有效签名
	 * @param paramsMap 参数列表
	 * @param sign 待验证的签名
	 * @return 签名是否有效
	 */
	public boolean isValidSign(Map<String, ?> paramsMap, String sign) {
		String theSign = createSign(paramsMap);
		return theSign.equals(sign);
	}

	/**
	 * 校验:给定的参数 生成的签名是否为有效签名,如果签名无效则抛出异常
	 * @param paramsMap 参数列表
	 * @param sign 待验证的签名
	 */
	public void checkSign(Map<String, ?> paramsMap, String sign) {
		if( ! isValidSign(paramsMap, sign) )  {
			throw new SaSignException("无效签名:" + sign).setCode(SaSignErrorCode.CODE_12202);
		}
	}

	/**
	 * 判断:参数列表中的 nonce、timestamp、sign 是否均为合法的
	 * @param paramMap 待校验的请求参数集合
	 * @return 是否合法
	 */
	@SuppressWarnings("all")
	public boolean isValidParamMap(Map<String, String> paramMap) {
		// 获取必须的三个参数
		String timestampValue = paramMap.get(timestamp);
		String nonceValue = paramMap.get(nonce);
		String signValue = paramMap.get(sign);

		// 参数非空校验
		// 配置isCheckNonce=false时,可以不传 nonce
		if(SaFoxUtil.isEmpty(timestampValue) || SaFoxUtil.isEmpty(signValue)) {

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Verify the secret key and (for multi-app) the appid→config mapping are identical on both sides
  2. Reproduce the server's splicing exactly: sort params by key, include timestamp/nonce/appid per config, use the configured digest algo
  3. Confirm digest-algo spelling is one of md5/sha1/sha256/sha384/sha512 and identical on both sides
  4. 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

Example fix

// before (client forgets timestamp & nonce in the signed string)
String sign = DigestUtils.md5Hex(bizParamsOnly);
// after
TreeMap<String,String> p = new TreeMap<>(bizParams);
p.put("timestamp", ts); p.put("nonce", nonce);
String sign = SaSignTemplateUtil.createSign(p, secretKey);
Defensive patterns

Strategy: try-catch

Validate before calling

String expected = saSignTemplate.createSign(paramsMap);
if (!expected.equals(sign)) throw new SecurityException("signature mismatch — check key, algo, and param splicing");

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/d5d7335580ba26e1. Report an issue: GitHub.