{"record":{"id":"6ae553b682fb3289","repo":"dromara/Sa-Token","slug":"30108","errorCode":"30108","errorMessage":"该 access_token 不具备 scope：","messagePattern":"该 access_token 不具备 scope：","errorType":"exception","errorClass":"SaOAuth2AccessTokenScopeException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java","lineNumber":477,"sourceCode":"\t\t\treturn true;\n\t\t} catch (SaOAuth2AccessTokenException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 校验：指定 Access-Token 是否具有指定 Scope 列表，如果不具备则抛出异常\n\t * @param accessToken Access-Token\n\t * @param scopes 需要校验的权限列表\n\t */\n\tpublic void checkAccessTokenScope(String accessToken, String... scopes) {\n\t\tAccessTokenModel at = checkAccessToken(accessToken);\n\t\tif(SaFoxUtil.isEmptyArray(scopes)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String scope : scopes) {\n\t\t\tif(! at.scopes.contains(scope)) {\n\t\t\t\tthrow new SaOAuth2AccessTokenScopeException(\"该 access_token 不具备 scope：\" + scope)\n\t\t\t\t\t\t.setAccessToken(accessToken)\n\t\t\t\t\t\t.setScope(scope)\n\t\t\t\t\t\t.setCode(SaOAuth2ErrorCode.CODE_30108);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 获取 Access-Token 所代表的LoginId\n\t * @param accessToken Access-Token\n\t * @return LoginId\n\t */\n\tpublic Object getLoginIdByAccessToken(String accessToken) {\n\t\treturn checkAccessToken(accessToken).loginId;\n\t}\n\n\t/**\n\t * 获取 Access-Token 所代表的 clientId","sourceCodeStart":459,"sourceCodeEnd":495,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java#L459-L495","documentation":"Thrown by checkAccessTokenScope when a valid access token does not include one of the scopes being enforced (code 30108, SaOAuth2AccessTokenScopeException). The token exists but was granted a narrower scope set than the API requires, so access is denied at the scope level rather than the token level.","triggerScenarios":"Calling an API protected with SaOAuth2Util.checkAccessToken(token, \"userinfo\") (or StpUtil with scope checks) when the authorize request that minted the token did not include 'userinfo' in its scope parameter.","commonSituations":"New API endpoint requires a scope the client never requests; user unchecked a scope on the consent page; client's configured allow-scopes on the server omit the needed scope; scopes string case or separator mismatch (comma vs space).","solutions":["Include the required scope in the authorize request: /oauth2/authorize?...&scope=userinfo,order","Add the missing scope to the client's allow-scope configuration on the OAuth2 server so it can be requested/granted","Verify scope name spelling and separator (sa-token uses commas) match between the API check and the granted token"],"exampleFix":"// before\nString url = server + \"/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=...\"; // no scope\n// after\nString url = server + \"/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=...&scope=userinfo,order\";","handlingStrategy":"try-catch","validationCode":"AccessTokenModel at = saOAuth2Template.getAccessToken(token);\nboolean hasScope = at != null && java.util.Arrays.asList(scopes).stream().allMatch(s -> at.scopes.contains(s));","typeGuard":null,"tryCatchPattern":"try { saOAuth2Template.checkAccessTokenScope(token, \"userinfo\"); } catch (SaOAuth2AccessTokenScopeException e) { return status(403, \"missing scope: \" + e.getScope()); }","preventionTips":["Request the full scope set the APIs will need at authorize time","Keep a single constant list of scope names shared by client and server code"],"tags":["oauth2","scope","access-token","authorization"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}