{"record":{"id":"b77434fe8f5f230d","repo":"dromara/Sa-Token","slug":"12302","errorCode":"12302","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":130,"sourceCode":"\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}\n\t\t// 数据自检\n\t\tak.checkByCanSaved();\n","sourceCodeStart":112,"sourceCodeEnd":148,"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#L112-L148","documentation":"Thrown by SaApiKeyTemplate.checkApiKey(String) when the ApiKeyModel exists but ak.timeExpired() returns true — the key's expiresTime has passed. Code 12302 marks an expired key; the record is still present, only its validity window has elapsed.","triggerScenarios":"Calling checkApiKey / checkApiKeyScope / checkApiKeyScopeOr with a key whose expiresTime timestamp is in the past (only keys created with a finite expiry can trigger this).","commonSituations":"Short-lived keys expiring between requests; long-running batch jobs using a key created at startup; clock skew between app servers; admin set a short expiry without noticing; key created with expiresTime accidentally in the past.","solutions":["Issue a new key via createApiKey(...) and update the client with the new value","For long-lived integrations, create the key with SaTokenDao.NEVER_EXPIRE or a multi-year window","If expiry is intentional, rotate keys on a schedule before they lapse (monitor expiresIn())"],"exampleFix":"// before\n// key created with 1-hour expiry yesterday\nsaApiKeyTemplate.checkApiKey(oldKey); // throws 12302\n\n// after\nString newKey = saApiKeyTemplate.createApiKey(10001, \"order-service\", SaTokenDao.NEVER_EXPIRE);\nsaApiKeyTemplate.checkApiKey(newKey);","handlingStrategy":"retry","validationCode":"ApiKeyModel ak = saApiKeyTemplate.getApiKey(apiKey);\nif (ak != null && ak.timeExpired()) {\n    apiKey = reissueKey(ak.getLoginId()); // createApiKey(...)\n}","typeGuard":null,"tryCatchPattern":"catch (ApiKeyException e) { if (e.getCode() == SaApiKeyErrorCode.CODE_12302) { apiKey = reissue(); return retry(request); } throw e; }","preventionTips":["Monitor ak.expiresIn() and rotate before expiry","For service-to-service keys choose NEVER_EXPIRE plus a revocation strategy, or long windows with scheduled rotation"],"tags":["sa-token","apikey","auth","expiry"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}