{"record":{"id":"637beab6eca3c6cc","repo":"dromara/Sa-Token","slug":"12311","errorCode":"12311","errorMessage":"该 API Key 不具备 Scope：","messagePattern":"该 API Key 不具备 Scope：","errorType":"exception","errorClass":"ApiKeyScopeException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-apikey/src/main/java/cn/dev33/satoken/apikey/template/SaApiKeyTemplate.java","lineNumber":315,"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 是否具有指定 Scope 列表 (AND 模式，需要全部具备)，如果不具备则抛出异常\n\t * @param apiKey ApiKey\n\t * @param scopes 需要校验的权限列表\n\t */\n\tpublic void checkApiKeyScope(String apiKey, String... scopes) {\n\t\tApiKeyModel ak = checkApiKey(apiKey);\n\t\tif(SaFoxUtil.isEmptyArray(scopes)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String scope : scopes) {\n\t\t\tif(! ak.getScopes().contains(scope)) {\n\t\t\t\tthrow new ApiKeyScopeException(\"该 API Key 不具备 Scope：\" + scope)\n\t\t\t\t\t\t.setApiKey(apiKey)\n\t\t\t\t\t\t.setScope(scope)\n\t\t\t\t\t\t.setCode(SaApiKeyErrorCode.CODE_12311);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 判断：指定 ApiKey 是否具有指定 Scope 列表 (OR 模式，具备其一即可)，返回 true 或 false\n\t * @param apiKey ApiKey\n\t * @param scopes 需要校验的权限列表\n\t */\n\tpublic boolean hasApiKeyScopeOr(String apiKey, String... scopes) {\n\t\ttry {\n\t\t\tcheckApiKeyScopeOr(apiKey, scopes);\n\t\t\treturn true;\n\t\t} catch (ApiKeyException e) {\n\t\t\treturn false;","sourceCodeStart":297,"sourceCodeEnd":333,"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#L297-L333","documentation":"Thrown by SaApiKeyTemplate.checkApiKeyScope(String, String...) in AND mode: the key passes checkApiKey but its scopes list does not contain one of the required scopes. Code 12311 is the ApiKeyScopeException error code; the exception carries the missing scope.","triggerScenarios":"Calling checkApiKeyScope(apiKey, \"userinfo\", \"order\") when the key's scopes collection lacks at least one of them (all must be present); annotation-based scope checks on a route hit the same path.","commonSituations":"Key was issued with scopes [\"userinfo\"] but the endpoint now requires [\"userinfo\",\"pay\"]; scope naming mismatch (\"user-info\" vs \"userinfo\", case); adding a new required scope to an API without re-issuing keys.","solutions":["Re-create the key including the missing scope: createApiKey(loginId, clientToken, timeout, scopes) with the full required set","Check for typos/case mismatch between the scope string required by the code and the one stored on the key","Inspect the stored scopes first: saApiKeyTemplate.getApiKey(apiKey).getScopes()"],"exampleFix":"// before\n// key created with scopes \"userinfo\" only\nsaApiKeyTemplate.checkApiKeyScope(apiKey, \"userinfo\", \"order\"); // throws 12311\n\n// after\nString key = saApiKeyTemplate.createApiKey(10001, \"svc\", 3600*24, Arrays.asList(\"userinfo\", \"order\"));\nsaApiKeyTemplate.checkApiKeyScope(key, \"userinfo\", \"order\");","handlingStrategy":"validation","validationCode":"ApiKeyModel ak = saApiKeyTemplate.getApiKey(apiKey);\nSet<String> need = new HashSet<>(Arrays.asList(\"userinfo\", \"order\"));\nif (ak == null || !ak.getScopes().containsAll(need)) {\n    return forbidden(\"missing scope\");\n}","typeGuard":null,"tryCatchPattern":"catch (ApiKeyScopeException e) { if (e.getCode() == SaApiKeyErrorCode.CODE_12311) { /* 403 with required scopes listed */ } }","preventionTips":["Issue keys with the exact scope set each endpoint requires; document scopes per route","Store scope strings in shared constants to avoid typos between issuance and enforcement"],"tags":["sa-token","apikey","authorization","scope"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}