{"record":{"id":"214a898b5c086bda","repo":"dromara/Sa-Token","slug":"30106","errorCode":"30106","errorMessage":"无效 access_token: ","messagePattern":"无效 access_token: ","errorType":"exception","errorClass":"SaOAuth2AccessTokenException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java","lineNumber":433,"sourceCode":"\n\t/**\n\t * 获取 AccessTokenModel，无效的 AccessToken 会返回 null\n\t * @param accessToken /\n\t * @return /\n\t */\n\tpublic AccessTokenModel getAccessToken(String accessToken) {\n\t\treturn SaOAuth2Manager.getDao().getAccessToken(accessToken);\n\t}\n\n\t/**\n\t * 校验 Access-Token，成功返回 AccessTokenModel，失败则抛出异常\n\t * @param accessToken /\n\t * @return /\n\t */\n\tpublic AccessTokenModel checkAccessToken(String accessToken) {\n\t\tAccessTokenModel at = SaOAuth2Manager.getDao().getAccessToken(accessToken);\n\t\tif(at == null) {\n\t\t\tthrow new SaOAuth2AccessTokenException(\"无效 access_token: \" + accessToken)\n\t\t\t\t\t.setAccessToken(accessToken)\n\t\t\t\t\t.setCode(SaOAuth2ErrorCode.CODE_30106);\n\t\t}\n\t\treturn at;\n\t}\n\n\t/**\n\t * 获取 Access-Token 列表：此应用下 对 某个用户 签发的所有 Access-token\n\t *\n\t * @param clientId /\n\t * @param loginId /\n\t * @return /\n\t */\n\tpublic List<String> getAccessTokenValueList(String clientId, Object loginId) {\n\t\treturn SaOAuth2Manager.getDao().getAccessTokenValueList_FromAdjustAfter(clientId, loginId);\n\t}\n\n\t/**","sourceCodeStart":415,"sourceCodeEnd":451,"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#L415-L451","documentation":"Thrown by SaOAuth2Template.checkAccessToken when the access token lookup in the DAO returns null (code 30106, SaOAuth2AccessTokenException). The token was never issued by this server, has expired, was revoked, or its storage was cleared.","triggerScenarios":"Calling checkAccessToken (directly or via an API-gateway/filter that validates access tokens) with a fabricated, expired, revoked, or truncated token string.","commonSituations":"Token expired between calls (short timeout config); server restart with default in-memory DAO wiping all tokens; Redis flush/eviction; client sends the refresh token or client token where the access token belongs; token copied with whitespace/newline from logs.","solutions":["Return a fresh access token via the refresh_token grant or re-run the authorization flow","Verify the exact token value is transmitted (Authorization header, no truncation, no whitespace)","Use a persistent shared DAO (Redis) across server instances and confirm the access-token timeout configuration","If revoked unintentionally, check revokeToken/revoke by clientId+loginId calls in your code"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"AccessTokenModel at = SaOAuth2Manager.getDao().getAccessToken(token);\nif (at == null) { token = refreshOrReauthorize(); }","typeGuard":null,"tryCatchPattern":"try { saOAuth2Template.checkAccessToken(token); } catch (SaOAuth2AccessTokenException e) { token = client.refreshAccessToken(); retry(request); }","preventionTips":["Track token expiry client-side and refresh proactively before it lapses","Persist tokens via a shared DAO (Redis) in clustered deployments"],"tags":["oauth2","access-token","validation","token"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}