{"record":{"id":"c5ab53b2056539d5","repo":"dromara/Sa-Token","slug":"12312","errorCode":"12312","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":384,"sourceCode":"\t\t\treturn true;\n\t\t} catch (ApiKeyException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 校验：指定 ApiKey 是否属于指定 LoginId，如果不是则抛出异常\n\t *\n\t * @param apiKey /\n\t * @param loginId /\n\t */\n\tpublic void checkApiKeyLoginId(String apiKey, Object loginId) {\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 (SaFoxUtil.notEquals(String.valueOf(ak.getLoginId()), String.valueOf(loginId))) {\n\t\t\tthrow new ApiKeyException(\"该 API Key 不属于用户: \" + loginId)\n\t\t\t\t\t.setApiKey(apiKey)\n\t\t\t\t\t.setCode(SaApiKeyErrorCode.CODE_12312);\n\t\t}\n\t}\n\n\n\t// ------------------- 索引操作\n\n\t/**\n\t * 调整指定 SaSession 的 TTL 值，以保证最小化内存占用\n\t * @param loginId /\n\t * @param session 可填写 null，代表使用 loginId 现场查询\n\t */\n\tpublic void adjustIndex(Object loginId, SaSession session) {\n\t\t// 先判断是否开启索引\n\t\tif(! getIsRecordIndex()) {\n\t\t\tSaManager.getLog().warn(\"当前 API Key 模块未开启索引记录功能，无法执行 adjustIndex 操作\");\n\t\t\treturn;","sourceCodeStart":366,"sourceCodeEnd":402,"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#L366-L402","documentation":"Thrown by SaApiKeyTemplate.checkApiKeyLoginId(String, Object) when the key exists but its loginId (stringified) does not equal the passed loginId. Code 12312 marks an ownership mismatch — the key is valid but belongs to a different account.","triggerScenarios":"Calling checkApiKeyLoginId(apiKey, loginId) where the key was created for loginId A but the request context/login claims loginId B. Comparison is string-based (String.valueOf on both sides).","commonSituations":"User A's key used against user B's session (potential credential leakage or client bug); type mismatch such as Integer 10001 vs Long 10001 vs String \"10001\" usually still matches after stringification, but a padded/formatted id (\"010001\") will not; key stored under a different account id after data migration.","solutions":["Verify which loginId the key was issued to: saApiKeyTemplate.getApiKey(apiKey).getLoginId()","Make the client use its own key (per-account issuance) instead of a shared one, or drop the ownership check if the key is intentionally shared","Check for id formatting differences (leading zeros, numeric type vs string) between creation and check time"],"exampleFix":"// before\n// session user 10002, but key belongs to 10001\nsaApiKeyTemplate.checkApiKeyLoginId(apiKey, StpUtil.getLoginId()); // throws 12312\n\n// after\n// issue per-user keys so ownership holds\nString key = saApiKeyTemplate.createApiKey(StpUtil.getLoginIdAsLong(), \"self\", 3600*24);\nsaApiKeyTemplate.checkApiKeyLoginId(key, StpUtil.getLoginId());","handlingStrategy":"validation","validationCode":"ApiKeyModel ak = saApiKeyTemplate.getApiKey(apiKey);\nif (ak == null || !String.valueOf(ak.getLoginId()).equals(String.valueOf(currentLoginId))) {\n    return forbidden(\"key does not belong to current user\");\n}","typeGuard":null,"tryCatchPattern":"catch (ApiKeyException e) { if (e.getCode() == SaApiKeyErrorCode.CODE_12312) { /* 403: potential key misuse, log security event */ } }","preventionTips":["Normalize loginId to one canonical string form at issuance and at check time","Treat 12312 as suspicious activity and alert, do not just return 403"],"tags":["sa-token","apikey","authorization","ownership"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}