dromara/Sa-Token · error · ApiKeyException

12301

12301

Error message

无效 API Key: 

What it means

Thrown by SaApiKeyTemplate.checkApiKey(String) when getApiKey(apiKey) returns null — no ApiKey record exists in the SaTokenDao for that key string. It is the first of three checks in key validation (existence, expiry, enabled). Code 12301 means the key is unknown.

Source

Thrown at sa-token-plugin/sa-token-apikey/src/main/java/cn/dev33/satoken/apikey/template/SaApiKeyTemplate.java:127

		}
		// 先从缓存中获取,缓存中找不到就尝试从数据库获取
		ApiKeyModel apiKeyModel = getApiKeyModelFromCache(apiKey);
		if(apiKeyModel == null) {
			apiKeyModel = getApiKeyModelFromDatabase(apiKey);
			saveApiKey(apiKeyModel);
		}
		return apiKeyModel;
	}

	/**
	 * 校验 ApiKey,成功返回 ApiKeyModel,失败则抛出异常
	 * @param apiKey /
	 * @return /
	 */
	public ApiKeyModel checkApiKey(String apiKey) {
		ApiKeyModel ak = getApiKey(apiKey);
		if(ak == null) {
			throw new ApiKeyException("无效 API Key: " + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12301);
		}
		if(ak.timeExpired()) {
			throw new ApiKeyException("API Key 已过期: " + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12302);
		}
		if(! ak.getIsValid()) {
			throw new ApiKeyException("API Key 已被禁用: " + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12303);
		}
		return ak;
	}

	/**
	 * 持久化:ApiKeyModel
	 * @param ak /
	 */
	public void saveApiKey(ApiKeyModel ak) {
		if(ak == null) {
			return;
		}

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Verify the exact key string being sent matches the one returned when the key was created
  2. Check the dao backend: if using the default in-memory dao, keys are lost on restart — switch to Redis or another persistent SaTokenDao
  3. Re-issue the key with saApiKeyTemplate.createApiKey(loginId, ...) and distribute the new value

Example fix

// before
// key came from config file but was never registered on this server
saApiKeyTemplate.checkApiKey(cfg.get("api-key")); // throws 12301

// after
// create the key once, store/persist it, then hand it to the client
String apiKey = saApiKeyTemplate.createApiKey(10001, "order-service", 3600 * 24);
saApiKeyTemplate.checkApiKey(apiKey);
Defensive patterns

Strategy: try-catch

Validate before calling

ApiKeyModel ak = saApiKeyTemplate.getApiKey(apiKey);
if (ak == null) {
    return unauthorized("unknown api key");
}

Try / catch

try {
    saApiKeyTemplate.checkApiKey(apiKey);
} catch (ApiKeyException e) {
    if (e.getCode() == SaApiKeyErrorCode.CODE_12301) {
        // treat as 401, do not leak whether the key ever existed
    } else throw e;
}

Prevention

When it happens

Trigger: Calling checkApiKey(apiKey), checkApiKeyScope(...), or an annotation-protected route with a key that was never created, was deleted, or does not match what is stored (typo, wrong environment/dao).

Common situations: Client sends an old key after the server's store was cleared (e.g. in-memory dao restarted, Redis flushed); key typo; using a key issued for a different environment; key was revoked/deleted via deleteApiKey.

Related errors


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