{"record":{"id":"a3043907c9e423b3","repo":"dromara/Sa-Token","slug":"12301","errorCode":"12301","errorMessage":"无效 API Key: ","messagePattern":"无效 API Key: ","errorType":"exception","errorClass":"ApiKeyException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-apikey/src/main/java/cn/dev33/satoken/apikey/template/SaApiKeyTemplate.java","lineNumber":127,"sourceCode":"\t\t}\n\t\t// 先从缓存中获取，缓存中找不到就尝试从数据库获取\n\t\tApiKeyModel apiKeyModel = getApiKeyModelFromCache(apiKey);\n\t\tif(apiKeyModel == null) {\n\t\t\tapiKeyModel = getApiKeyModelFromDatabase(apiKey);\n\t\t\tsaveApiKey(apiKeyModel);\n\t\t}\n\t\treturn apiKeyModel;\n\t}\n\n\t/**\n\t * 校验 ApiKey，成功返回 ApiKeyModel，失败则抛出异常\n\t * @param apiKey /\n\t * @return /\n\t */\n\tpublic ApiKeyModel checkApiKey(String apiKey) {\n\t\tApiKeyModel ak = getApiKey(apiKey);\n\t\tif(ak == null) {\n\t\t\tthrow new ApiKeyException(\"无效 API Key: \" + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12301);\n\t\t}\n\t\tif(ak.timeExpired()) {\n\t\t\tthrow new ApiKeyException(\"API Key 已过期: \" + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12302);\n\t\t}\n\t\tif(! ak.getIsValid()) {\n\t\t\tthrow new ApiKeyException(\"API Key 已被禁用: \" + apiKey).setApiKey(apiKey).setCode(SaApiKeyErrorCode.CODE_12303);\n\t\t}\n\t\treturn ak;\n\t}\n\n\t/**\n\t * 持久化：ApiKeyModel\n\t * @param ak /\n\t */\n\tpublic void saveApiKey(ApiKeyModel ak) {\n\t\tif(ak == null) {\n\t\t\treturn;\n\t\t}","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-apikey/src/main/java/cn/dev33/satoken/apikey/template/SaApiKeyTemplate.java#L109-L145","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the exact key string being sent matches the one returned when the key was created","Check the dao backend: if using the default in-memory dao, keys are lost on restart — switch to Redis or another persistent SaTokenDao","Re-issue the key with saApiKeyTemplate.createApiKey(loginId, ...) and distribute the new value"],"exampleFix":"// before\n// key came from config file but was never registered on this server\nsaApiKeyTemplate.checkApiKey(cfg.get(\"api-key\")); // throws 12301\n\n// after\n// create the key once, store/persist it, then hand it to the client\nString apiKey = saApiKeyTemplate.createApiKey(10001, \"order-service\", 3600 * 24);\nsaApiKeyTemplate.checkApiKey(apiKey);","handlingStrategy":"try-catch","validationCode":"ApiKeyModel ak = saApiKeyTemplate.getApiKey(apiKey);\nif (ak == null) {\n    return unauthorized(\"unknown api key\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    saApiKeyTemplate.checkApiKey(apiKey);\n} catch (ApiKeyException e) {\n    if (e.getCode() == SaApiKeyErrorCode.CODE_12301) {\n        // treat as 401, do not leak whether the key ever existed\n    } else throw e;\n}","preventionTips":["Use a persistent SaTokenDao (Redis) so keys survive restarts","Clients should fetch keys from the issuing service rather than hard-coding them","Never log the full ApiKey value on failure"],"tags":["sa-token","apikey","auth","not-found"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}