dromara/Sa-Token · error · SaSignException

CODE_12203

CODE_12203

Error message

timestamp 超出允许的范围:{timestamp}

What it means

Thrown by SaSignTemplate.checkTimestamp when |now - timestamp| exceeds the configured timestampDisparity and disparity is not -1 (unlimited) — code 12203. Server-side API signing includes a freshness window to block replay of intercepted signed requests; a timestamp outside the window is rejected before nonce/sign are even evaluated.

Source

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

	/**
	 * 判断:指定时间戳与当前时间戳的差距是否在允许的范围内
	 * @param timestamp 待校验的时间戳
	 * @return 是否在允许的范围内
	 */
	public boolean isValidTimestamp(long timestamp) {
		long allowDisparity = getSignConfigOrGlobal().getTimestampDisparity();
		long disparity = Math.abs(System.currentTimeMillis() - timestamp);
		return allowDisparity == -1 || disparity <= allowDisparity;
	}

	/**
	 * 校验:指定时间戳与当前时间戳的差距是否在允许的范围内,如果超出则抛出异常
	 * @param timestamp 待校验的时间戳
	 */
	public void checkTimestamp(long timestamp) {
		if( ! isValidTimestamp(timestamp) ) {
			throw new SaSignException("timestamp 超出允许的范围:" + timestamp).setCode(SaSignErrorCode.CODE_12203);
		}
	}

	/**
	 * 判断:随机字符串 nonce 是否有效。
	 * 		注意:同一 nonce 可以被多次判断有效,不会被缓存
	 * @param nonce 待判断的随机字符串
	 * @return 是否有效
	 */
	public boolean isValidNonce(String nonce) {
		// 为空代表无效
		if(SaFoxUtil.isEmpty(nonce)) {
			return false;
		}

		// 校验此 nonce 是否已被使用过
		String key = splicingNonceSaveKey(nonce);
		return SaManager.getSaTokenDao().get(key) == null;

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Sync server clocks (NTP/chrony) on both signing and verifying machines
  2. Generate the timestamp at request time in milliseconds (System.currentTimeMillis()), never cache or reuse it
  3. Widen sa-token sign timestamp-disparity config if legitimate latency exceeds the window, or set -1 only if you accept replay risk mitigated by nonce

Example fix

// before
params.put("timestamp", "1609459200000"); // fixed/old timestamp
// after
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
Defensive patterns

Strategy: validation

Validate before calling

long disparity = Math.abs(System.currentTimeMillis() - timestamp);
if (disparity > signConfig.getTimestampDisparity()) throw new IllegalArgumentException("timestamp stale by " + disparity + " ms");

Try / catch

try { saSignTemplate.checkTimestamp(ts); } catch (SaSignException e) { if (e.getCode() == 12203) return status(401, "timestamp out of range — check clock sync"); throw e; }

Prevention

When it happens

Trigger: Sending a signed request whose timestamp parameter deviates from server time by more than timestampDisparity milliseconds (default window, commonly 15 min), including stale timestamps from cached/retried requests.

Common situations: Client machine clock drift or wrong timezone-epoch handling (seconds vs milliseconds); NTP not running on app servers; requests queued/retried long after signing; disparity configured very tightly.

Related errors


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