From b552dd334cb24150761e23cd0a604fc63b033f20 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Mon, 26 Aug 2024 02:34:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20sa-token-oauth2=20?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=BB=86=E5=88=86=E7=8A=B6=E6=80=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-doc/_sidebar.md | 10 +-- sa-token-doc/fun/data-structure.md | 4 +- sa-token-doc/fun/exception-code.md | 55 +++++++------- sa-token-doc/oauth2/oauth2-check-domain.md | 2 +- sa-token-doc/oauth2/oauth2-custom-scope.md | 2 +- .../data/convert/SaOAuth2DataConverter.java | 7 +- .../SaOAuth2DataConverterDefaultImpl.java | 8 +- .../SaOAuth2DataGenerateDefaultImpl.java | 8 +- .../data/resolver/SaOAuth2DataResolver.java | 4 +- .../SaOAuth2DataResolverDefaultImpl.java | 36 +++++---- .../oauth2/error/SaOAuth2ErrorCode.java | 55 ++++++-------- .../SaOAuth2AuthorizationCodeException.java | 74 +++++++++++++++++++ .../handler/RefreshTokenGrantTypeHandler.java | 7 +- .../processor/SaOAuth2ServerProcessor.java | 12 +-- .../oauth2/strategy/SaOAuth2Strategy.java | 9 ++- .../oauth2/template/SaOAuth2Template.java | 31 ++++---- 16 files changed, 195 insertions(+), 129 deletions(-) create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/exception/SaOAuth2AuthorizationCodeException.java diff --git a/sa-token-doc/_sidebar.md b/sa-token-doc/_sidebar.md index f23aa033..b926c996 100644 --- a/sa-token-doc/_sidebar.md +++ b/sa-token-doc/_sidebar.md @@ -56,19 +56,19 @@ - [OAuth2-Server搭建](/oauth2/oauth2-server) - [OAuth2-Server端开放 API 接口](/oauth2/oauth2-apidoc) - [配置 client 域名校验 ](/oauth2/oauth2-check-domain) - - [定制化登录页面与授权页面](/oauth2/oauth2-custom-login) - - [自定义 API 路由 ](/oauth2/oauth2-custom-api) - - [自定义 Scope 权限以处理器](/oauth2/oauth2-custom-scope) + - [自定义 Scope 权限及处理器](/oauth2/oauth2-custom-scope) - [为 Scope 划分等级](/oauth2/oauth2-scope-level) - [自定义 grant_type](/oauth2/oauth2-custom-grant_type) + - [定制化登录页面与授权页面](/oauth2/oauth2-custom-login) + - [自定义 API 路由 ](/oauth2/oauth2-custom-api) - [开启 OIDC 协议](/oauth2/oauth2-oidc) - - [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking) - [使用注解校验 Access-Token](/oauth2/oauth2-at-check) + - [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking) - [OAuth2 代码 API 参考](/oauth2/oauth2-dev) - [常见问题总结](/oauth2/oauth2-questions) - - + - **微服务** - [分布式Session会话](/micro/dcs-session) diff --git a/sa-token-doc/fun/data-structure.md b/sa-token-doc/fun/data-structure.md index a74bafcf..f5595551 100644 --- a/sa-token-doc/fun/data-structure.md +++ b/sa-token-doc/fun/data-structure.md @@ -311,9 +311,9 @@ clientId 反查 Client-Token {tokenName}:oauth2:client-token-index:{clientId} ``` -Past-Token 次级应用令牌索引 +Lower-Client-Token 次级应用令牌索引 ``` js -{tokenName}:oauth2:past-token-index:{clientId} +{tokenName}:oauth2:lower-client-token-index:{clientId} ``` ### 3.5、用户授权记录 diff --git a/sa-token-doc/fun/exception-code.md b/sa-token-doc/fun/exception-code.md index b86aeb9d..b2df661e 100644 --- a/sa-token-doc/fun/exception-code.md +++ b/sa-token-doc/fun/exception-code.md @@ -160,37 +160,32 @@ SaToken 中的所有异常都是继承于 `SaTokenException` 的,也就是说 #### sa-token-oauth2 相关: -| code码值 | 含义 | -| :-------- | :-------- | -| 30101 | client_id 不可为空 | -| 30102 | scope 不可为空 | +| code码值 | 含义 | +| :-------- | :-------- | +| 30101 | client_id 不可为空 | +| 30102 | scope 不可为空 | | 30103 | redirect_uri 不可为空 | -| 30104 | LoginId 不可为空 | -| 30105 | 无效client_id | -| 30106 | 无效access_token | -| 30107 | 无效 client_token | -| 30108 | Access-Token 不具备指定的 Scope | -| 30109 | Client-Token 不具备指定的 Scope | -| 30110 | 无效 code 码 | -| 30111 | 无效 Refresh-Token | -| 30112 | 请求的Scope暂未签约 | -| 30113 | 无效redirect_url | -| 30114 | 非法redirect_url | -| 30115 | 无效client_secret | -| 30116 | 请求的Scope暂未签约 | -| 30117 | 无效code | -| 30118 | 无效client_id | -| 30119 | 无效client_secret | -| 30120 | 无效redirect_uri | -| 30121 | 无效refresh_token | -| 30122 | 无效client_id | -| 30123 | 无效client_secret | -| 30124 | 无效client_id | -| 30125 | 无效response_type | -| 30131 | 暂未开放授权码模式 | -| 30132 | 暂未开放隐藏式模式 | -| 30133 | 暂未开放密码式模式 | -| 30134 | 暂未开放凭证式模式 | +| 30104 | LoginId 不可为空 | +| 30105 | 无效 client_id | +| 30106 | 无效 access_token | +| 30107 | 无效 client_token | +| 30108 | Access-Token 不具备指定的 Scope | +| 30109 | Client-Token 不具备指定的 Scope | +| 30110 | 无效 code 码 | +| 30111 | 无效 Refresh-Token | +| 30112 | 请求的 Scope 暂未签约 | +| 30113 | 无效 redirect_url | +| 30114 | 非法 redirect_url | +| 30115 | 无效 client_secret | +| 30120 | redirect_uri 不一致 | +| 30122 | client_id 不一致 | +| 30125 | 无效 response_type | +| 30126 | 无效 grant_type | +| 30127 | 无效 state | +| 30141 | 系统暂未开放的授权模式 | +| 30142 | 应用暂未开放的授权模式 | +| 30151 | 无效的请求 Method | +| 30191 | 其它异常 | #### sa-token-jwt 插件相关: diff --git a/sa-token-doc/oauth2/oauth2-check-domain.md b/sa-token-doc/oauth2/oauth2-check-domain.md index 841d3ff0..de3250ca 100644 --- a/sa-token-doc/oauth2/oauth2-check-domain.md +++ b/sa-token-doc/oauth2/oauth2-check-domain.md @@ -86,7 +86,7 @@ URL 没有通过校验,拒绝授权! - 反例:`http://sa-oauth-client.com@sa-token.cc` *详见源码:[SaOAuth2Template.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java) -`checkRightUrl` 方法。* +`checkRedirectUri` 方法。* 2、AllowUrls 配置的地址 `*` 通配符只允许出现在字符串末尾,不允许出现在字符串中间位置。 diff --git a/sa-token-doc/oauth2/oauth2-custom-scope.md b/sa-token-doc/oauth2/oauth2-custom-scope.md index a857913d..e3e6e881 100644 --- a/sa-token-doc/oauth2/oauth2-custom-scope.md +++ b/sa-token-doc/oauth2/oauth2-custom-scope.md @@ -1,4 +1,4 @@ -# OAuth2-自定义权限处理器 +# OAuth2-自定义 Scope 权限及处理器 --- diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java index fddeb33a..e66097d1 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java @@ -44,12 +44,11 @@ public interface SaOAuth2DataConverter { String convertScopeListToString(List scopeList); /** - * 转换 AllowUrl 数据格式:String -> List - * @param allowUrl / + * 转换 redirect_uri 数据格式:String -> List + * @param redirectUris / * @return / */ - List convertAllowUrlStringToList(String allowUrl); - + List convertRedirectUriStringToList(String redirectUris); /** * 将 Code 转换为 Access-Token diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java index 03073085..b1738570 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java @@ -59,14 +59,14 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { } /** - * 转换 AllowUrl 数据格式:String -> List + * 转换 redirect_uri 数据格式:String -> List */ @Override - public List convertAllowUrlStringToList(String allowUrl) { - if(SaFoxUtil.isEmpty(allowUrl)) { + public List convertRedirectUriStringToList(String redirectUris) { + if(SaFoxUtil.isEmpty(redirectUris)) { return new ArrayList<>(); } - return SaFoxUtil.convertStringToList(allowUrl); + return SaFoxUtil.convertStringToList(redirectUris); } /** diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerateDefaultImpl.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerateDefaultImpl.java index 36c1e9fe..b9f5186e 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerateDefaultImpl.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerateDefaultImpl.java @@ -26,7 +26,9 @@ import cn.dev33.satoken.oauth2.data.model.RefreshTokenModel; import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel; import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel; import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; +import cn.dev33.satoken.oauth2.exception.SaOAuth2AuthorizationCodeException; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; +import cn.dev33.satoken.oauth2.exception.SaOAuth2RefreshTokenException; import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; import cn.dev33.satoken.util.SaFoxUtil; @@ -79,7 +81,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { // 1、先校验 CodeModel cm = dao.getCode(code); - SaOAuth2Exception.throwBy(cm == null, "无效code", SaOAuth2ErrorCode.CODE_30110); + SaOAuth2AuthorizationCodeException.throwBy(cm == null, "无效 code: " + code, code, SaOAuth2ErrorCode.CODE_30110); // 2、删除旧Token dao.deleteAccessToken(dao.getAccessTokenValue(cm.clientId, cm.loginId)); @@ -118,7 +120,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { // 获取 Refresh-Token 信息 RefreshTokenModel rt = dao.getRefreshToken(refreshToken); - SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken, SaOAuth2ErrorCode.CODE_30111); + SaOAuth2RefreshTokenException.throwBy(rt == null, "无效 refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30111); // 如果配置了[每次刷新产生新的Refresh-Token] SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(rt.clientId); @@ -276,7 +278,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { public void checkState(String state) { String value = SaOAuth2Manager.getDao().getState(state); if(SaFoxUtil.isNotEmpty(value)) { - throw new SaOAuth2Exception("多次请求的 state 不可重复: " + state); + throw new SaOAuth2Exception("多次请求的 state 不可重复: " + state).setCode(SaOAuth2ErrorCode.CODE_30127); } SaOAuth2Manager.getDao().saveState(state); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolver.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolver.java index 49d3e949..37aacbd1 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolver.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolver.java @@ -72,7 +72,7 @@ public interface SaOAuth2DataResolver { * @param at token信息 * @return / */ - Map buildTokenReturnValue(AccessTokenModel at); + Map buildAccessTokenReturnValue(AccessTokenModel at); /** * 构建返回值: RefreshToken 刷新 Access-Token @@ -80,7 +80,7 @@ public interface SaOAuth2DataResolver { * @return / */ default Map buildRefreshTokenReturnValue(AccessTokenModel at) { - return buildTokenReturnValue(at); + return buildAccessTokenReturnValue(at); } /** diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java index 4cac496b..64ade8a6 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java @@ -18,12 +18,13 @@ package cn.dev33.satoken.oauth2.data.resolver; import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil; import cn.dev33.satoken.oauth2.SaOAuth2Manager; -import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; +import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.TokenType; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel; import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel; +import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; @@ -50,8 +51,8 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { @Override public ClientIdAndSecretModel readClientIdAndSecret(SaRequest request) { // 优先从请求参数中获取 - String clientId = request.getParam(SaOAuth2Consts.Param.client_id); - String clientSecret = request.getParam(SaOAuth2Consts.Param.client_secret); + String clientId = request.getParam(Param.client_id); + String clientSecret = request.getParam(Param.client_secret); if(SaFoxUtil.isNotEmpty(clientId)) { return new ClientIdAndSecretModel(clientId, clientSecret); } @@ -68,22 +69,22 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { } // 如果都没有提供,则抛出异常 - throw new SaOAuth2Exception("请提供 client 信息"); + throw new SaOAuth2Exception("请提供 client 信息").setCode(SaOAuth2ErrorCode.CODE_30191); } /** - * 数据读取:从请求对象中读取 AccessToken + * 数据读取:从请求对象中读取 AccessToken,获取不到返回 null */ @Override public String readAccessToken(SaRequest request) { // 优先从请求参数中获取 - String accessToken = request.getParam(SaOAuth2Consts.Param.access_token); + String accessToken = request.getParam(Param.access_token); if(SaFoxUtil.isNotEmpty(accessToken)) { return accessToken; } // 如果请求参数中没有提供 access_token 参数,则尝试从 Authorization 中获取 - String authorizationValue = request.getHeader(SaOAuth2Consts.Param.Authorization); + String authorizationValue = request.getHeader(Param.Authorization); if(SaFoxUtil.isEmpty(authorizationValue)) { return null; } @@ -99,18 +100,18 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { } /** - * 数据读取:从请求对象中读取 ClientToken + * 数据读取:从请求对象中读取 ClientToken,获取不到返回 null */ @Override public String readClientToken(SaRequest request) { // 优先从请求参数中获取 - String clientToken = request.getParam(SaOAuth2Consts.Param.client_token); + String clientToken = request.getParam(Param.client_token); if(SaFoxUtil.isNotEmpty(clientToken)) { return clientToken; } // 如果请求参数中没有提供 client_token 参数,则尝试从 Authorization 中获取 - String authorizationValue = request.getHeader(SaOAuth2Consts.Param.Authorization); + String authorizationValue = request.getHeader(Param.Authorization); if(SaFoxUtil.isEmpty(authorizationValue)) { return null; } @@ -131,13 +132,11 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { @Override public RequestAuthModel readRequestAuthModel(SaRequest req, Object loginId) { RequestAuthModel ra = new RequestAuthModel(); - ra.clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id); - ra.responseType = req.getParamNotNull(SaOAuth2Consts.Param.response_type); - ra.redirectUri = req.getParamNotNull(SaOAuth2Consts.Param.redirect_uri); - ra.state = req.getParam(SaOAuth2Consts.Param.state); - // 数据解析 - String scope = req.getParam(SaOAuth2Consts.Param.scope, ""); - ra.scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope); + ra.clientId = req.getParamNotNull(Param.client_id); + ra.responseType = req.getParamNotNull(Param.response_type); + ra.redirectUri = req.getParamNotNull(Param.redirect_uri); + ra.state = req.getParam(Param.state); + ra.scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(req.getParam(Param.scope)); ra.loginId = loginId; return ra; } @@ -147,7 +146,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { * 构建返回值: 获取 token */ @Override - public Map buildTokenReturnValue(AccessTokenModel at) { + public Map buildAccessTokenReturnValue(AccessTokenModel at) { Map map = new LinkedHashMap<>(); map.put("token_type", at.tokenType); map.put("access_token", at.accessToken); @@ -172,7 +171,6 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { Map map = new LinkedHashMap<>(); map.put("token_type", ct.tokenType); map.put("client_token", ct.clientToken); - // 兼容 OAuth2 协议 if(SaOAuth2Manager.getServerConfig().mode4ReturnAccessToken) { map.put("access_token", ct.clientToken); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java index b6f3cbd7..37026d7e 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java @@ -35,10 +35,10 @@ public interface SaOAuth2ErrorCode { /** LoginId 不可为空 */ int CODE_30104 = 30104; - /** 无效client_id */ + /** 无效 client_id */ int CODE_30105 = 30105; - /** 无效access_token */ + /** 无效 access_token */ int CODE_30106 = 30106; /** 无效 client_token */ @@ -50,57 +50,39 @@ public interface SaOAuth2ErrorCode { /** Client-Token 不具备指定的 Scope */ int CODE_30109 = 30109; - /** 无效 code 码 */ + /** 无效 Code 码 */ int CODE_30110 = 30110; /** 无效 Refresh-Token */ int CODE_30111 = 30111; - /** 请求的Scope暂未签约 */ + /** 请求的 Scope 暂未签约 */ int CODE_30112 = 30112; - /** 无效redirect_url */ + /** 无效 redirect_url */ int CODE_30113 = 30113; - /** 非法redirect_url */ + /** 非法 redirect_url */ int CODE_30114 = 30114; /** 无效client_secret */ int CODE_30115 = 30115; - /** 请求的Scope暂未签约 */ - int CODE_30116 = 30116; - - /** 无效code */ - int CODE_30117 = 30117; - - /** 无效client_id */ - int CODE_30118 = 30118; - - /** 无效client_secret */ - int CODE_30119 = 30119; - - /** 无效redirect_uri */ + /** redirect_uri 不一致 */ int CODE_30120 = 30120; - /** 无效refresh_token */ - int CODE_30121 = 30121; - - /** 无效client_id */ + /** client_id 不一致 */ int CODE_30122 = 30122; - /** 无效client_secret */ - int CODE_30123 = 30123; - - /** 无效client_id */ - int CODE_30124 = 30124; - - /** 无效response_type */ + /** 无效 response_type */ int CODE_30125 = 30125; - /** 无效grant_type */ + /** 无效 grant_type */ int CODE_30126 = 30126; + /** 无效 state */ + int CODE_30127 = 30127; + /** 暂未开放授权码模式 */ int CODE_30131 = 30131; @@ -113,7 +95,16 @@ public interface SaOAuth2ErrorCode { /** 暂未开放凭证式模式 */ int CODE_30134 = 30134; - /** 无效的请求 Method */ + /** 系统暂未开放的授权模式 */ int CODE_30141 = 30141; + /** 应用暂未开放的授权模式 */ + int CODE_30142 = 30142; + + /** 无效的请求 Method */ + int CODE_30151 = 30151; + + /** 其它异常 */ + int CODE_30191 = 30191; + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/exception/SaOAuth2AuthorizationCodeException.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/exception/SaOAuth2AuthorizationCodeException.java new file mode 100644 index 00000000..5b8d45db --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/exception/SaOAuth2AuthorizationCodeException.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.oauth2.exception; + +/** + * 一个异常:代表 Code 授权码相关错误 + * + * @author click33 + * @since 1.39.0 + */ +public class SaOAuth2AuthorizationCodeException extends SaOAuth2Exception { + + /** + * 序列化版本号 + */ + private static final long serialVersionUID = 6806129545290130114L; + + /** + * 一个异常:代表 Access-Token 相关错误 + * @param cause 根异常原因 + */ + public SaOAuth2AuthorizationCodeException(Throwable cause) { + super(cause); + } + + /** + * 一个异常:代表 Access-Token 相关错误 + * @param message 异常描述 + */ + public SaOAuth2AuthorizationCodeException(String message) { + super(message); + } + + /** + * 具体引起异常的 code 值 + */ + public String authorizationCode; + + public String getAuthorizationCode() { + return authorizationCode; + } + + public SaOAuth2AuthorizationCodeException setAuthorizationCode(String authorizationCode) { + this.authorizationCode = authorizationCode; + return this; + } + + /** + * 如果 flag==true,则抛出 message 异常 + * @param flag 标记 + * @param message 异常信息 + * @param authorizationCode 引入异常的 code 值 + * @param code 异常细分码 + */ + public static void throwBy(boolean flag, String message, String authorizationCode, int code) { + if(flag) { + throw new SaOAuth2AuthorizationCodeException(message).setAuthorizationCode(authorizationCode).setCode(code); + } + } + +} diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/RefreshTokenGrantTypeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/RefreshTokenGrantTypeHandler.java index 238e35c1..df0143f5 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/RefreshTokenGrantTypeHandler.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/RefreshTokenGrantTypeHandler.java @@ -22,7 +22,8 @@ import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import cn.dev33.satoken.oauth2.data.model.RefreshTokenModel; import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; -import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; +import cn.dev33.satoken.oauth2.exception.SaOAuth2ClientModelException; +import cn.dev33.satoken.oauth2.exception.SaOAuth2RefreshTokenException; import java.util.List; @@ -46,10 +47,10 @@ public class RefreshTokenGrantTypeHandler implements SaOAuth2GrantTypeHandlerInt // 校验:Refresh-Token 是否存在 RefreshTokenModel rt = SaOAuth2Manager.getDao().getRefreshToken(refreshToken); - SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken, SaOAuth2ErrorCode.CODE_30121); + SaOAuth2RefreshTokenException.throwBy(rt == null, "无效refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30111); // 校验:Refresh-Token 代表的 ClientId 与提供的 ClientId 是否一致 - SaOAuth2Exception.throwBy( ! rt.clientId.equals(clientId), "无效client_id: " + clientId, SaOAuth2ErrorCode.CODE_30122); + SaOAuth2ClientModelException.throwBy( ! rt.clientId.equals(clientId), "无效client_id: " + clientId, clientId, SaOAuth2ErrorCode.CODE_30122); // 获取新 Access-Token AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().refreshAccessToken(refreshToken); diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java index e6401002..840d9f4d 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java @@ -157,7 +157,7 @@ public class SaOAuth2ServerProcessor { } // 默认返回 - throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125); + throw new SaOAuth2Exception("无效 response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125); } /** @@ -166,7 +166,7 @@ public class SaOAuth2ServerProcessor { */ public Object token() { AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(SaHolder.getRequest()); - return SaOAuth2Manager.getDataResolver().buildTokenReturnValue(accessTokenModel); + return SaOAuth2Manager.getDataResolver().buildAccessTokenReturnValue(accessTokenModel); } /** @@ -239,7 +239,7 @@ public class SaOAuth2ServerProcessor { // 此请求只允许 POST 方式 if(!req.isMethod(SaHttpMethod.POST)) { - throw new SaOAuth2Exception("无效请求方式:" + req.getMethod()).setCode(SaOAuth2ErrorCode.CODE_30141); + throw new SaOAuth2Exception("无效请求方式:" + req.getMethod()).setCode(SaOAuth2ErrorCode.CODE_30151); } // 确认授权 @@ -366,7 +366,7 @@ public class SaOAuth2ServerProcessor { } // 其它 else { - throw new SaOAuth2Exception("无效 response_type: " + req.getParam(Param.response_type)).setCode(SaOAuth2ErrorCode.CODE_30125); + throw new SaOAuth2Exception("无效 response_type: " + responseType).setCode(SaOAuth2ErrorCode.CODE_30125); } } @@ -374,14 +374,14 @@ public class SaOAuth2ServerProcessor { * 系统未开放此授权模式时抛出异常 */ public void throwErrorSystemNotEnableModel() { - throw new SaOAuth2Exception("系统暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131); + throw new SaOAuth2Exception("系统暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30141); } /** * 应用未开放此授权模式时抛出异常 */ public void throwErrorClientNotEnableModel() { - throw new SaOAuth2Exception("应用暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131); + throw new SaOAuth2Exception("应用暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30142); } } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java index e9ca6372..ec722221 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java @@ -22,6 +22,7 @@ import cn.dev33.satoken.oauth2.consts.GrantType; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel; import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel; +import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.oauth2.function.strategy.*; import cn.dev33.satoken.oauth2.granttype.handler.AuthorizationCodeGrantTypeHandler; @@ -162,16 +163,16 @@ public final class SaOAuth2Strategy { String grantType = req.getParamNotNull(SaOAuth2Consts.Param.grant_type); SaOAuth2GrantTypeHandlerInterface grantTypeHandler = grantTypeHandlerMap.get(grantType); if(grantTypeHandler == null) { - throw new RuntimeException("无效 grant_type: " + grantType); + throw new SaOAuth2Exception("无效 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); } // 看看全局是否开启了此 grantType SaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig(); if(grantType.equals(GrantType.authorization_code) && !config.getEnableAuthorizationCode() ) { - throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType); + throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); } if(grantType.equals(GrantType.password) && !config.getEnablePassword() ) { - throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType); + throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); } // 校验 clientSecret 和 scope @@ -181,7 +182,7 @@ public final class SaOAuth2Strategy { // 检测应用是否开启此 grantType if(!clientModel.getAllowGrantTypes().contains(grantType)) { - throw new SaOAuth2Exception("应用未开放的 grant_type: " + grantType); + throw new SaOAuth2Exception("应用未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30141); } // 调用 处理器 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java index 2e6a506c..5ce1b8fb 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java @@ -72,8 +72,11 @@ public class SaOAuth2Template { */ public SaClientModel checkClientSecret(String clientId, String clientSecret) { SaClientModel cm = checkClientModel(clientId); - SaOAuth2ClientModelException.throwBy(cm.clientSecret == null || ! cm.clientSecret.equals(clientSecret), "无效client_secret: " + clientSecret, - clientId, SaOAuth2ErrorCode.CODE_30115); + if(cm.clientSecret == null || ! cm.clientSecret.equals(clientSecret)) { + throw new SaOAuth2ClientModelException("无效 client_secret: " + clientSecret) + .setClientId(clientId) + .setCode(SaOAuth2ErrorCode.CODE_30115); + } return cm; } @@ -146,7 +149,7 @@ public class SaOAuth2Template { public void checkRedirectUri(String clientId, String url) { // 1、是否是一个有效的url if( ! SaFoxUtil.isUrl(url)) { - throw new SaOAuth2ClientModelException("无效redirect_url:" + url) + throw new SaOAuth2ClientModelException("无效 redirect_url:" + url) .setClientId(clientId) .setCode(SaOAuth2ErrorCode.CODE_30113); } @@ -180,7 +183,8 @@ public class SaOAuth2Template { // // 但是为了安全起见,这么做还是有必要的 throw new SaOAuth2ClientModelException("无效 redirect_url(不允许出现@字符):" + url) - .setClientId(clientId); + .setClientId(clientId) + .setCode(SaOAuth2ErrorCode.CODE_30113); } // 4、是否在[允许地址列表]之中 @@ -231,7 +235,8 @@ public class SaOAuth2Template { // http://sa-oauth-server.com:8000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://shop.sa-oauth2-client.com/ // // 但是为了安全起见,这么做还是有必要的 - throw new SaOAuth2Exception("无效的 allow-url 配置(*通配符只允许出现在最后一位):" + url); + throw new SaOAuth2Exception("无效的 allow-url 配置(*通配符只允许出现在最后一位):" + url) + .setCode(SaOAuth2ErrorCode.CODE_30114); } } } @@ -299,18 +304,18 @@ public class SaOAuth2Template { // 校验:Code是否存在 CodeModel cm = dao.getCode(code); - SaOAuth2Exception.throwBy(cm == null, "无效 code: " + code, SaOAuth2ErrorCode.CODE_30117); + SaOAuth2AuthorizationCodeException.throwBy(cm == null, "无效 code: " + code, code, SaOAuth2ErrorCode.CODE_30110); // 校验:ClientId是否一致 - SaOAuth2Exception.throwBy( ! cm.clientId.equals(clientId), "无效 client_id: " + clientId, SaOAuth2ErrorCode.CODE_30118); + SaOAuth2ClientModelException.throwBy( ! cm.clientId.equals(clientId), "无效 client_id: " + clientId, clientId, SaOAuth2ErrorCode.CODE_30105); // 校验:Secret是否正确 String dbSecret = checkClientModel(clientId).clientSecret; - SaOAuth2Exception.throwBy(dbSecret == null || ! dbSecret.equals(clientSecret), "无效 client_secret: " + clientSecret, SaOAuth2ErrorCode.CODE_30119); + SaOAuth2ClientModelException.throwBy(dbSecret == null || ! dbSecret.equals(clientSecret), "无效 client_secret: " + clientSecret, clientId, SaOAuth2ErrorCode.CODE_30115); // 如果提供了redirectUri,则校验其是否与请求Code时提供的一致 if( ! SaFoxUtil.isEmpty(redirectUri)) { - SaOAuth2Exception.throwBy( ! redirectUri.equals(cm.redirectUri), "无效 redirect_uri: " + redirectUri, SaOAuth2ErrorCode.CODE_30120); + SaOAuth2ClientModelException.throwBy( ! redirectUri.equals(cm.redirectUri), "无效 redirect_uri: " + redirectUri, clientId, SaOAuth2ErrorCode.CODE_30120); } // 返回CodeModel @@ -330,15 +335,15 @@ public class SaOAuth2Template { // 校验:Refresh-Token是否存在 RefreshTokenModel rt = dao.getRefreshToken(refreshToken); - SaOAuth2RefreshTokenException.throwBy(rt == null, "无效 refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30121); + SaOAuth2RefreshTokenException.throwBy(rt == null, "无效 refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30111); // 校验:ClientId是否一致 SaOAuth2ClientModelException.throwBy( ! rt.clientId.equals(clientId), "无效 client_id: " + clientId, clientId, SaOAuth2ErrorCode.CODE_30122); // 校验:Secret是否正确 String dbSecret = checkClientModel(clientId).clientSecret; - SaOAuth2ClientModelException.throwBy(dbSecret == null || ! dbSecret.equals(clientSecret), "无效client_secret: " + clientSecret, - clientId, SaOAuth2ErrorCode.CODE_30123); + SaOAuth2ClientModelException.throwBy(dbSecret == null || ! dbSecret.equals(clientSecret), "无效 client_secret: " + clientSecret, + clientId, SaOAuth2ErrorCode.CODE_30115); // 返回 Refresh-Token return rt; @@ -353,7 +358,7 @@ public class SaOAuth2Template { */ public AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) { AccessTokenModel at = checkAccessToken(accessToken); - SaOAuth2ClientModelException.throwBy( ! at.clientId.equals(clientId), "无效 client_id:" + clientId, clientId, SaOAuth2ErrorCode.CODE_30124); + SaOAuth2ClientModelException.throwBy( ! at.clientId.equals(clientId), "无效 client_id:" + clientId, clientId, SaOAuth2ErrorCode.CODE_30122); checkClientSecret(clientId, clientSecret); return at; }