From 15e2d9f66880977779d3d150f5c258726bfa9444 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 14 Aug 2024 18:11:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=20scope=20=E5=AD=97=E6=AE=B5=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20List=20=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/dev33/satoken/util/SaFoxUtil.java | 3 +- .../com/pj/oauth2/SaOAuth2DataLoaderImpl.java | 4 +- .../pj/oauth2/SaOAuth2ServerController.java | 36 ++++--- .../dev33/satoken/oauth2/SaOAuth2Manager.java | 20 ++++ .../satoken/oauth2/config/SaOAuth2Config.java | 59 ++--------- .../data/convert/SaOAuth2DataConverter.java | 49 +++++++++ .../SaOAuth2DataConverterDefaultImpl.java | 64 ++++++++++++ .../oauth2/data/model/AccessTokenModel.java | 91 +++++++++++++++-- .../oauth2/data/model/ClientTokenModel.java | 18 ++-- .../satoken/oauth2/data/model/CodeModel.java | 69 +++++-------- .../oauth2/data/model/RefreshTokenModel.java | 59 ++++++++++- .../oauth2/data/model/RequestAuthModel.java | 17 ++-- .../oauth2/data/model/SaClientModel.java | 27 ++--- .../SaOAuth2DataResolverDefaultImpl.java | 5 +- .../function/SaOAuth2ConfirmViewFunction.java | 34 +++++++ .../SaOAuth2DoLoginHandleFunction.java | 33 +++++++ .../SaOAuth2NotLoginViewFunction.java | 33 +++++++ .../processor/SaOAuth2ServerProcessor.java | 29 +++--- .../oauth2/template/SaOAuth2Template.java | 99 ++++++++++--------- .../satoken/oauth2/template/SaOAuth2Util.java | 32 +++--- 20 files changed, 544 insertions(+), 237 deletions(-) create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2ConfirmViewFunction.java create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2DoLoginHandleFunction.java create mode 100644 sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2NotLoginViewFunction.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java index a28a1072..530e1fbd 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java @@ -29,7 +29,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Pattern; /** * Sa-Token 内部工具类 @@ -563,7 +562,7 @@ public class SaFoxUtil { * @return 字符串 */ public static String convertListToString(List list) { - if(list == null || list.size() == 0) { + if(list == null || list.isEmpty()) { return ""; } StringBuilder str = new StringBuilder(); diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java index 626381f3..468d9bcd 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java @@ -5,6 +5,8 @@ import cn.dev33.satoken.oauth2.data.model.SaClientModel; import cn.dev33.satoken.secure.SaSecureUtil; import org.springframework.stereotype.Component; +import java.util.Arrays; + /** * Sa-Token OAuth2:自定义数据加载器 * @@ -22,7 +24,7 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader { .setClientId("1001") .setClientSecret("aaaa-bbbb-cccc-dddd-eeee") .setAllowUrl("*") - .setContractScope("userinfo") + .setContractScopes(Arrays.asList("userinfo")) .setIsAutoMode(true); } return null; diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java index f83a61c9..03fbbf60 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java @@ -34,27 +34,25 @@ public class SaOAuth2ServerController { // Sa-OAuth2 定制化配置 @Autowired public void setSaOAuth2Config(SaOAuth2Config cfg) { - cfg. - // 未登录的视图 - setNotLoginView(()->{ - return new ModelAndView("login.html"); - }). + // 未登录的视图 + cfg.notLoginView = ()->{ + return new ModelAndView("login.html"); + }; // 登录处理函数 - setDoLoginHandle((name, pwd) -> { - if("sa".equals(name) && "123456".equals(pwd)) { - StpUtil.login(10001); - return SaResult.ok(); - } - return SaResult.error("账号名或密码错误"); - }). + cfg.doLoginHandle = (name, pwd) -> { + if("sa".equals(name) && "123456".equals(pwd)) { + StpUtil.login(10001); + return SaResult.ok(); + } + return SaResult.error("账号名或密码错误"); + }; // 授权确认视图 - setConfirmView((clientId, scope)->{ - Map map = new HashMap<>(); - map.put("clientId", clientId); - map.put("scope", scope); - return new ModelAndView("confirm.html", map); - }) - ; + cfg.confirmView = (clientId, scope)->{ + Map map = new HashMap<>(); + map.put("clientId", clientId); + map.put("scope", scope); + return new ModelAndView("confirm.html", map); + }; } // 全局异常拦截 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java index 70ebddcc..88b07412 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java @@ -16,6 +16,8 @@ package cn.dev33.satoken.oauth2; import cn.dev33.satoken.oauth2.config.SaOAuth2Config; +import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter; +import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverterDefaultImpl; import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader; import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoaderDefaultImpl; import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver; @@ -84,4 +86,22 @@ public class SaOAuth2Manager { SaOAuth2Manager.dataResolver = dataResolver; } + /** + * OAuth2 数据格式转换器 + */ + private static volatile SaOAuth2DataConverter dataConverter; + public static SaOAuth2DataConverter getDataConverter() { + if (dataConverter == null) { + synchronized (SaOAuth2Manager.class) { + if (dataConverter == null) { + setDataConverter(new SaOAuth2DataConverterDefaultImpl()); + } + } + } + return dataConverter; + } + public static void setDataConverter(SaOAuth2DataConverter dataConverter) { + SaOAuth2Manager.dataConverter = dataConverter; + } + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java index 4511f226..7478d63b 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java @@ -15,11 +15,12 @@ */ package cn.dev33.satoken.oauth2.config; +import cn.dev33.satoken.oauth2.function.SaOAuth2ConfirmViewFunction; +import cn.dev33.satoken.oauth2.function.SaOAuth2DoLoginHandleFunction; +import cn.dev33.satoken.oauth2.function.SaOAuth2NotLoginViewFunction; import cn.dev33.satoken.util.SaResult; import java.io.Serializable; -import java.util.function.BiFunction; -import java.util.function.Supplier; /** * Sa-Token-OAuth2 配置类 Model @@ -218,65 +219,17 @@ public class SaOAuth2Config implements Serializable { /** * OAuth-Server端:未登录时返回的View */ - public Supplier notLoginView = () -> "当前会话在OAuth-Server认证中心尚未登录"; + public SaOAuth2NotLoginViewFunction notLoginView = () -> "当前会话在OAuth-Server认证中心尚未登录"; /** * OAuth-Server端:确认授权时返回的View */ - public BiFunction confirmView = (clientId, scope) -> "本次操作需要用户授权"; + public SaOAuth2ConfirmViewFunction confirmView = (clientId, scopes) -> "本次操作需要用户授权"; /** * OAuth-Server端:登录函数 */ - public BiFunction doLoginHandle = (name, pwd) -> SaResult.error(); - - /** - * @param notLoginView OAuth-Server端:未登录时返回的View - * @return 对象自身 - */ - public SaOAuth2Config setNotLoginView(Supplier notLoginView) { - this.notLoginView = notLoginView; - return this; - } - - /** - * @return 函数 OAuth-Server端:未登录时返回的View - */ - public Supplier getNotLoginView() { - return notLoginView; - } - - /** - * @param confirmView OAuth-Server端:确认授权时返回的View - * @return 对象自身 - */ - public SaOAuth2Config setConfirmView(BiFunction confirmView) { - this.confirmView = confirmView; - return this; - } - - /** - * @return 函数 OAuth-Server端:确认授权时返回的View - */ - public BiFunction getConfirmView() { - return confirmView; - } - - /** - * @param doLoginHandle OAuth-Server端:登录函数 - * @return 对象自身 - */ - public SaOAuth2Config setDoLoginHandle(BiFunction doLoginHandle) { - this.doLoginHandle = doLoginHandle; - return this; - } - - /** - * @return 函数 OAuth-Server端:登录函数 - */ - public BiFunction getDoLoginHandle() { - return doLoginHandle; - } + public SaOAuth2DoLoginHandleFunction doLoginHandle = (name, pwd) -> SaResult.error(); @Override public String toString() { 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 new file mode 100644 index 00000000..9a9e953b --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverter.java @@ -0,0 +1,49 @@ +/* + * 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.data.convert; + +import java.util.List; + +/** + * Sa-Token OAuth2 数据格式转换器 + * + * @author click33 + * @since 1.39.0 + */ +public interface SaOAuth2DataConverter { + + /** + * 转换 scope 数据格式:String -> List + * @param scopeString / + * @return / + */ + List convertScopeStringToList(String scopeString); + + /** + * 转换 scope 数据格式:List -> String + * @param scopeList / + * @return / + */ + String convertScopeListToString(List scopeList); + + /** + * 转换 AllowUrl 数据格式:String -> List + * @param allowUrl / + * @return / + */ + List convertAllowUrlStringToList(String allowUrl); + +} 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 new file mode 100644 index 00000000..aa818ac2 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/convert/SaOAuth2DataConverterDefaultImpl.java @@ -0,0 +1,64 @@ +/* + * 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.data.convert; + +import cn.dev33.satoken.util.SaFoxUtil; + +import java.util.Collections; +import java.util.List; + +/** + * Sa-Token OAuth2 数据格式转换器,默认实现类 + * + * @author click33 + * @since 1.39.0 + */ +public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { + + /** + * 转换 scope 数据格式:String -> List + */ + @Override + public List convertScopeStringToList(String scopeString) { + if(SaFoxUtil.isEmpty(scopeString)) { + return Collections.emptyList(); + } + // 兼容以下三种分隔符:空格、逗号、%20 + scopeString = scopeString.replaceAll(" ", ","); + scopeString = scopeString.replaceAll("%20", ","); + return SaFoxUtil.convertStringToList(scopeString); + } + + /** + * 转换 scope 数据格式:List -> String + */ + @Override + public String convertScopeListToString(List scopeList) { + return SaFoxUtil.convertListToString(scopeList); + } + + /** + * 转换 AllowUrl 数据格式:String -> List + */ + @Override + public List convertAllowUrlStringToList(String allowUrl) { + if(SaFoxUtil.isEmpty(allowUrl)) { + return Collections.emptyList(); + } + return SaFoxUtil.convertStringToList(allowUrl); + } +} + diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/AccessTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/AccessTokenModel.java index 5ae94487..d16974f6 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/AccessTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/AccessTokenModel.java @@ -17,6 +17,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; /** @@ -67,31 +68,106 @@ public class AccessTokenModel implements Serializable { /** * 授权范围 */ - public String scope; + public List scopes; public AccessTokenModel() {} + /** * 构建一个 * @param accessToken accessToken * @param clientId 应用id - * @param scope 请求授权范围 + * @param scopes 请求授权范围 * @param loginId 对应的账号id */ - public AccessTokenModel(String accessToken, String clientId, Object loginId, String scope) { + public AccessTokenModel(String accessToken, String clientId, Object loginId, List scopes) { super(); this.accessToken = accessToken; this.clientId = clientId; this.loginId = loginId; - this.scope = scope; + this.scopes = scopes; } - + + + public String getAccessToken() { + return accessToken; + } + + public AccessTokenModel setAccessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + public String getRefreshToken() { + return refreshToken; + } + + public AccessTokenModel setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public long getExpiresTime() { + return expiresTime; + } + + public AccessTokenModel setExpiresTime(long expiresTime) { + this.expiresTime = expiresTime; + return this; + } + + public long getRefreshExpiresTime() { + return refreshExpiresTime; + } + + public AccessTokenModel setRefreshExpiresTime(long refreshExpiresTime) { + this.refreshExpiresTime = refreshExpiresTime; + return this; + } + + public String getClientId() { + return clientId; + } + + public AccessTokenModel setClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Object getLoginId() { + return loginId; + } + + public AccessTokenModel setLoginId(Object loginId) { + this.loginId = loginId; + return this; + } + + public String getOpenid() { + return openid; + } + + public AccessTokenModel setOpenid(String openid) { + this.openid = openid; + return this; + } + + public List getScopes() { + return scopes; + } + + public AccessTokenModel setScopes(List scopes) { + this.scopes = scopes; + return this; + } + @Override public String toString() { return "AccessTokenModel [accessToken=" + accessToken + ", refreshToken=" + refreshToken + ", accessTokenTimeout=" + expiresTime + ", refreshTokenTimeout=" + refreshExpiresTime - + ", clientId=" + clientId + ", scope=" + scope + ", openid=" + openid + "]"; + + ", clientId=" + clientId + ", scopes=" + scopes + ", openid=" + openid + "]"; } + // 追加只读属性 /** * 获取:此 Access-Token 的剩余有效期(秒) @@ -115,6 +191,7 @@ public class AccessTokenModel implements Serializable { * 将所有属性转换为下划线形式的Map * @return 属性转Map */ + @Deprecated public Map toLineMap() { Map map = new LinkedHashMap<>(); map.put("access_token", accessToken); @@ -122,7 +199,7 @@ public class AccessTokenModel implements Serializable { map.put("expires_in", getExpiresIn()); map.put("refresh_expires_in", getRefreshExpiresIn()); map.put("client_id", clientId); - map.put("scope", scope); + map.put("scopes", scopes); map.put("openid", openid); return map; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/ClientTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/ClientTokenModel.java index 012e7556..ab781719 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/ClientTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/ClientTokenModel.java @@ -17,6 +17,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; /** @@ -47,7 +48,7 @@ public class ClientTokenModel implements Serializable { /** * 授权范围 */ - public String scope; + public List scopes; public ClientTokenModel() {} @@ -55,19 +56,19 @@ public class ClientTokenModel implements Serializable { * 构建一个 * @param accessToken accessToken * @param clientId 应用id - * @param scope 请求授权范围 + * @param scopes 请求授权范围 */ - public ClientTokenModel(String accessToken, String clientId, String scope) { + public ClientTokenModel(String accessToken, String clientId, List scopes) { super(); this.clientToken = accessToken; this.clientId = clientId; - this.scope = scope; + this.scopes = scopes; } @Override public String toString() { return "ClientTokenModel [clientToken=" + clientToken + ", expiresTime=" + expiresTime + ", clientId=" - + clientId + ", scope=" + scope + "]"; + + clientId + ", scopes=" + scopes + "]"; } /** @@ -80,15 +81,16 @@ public class ClientTokenModel implements Serializable { } /** - * 将所有属性转换为下划线形式的Map - * @return 属性转Map + * 将所有属性转换为下划线形式的Map + * @return 属性转Map */ + @Deprecated public Map toLineMap() { Map map = new LinkedHashMap<>(); map.put("client_token", clientToken); map.put("expires_in", getExpiresIn()); map.put("client_id", clientId); - map.put("scope", scope); + map.put("scopes", scopes); return map; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/CodeModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/CodeModel.java index 1e0a4918..57653f27 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/CodeModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/CodeModel.java @@ -16,6 +16,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; +import java.util.List; /** * Model: 授权码 @@ -40,7 +41,7 @@ public class CodeModel implements Serializable { /** * 授权范围 */ - public String scope; + public List scopes; /** * 对应账号id @@ -62,93 +63,67 @@ public class CodeModel implements Serializable { * 构建一个 * @param code 授权码 * @param clientId 应用id - * @param scope 请求授权范围 + * @param scopes 请求授权范围 * @param loginId 对应的账号id * @param redirectUri 重定向地址 */ - public CodeModel(String code, String clientId, String scope, Object loginId, String redirectUri) { + public CodeModel(String code, String clientId, List scopes, Object loginId, String redirectUri) { super(); this.code = code; this.clientId = clientId; - this.scope = scope; + this.scopes = scopes; this.loginId = loginId; this.redirectUri = redirectUri; } - - /** - * @return code - */ public String getCode() { return code; } - /** - * @param code 要设置的 code - */ - public void setCode(String code) { + public CodeModel setCode(String code) { this.code = code; + return this; } - /** - * @return clientId - */ public String getClientId() { return clientId; } - /** - * @param clientId 要设置的 clientId - */ - public void setClientId(String clientId) { + public CodeModel setClientId(String clientId) { this.clientId = clientId; + return this; } - /** - * @return scope - */ - public String getScope() { - return scope; + public List getScopes() { + return scopes; } - /** - * @param scope 要设置的 scope - */ - public void setScope(String scope) { - this.scope = scope; + public CodeModel setScopes(List scopes) { + this.scopes = scopes; + return this; } - /** - * @return loginId - */ public Object getLoginId() { return loginId; } - /** - * @param loginId 要设置的 loginId - */ - public void setLoginId(Object loginId) { + public CodeModel setLoginId(Object loginId) { this.loginId = loginId; + return this; } - - /** - * @return redirectUri - */ + public String getRedirectUri() { return redirectUri; } - - /** - * @param redirectUri 要设置的 redirectUri - */ - public void setRedirectUri(String redirectUri) { + + public CodeModel setRedirectUri(String redirectUri) { this.redirectUri = redirectUri; + return this; } - + @Override public String toString() { - return "CodeModel [code=" + code + ", clientId=" + clientId + ", scope=" + scope + ", loginId=" + loginId + return "CodeModel [code=" + code + ", clientId=" + clientId + ", scopes=" + scopes + ", loginId=" + loginId + ", redirectUri=" + redirectUri + "]"; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RefreshTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RefreshTokenModel.java index ae60d9f2..7f036358 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RefreshTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RefreshTokenModel.java @@ -16,6 +16,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; +import java.util.List; /** * Model: Refresh-Token @@ -45,7 +46,7 @@ public class RefreshTokenModel implements Serializable { /** * 授权范围 */ - public String scope; + public List scopes; /** * 对应账号id @@ -57,10 +58,64 @@ public class RefreshTokenModel implements Serializable { */ public String openid; + public String getRefreshToken() { + return refreshToken; + } + + public RefreshTokenModel setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public long getExpiresTime() { + return expiresTime; + } + + public RefreshTokenModel setExpiresTime(long expiresTime) { + this.expiresTime = expiresTime; + return this; + } + + public String getClientId() { + return clientId; + } + + public RefreshTokenModel setClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public List getScopes() { + return scopes; + } + + public RefreshTokenModel setScopes(List scopes) { + this.scopes = scopes; + return this; + } + + public Object getLoginId() { + return loginId; + } + + public RefreshTokenModel setLoginId(Object loginId) { + this.loginId = loginId; + return this; + } + + public String getOpenid() { + return openid; + } + + public RefreshTokenModel setOpenid(String openid) { + this.openid = openid; + return this; + } + @Override public String toString() { return "RefreshTokenModel [refreshToken=" + refreshToken + ", expiresTime=" + expiresTime - + ", clientId=" + clientId + ", scope=" + scope + ", loginId=" + loginId + ", openid=" + openid + "]"; + + ", clientId=" + clientId + ", scopes=" + scopes + ", loginId=" + loginId + ", openid=" + openid + "]"; } /** diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RequestAuthModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RequestAuthModel.java index 3ee815ec..c6d6d3d4 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RequestAuthModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/RequestAuthModel.java @@ -16,6 +16,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; +import java.util.List; import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; @@ -39,7 +40,7 @@ public class RequestAuthModel implements Serializable { /** * 授权范围 */ - public String scope; + public List scopes; /** * 对应的账号id @@ -79,18 +80,18 @@ public class RequestAuthModel implements Serializable { } /** - * @return scope + * @return scopes */ - public String getScope() { - return scope; + public List getScopes() { + return scopes; } /** - * @param scope 要设置的 scope + * @param scopes 要设置的 scopes * @return 对象自身 */ - public RequestAuthModel setScope(String scope) { - this.scope = scope; + public RequestAuthModel setScopes(List scopes) { + this.scopes = scopes; return this; } @@ -166,7 +167,7 @@ public class RequestAuthModel implements Serializable { if(SaFoxUtil.isEmpty(clientId)) { throw new SaOAuth2Exception("client_id 不可为空").setCode(SaOAuth2ErrorCode.CODE_30101); } - if(SaFoxUtil.isEmpty(scope)) { + if(SaFoxUtil.isEmpty(scopes)) { throw new SaOAuth2Exception("scope 不可为空").setCode(SaOAuth2ErrorCode.CODE_30102); } if(SaFoxUtil.isEmpty(redirectUri)) { diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/SaClientModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/SaClientModel.java index f7023eca..8c944ba4 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/SaClientModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/SaClientModel.java @@ -15,11 +15,12 @@ */ package cn.dev33.satoken.oauth2.data.model; -import java.io.Serializable; - import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.config.SaOAuth2Config; +import java.io.Serializable; +import java.util.List; + /** * Client应用信息 Model * @@ -43,7 +44,7 @@ public class SaClientModel implements Serializable { /** * 应用签约的所有权限, 多个用逗号隔开 */ - public String contractScope; + public List contractScopes; /** * 应用允许授权的所有URL, 多个用逗号隔开 @@ -93,11 +94,11 @@ public class SaClientModel implements Serializable { this.clientTokenTimeout = config.getClientTokenTimeout(); this.pastClientTokenTimeout = config.getPastClientTokenTimeout(); } - public SaClientModel(String clientId, String clientSecret, String contractScope, String allowUrl) { + public SaClientModel(String clientId, String clientSecret, List contractScopes, String allowUrl) { super(); this.clientId = clientId; this.clientSecret = clientSecret; - this.contractScope = contractScope; + this.contractScopes = contractScopes; this.allowUrl = allowUrl; } @@ -134,18 +135,18 @@ public class SaClientModel implements Serializable { } /** - * @return 应用签约的所有权限, 多个用逗号隔开 + * @return 应用签约的所有权限 */ - public String getContractScope() { - return contractScope; + public List getContractScopes() { + return contractScopes; } /** - * @param contractScope 应用签约的所有权限, 多个用逗号隔开 + * @param contractScopes 应用签约的所有权限, 多个用逗号隔开 * @return 对象自身 */ - public SaClientModel setContractScope(String contractScope) { - this.contractScope = contractScope; + public SaClientModel setContractScopes(List contractScopes) { + this.contractScopes = contractScopes; return this; } @@ -329,8 +330,8 @@ public class SaClientModel implements Serializable { // @Override public String toString() { - return "SaClientModel [clientId=" + clientId + ", clientSecret=" + clientSecret + ", contractScope=" - + contractScope + ", allowUrl=" + allowUrl + ", isCode=" + isCode + ", isImplicit=" + isImplicit + return "SaClientModel [clientId=" + clientId + ", clientSecret=" + clientSecret + ", contractScopes=" + + contractScopes + ", allowUrl=" + allowUrl + ", isCode=" + isCode + ", isImplicit=" + isImplicit + ", isPassword=" + isPassword + ", isClient=" + isClient + ", isAutoMode=" + isAutoMode + ", isNewRefresh=" + isNewRefresh + ", accessTokenTimeout=" + accessTokenTimeout + ", refreshTokenTimeout=" + refreshTokenTimeout + ", clientTokenTimeout=" + clientTokenTimeout 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 b4df5ee1..1d437eb7 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 @@ -15,6 +15,7 @@ */ package cn.dev33.satoken.oauth2.data.resolver; +import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.TokenType; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; @@ -44,7 +45,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { map.put("expires_in", at.getExpiresIn()); map.put("refresh_expires_in", at.getRefreshExpiresIn()); map.put("client_id", at.clientId); - map.put("scope", at.scope); + map.put("scope", SaOAuth2Manager.getDataConverter().convertScopeListToString(at.scopes)); map.put("openid", at.openid); return SaResult.ok().setMap(map); } @@ -59,7 +60,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议 map.put("expires_in", ct.getExpiresIn()); map.put("client_id", ct.clientId); - map.put("scope", ct.scope); + map.put("scope", SaOAuth2Manager.getDataConverter().convertScopeListToString(ct.scopes)); return SaResult.ok().setMap(map); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2ConfirmViewFunction.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2ConfirmViewFunction.java new file mode 100644 index 00000000..6f38bcec --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2ConfirmViewFunction.java @@ -0,0 +1,34 @@ +/* + * 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.function; + + +import java.util.List; +import java.util.function.BiFunction; + +/** + * 函数式接口:OAuth-Server端 确认授权时返回的View + * + *

参数:无

+ *

返回:view 视图

+ * + * @author click33 + * @since 1.39.0 + */ +@FunctionalInterface +public interface SaOAuth2ConfirmViewFunction extends BiFunction, Object> { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2DoLoginHandleFunction.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2DoLoginHandleFunction.java new file mode 100644 index 00000000..af699821 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2DoLoginHandleFunction.java @@ -0,0 +1,33 @@ +/* + * 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.function; + + +import java.util.function.BiFunction; + +/** + * 函数式接口:登录函数 + * + *

参数:name, pwd

+ *

返回:认证返回结果

+ * + * @author click33 + * @since 1.39.0 + */ +@FunctionalInterface +public interface SaOAuth2DoLoginHandleFunction extends BiFunction { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2NotLoginViewFunction.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2NotLoginViewFunction.java new file mode 100644 index 00000000..baf29c89 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/SaOAuth2NotLoginViewFunction.java @@ -0,0 +1,33 @@ +/* + * 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.function; + + +import java.util.function.Supplier; + +/** + * 函数式接口:OAuth-Server端 未登录时返回的View + * + *

参数:clientId, scope

+ *

返回:view 视图

+ * + * @author click33 + * @since 1.39.0 + */ +@FunctionalInterface +public interface SaOAuth2NotLoginViewFunction extends Supplier { + +} \ No newline at end of file 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 af51d95f..f732d813 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 @@ -35,6 +35,8 @@ import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; +import java.util.List; + /** * Sa-Token OAuth2 请求处理器 * @@ -142,7 +144,7 @@ public class SaOAuth2ServerProcessor { // 1、如果尚未登录, 则先去登录 if( ! getStpLogic().isLogin()) { - return cfg.getNotLoginView().get(); + return cfg.notLoginView.get(); } // 2、构建请求Model @@ -152,12 +154,12 @@ public class SaOAuth2ServerProcessor { oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri); // 4、校验:此次申请的Scope,该Client是否已经签约 - oauth2Template.checkContract(ra.clientId, ra.scope); + oauth2Template.checkContract(ra.clientId, ra.scopes); // 5、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 - boolean isGrant = oauth2Template.isGrant(ra.loginId, ra.clientId, ra.scope); + boolean isGrant = oauth2Template.isGrant(ra.loginId, ra.clientId, ra.scopes); if( ! isGrant) { - return cfg.getConfirmView().apply(ra.clientId, ra.scope); + return cfg.confirmView.apply(ra.clientId, ra.scopes); } // 6、判断授权类型 @@ -275,7 +277,7 @@ public class SaOAuth2ServerProcessor { SaRequest req = SaHolder.getRequest(); SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); - return cfg.getDoLoginHandle().apply(req.getParamNotNull(Param.name), req.getParamNotNull(Param.pwd)); + return cfg.doLoginHandle.apply(req.getParamNotNull(Param.name), req.getParamNotNull(Param.pwd)); } /** @@ -288,8 +290,9 @@ public class SaOAuth2ServerProcessor { String clientId = req.getParamNotNull(Param.client_id); String scope = req.getParamNotNull(Param.scope); + List scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope); Object loginId = getStpLogic().getLoginId(); - oauth2Template.saveGrantScope(clientId, loginId, scope); + oauth2Template.saveGrantScope(clientId, loginId, scopes); return SaResult.ok(); } @@ -308,15 +311,16 @@ public class SaOAuth2ServerProcessor { String clientId = req.getParamNotNull(Param.client_id); String clientSecret = req.getParamNotNull(Param.client_secret); String scope = req.getParam(Param.scope, ""); + List scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope); // 2、校验 ClientScope 和 scope - oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scope); + oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes); // 3、防止因前端误传token造成逻辑干扰 // SaHolder.getStorage().set(getStpLogic().stpLogic.splicingKeyJustCreatedSave(), "no-token"); // 3、调用API 开始登录,如果没能成功登录,则直接退出 - Object retObj = cfg.getDoLoginHandle().apply(username, password); + Object retObj = cfg.doLoginHandle.apply(username, password); if( ! getStpLogic().isLogin()) { return retObj; } @@ -325,7 +329,7 @@ public class SaOAuth2ServerProcessor { RequestAuthModel ra = new RequestAuthModel(); ra.clientId = clientId; ra.loginId = getStpLogic().getLoginId(); - ra.scope = scope; + ra.scopes = scopes; // 5、生成 Access-Token AccessTokenModel at = oauth2Template.generateAccessToken(ra, true); @@ -345,16 +349,17 @@ public class SaOAuth2ServerProcessor { // 获取参数 String clientId = req.getParamNotNull(Param.client_id); String clientSecret = req.getParamNotNull(Param.client_secret); - String scope = req.getParam(Param.scope); + String scope = req.getParam(Param.scope, ""); + List scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope); //校验 ClientScope - oauth2Template.checkContract(clientId, scope); + oauth2Template.checkContract(clientId, scopes); // 校验 ClientSecret oauth2Template.checkClientSecret(clientId, clientSecret); // 生成 - ClientTokenModel ct = oauth2Template.generateClientToken(clientId, scope); + ClientTokenModel ct = oauth2Template.generateClientToken(clientId, scopes); // 返回 return SaOAuth2Manager.getDataResolver().buildClientTokenReturnValue(ct); 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 9d26c40d..8baff752 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 @@ -25,6 +25,7 @@ import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; +import java.util.HashSet; import java.util.List; /** @@ -107,7 +108,7 @@ public class SaOAuth2Template { return; } AccessTokenModel at = checkAccessToken(accessToken); - List scopeList = SaFoxUtil.convertStringToList(at.scope); + List scopeList = at.scopes; for (String scope : scopes) { SaOAuth2Exception.throwBy( ! scopeList.contains(scope), "该 Access-Token 不具备 Scope:" + scope, SaOAuth2ErrorCode.CODE_30108); } @@ -122,7 +123,7 @@ public class SaOAuth2Template { return; } ClientTokenModel ct = checkClientToken(clientToken); - List scopeList = SaFoxUtil.convertStringToList(ct.scope); + List scopeList = ct.scopes; for (String scope : scopes) { SaOAuth2Exception.throwBy( ! scopeList.contains(scope), "该 Client-Token 不具备 Scope:" + scope, SaOAuth2ErrorCode.CODE_30109); } @@ -141,7 +142,9 @@ public class SaOAuth2Template { ra.responseType = req.getParamNotNull(Param.response_type); ra.redirectUri = req.getParamNotNull(Param.redirect_uri); ra.state = req.getParam(Param.state); - ra.scope = req.getParam(Param.scope, ""); + // 数据解析 + String scope = req.getParam(Param.scope, ""); + ra.scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope); ra.loginId = loginId; return ra; } @@ -156,8 +159,8 @@ public class SaOAuth2Template { deleteCode(getCodeValue(ra.clientId, ra.loginId)); // 生成新Code - String code = randomCode(ra.clientId, ra.loginId, ra.scope); - CodeModel cm = new CodeModel(code, ra.clientId, ra.scope, ra.loginId, ra.redirectUri); + String code = randomCode(ra.clientId, ra.loginId, ra.scopes); + CodeModel cm = new CodeModel(code, ra.clientId, ra.scopes, ra.loginId, ra.redirectUri); // 保存新Code saveCode(cm); @@ -250,8 +253,8 @@ public class SaOAuth2Template { } // 2、生成 新Access-Token - String newAtValue = randomAccessToken(ra.clientId, ra.loginId, ra.scope); - AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scope); + String newAtValue = randomAccessToken(ra.clientId, ra.loginId, ra.scopes); + AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scopes); at.openid = getOpenid(ra.clientId, ra.loginId); at.expiresTime = System.currentTimeMillis() + (checkClientModel(ra.clientId).getAccessTokenTimeout() * 1000); @@ -272,10 +275,10 @@ public class SaOAuth2Template { /** * 构建Model:Client-Token * @param clientId 应用id - * @param scope 授权范围 + * @param scopes 授权范围 * @return Client-Token Model */ - public ClientTokenModel generateClientToken(String clientId, String scope) { + public ClientTokenModel generateClientToken(String clientId, List scopes) { // 1、删掉旧 Past-Token deleteClientToken(getPastTokenValue(clientId)); @@ -291,7 +294,7 @@ public class SaOAuth2Template { } // 3、生成新Client-Token - ClientTokenModel ct = new ClientTokenModel(randomClientToken(clientId, scope), clientId, scope); + ClientTokenModel ct = new ClientTokenModel(randomClientToken(clientId, scopes), clientId, scopes); ct.expiresTime = System.currentTimeMillis() + (cm.getClientTokenTimeout() * 1000); // 3、保存新Client-Token @@ -356,23 +359,21 @@ public class SaOAuth2Template { * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope * @param loginId 账号id * @param clientId 应用id - * @param scope 权限 + * @param scopes 权限 * @return 是否已经授权 */ - public boolean isGrant(Object loginId, String clientId, String scope) { - List grantScopeList = SaFoxUtil.convertStringToList(getGrantScope(clientId, loginId)); - List scopeList = SaFoxUtil.convertStringToList(scope); - return scopeList.size() == 0 || grantScopeList.containsAll(scopeList); + public boolean isGrant(Object loginId, String clientId, List scopes) { + List grantScopeList = getGrantScope(clientId, loginId); + return scopes.isEmpty() || new HashSet<>(grantScopeList).containsAll(scopes); } /** * 校验:该Client是否签约了指定的Scope * @param clientId 应用id - * @param scope 权限(多个用逗号隔开) + * @param scopes 权限(多个用逗号隔开) */ - public void checkContract(String clientId, String scope) { - List clientScopeList = SaFoxUtil.convertStringToList(checkClientModel(clientId).contractScope); - List scopelist = SaFoxUtil.convertStringToList(scope); - if( ! clientScopeList.containsAll(scopelist)) { + public void checkContract(String clientId, List scopes) { + List clientScopeList = checkClientModel(clientId).contractScopes; + if( ! new HashSet<>(clientScopeList).containsAll(scopes)) { throw new SaOAuth2Exception("请求的Scope暂未签约").setCode(SaOAuth2ErrorCode.CODE_30112); } } @@ -394,7 +395,8 @@ public class SaOAuth2Template { } // 3、是否在[允许地址列表]之中 - List allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl); + SaClientModel clientModel = checkClientModel(clientId); + List allowList = SaOAuth2Manager.getDataConverter().convertAllowUrlStringToList(clientModel.allowUrl); if( ! SaStrategy.instance.hasElement.apply(allowList, url)) { throw new SaOAuth2Exception("非法redirect_url:" + url).setCode(SaOAuth2ErrorCode.CODE_30114); } @@ -415,16 +417,15 @@ public class SaOAuth2Template { * 校验:clientId 与 clientSecret 是否正确,并且是否签约了指定 scopes * @param clientId 应用id * @param clientSecret 秘钥 - * @param scopes 权限(多个用逗号隔开) + * @param scopes 权限 * @return SaClientModel对象 */ - public SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, String scopes) { + public SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, List scopes) { // 先校验 clientSecret SaClientModel cm = checkClientSecret(clientId, clientSecret); // 再校验 是否签约 - List clientScopeList = SaFoxUtil.convertStringToList(cm.contractScope); - List scopelist = SaFoxUtil.convertStringToList(scopes); - if( ! clientScopeList.containsAll(scopelist)) { + List clientScopeList = cm.contractScopes; + if( ! new HashSet<>(clientScopeList).containsAll(scopes)) { throw new SaOAuth2Exception("请求的Scope暂未签约").setCode(SaOAuth2ErrorCode.CODE_30116); } // 返回数据 @@ -504,11 +505,11 @@ public class SaOAuth2Template { */ public AccessTokenModel convertCodeToAccessToken(CodeModel cm) { AccessTokenModel at = new AccessTokenModel(); - at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scope); + at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scopes); // at.refreshToken = randomRefreshToken(cm.clientId, cm.loginId, cm.scope); at.clientId = cm.clientId; at.loginId = cm.loginId; - at.scope = cm.scope; + at.scopes = cm.scopes; at.openid = getOpenid(cm.clientId, cm.loginId); at.expiresTime = System.currentTimeMillis() + (checkClientModel(cm.clientId).getAccessTokenTimeout() * 1000); // at.refreshExpiresTime = System.currentTimeMillis() + (checkClientModel(cm.clientId).getRefreshTokenTimeout() * 1000); @@ -521,10 +522,10 @@ public class SaOAuth2Template { */ public RefreshTokenModel convertAccessTokenToRefreshToken(AccessTokenModel at) { RefreshTokenModel rt = new RefreshTokenModel(); - rt.refreshToken = randomRefreshToken(at.clientId, at.loginId, at.scope); + rt.refreshToken = randomRefreshToken(at.clientId, at.loginId, at.scopes); rt.clientId = at.clientId; rt.loginId = at.loginId; - rt.scope = at.scope; + rt.scopes = at.scopes; rt.openid = at.openid; rt.expiresTime = System.currentTimeMillis() + (checkClientModel(at.clientId).getRefreshTokenTimeout() * 1000); // 改变at属性 @@ -539,11 +540,11 @@ public class SaOAuth2Template { */ public AccessTokenModel convertRefreshTokenToAccessToken(RefreshTokenModel rt) { AccessTokenModel at = new AccessTokenModel(); - at.accessToken = randomAccessToken(rt.clientId, rt.loginId, rt.scope); + at.accessToken = randomAccessToken(rt.clientId, rt.loginId, rt.scopes); at.refreshToken = rt.refreshToken; at.clientId = rt.clientId; at.loginId = rt.loginId; - at.scope = rt.scope; + at.scopes = rt.scopes; at.openid = rt.openid; at.expiresTime = System.currentTimeMillis() + (checkClientModel(rt.clientId).getAccessTokenTimeout() * 1000); at.refreshExpiresTime = rt.expiresTime; @@ -556,10 +557,10 @@ public class SaOAuth2Template { */ public RefreshTokenModel convertRefreshTokenToRefreshToken(RefreshTokenModel rt) { RefreshTokenModel newRt = new RefreshTokenModel(); - newRt.refreshToken = randomRefreshToken(rt.clientId, rt.loginId, rt.scope); + newRt.refreshToken = randomRefreshToken(rt.clientId, rt.loginId, rt.scopes); newRt.expiresTime = System.currentTimeMillis() + (checkClientModel(rt.clientId).getRefreshTokenTimeout() * 1000); newRt.clientId = rt.clientId; - newRt.scope = rt.scope; + newRt.scopes = rt.scopes; newRt.loginId = rt.loginId; newRt.openid = rt.openid; return newRt; @@ -665,12 +666,13 @@ public class SaOAuth2Template { * 持久化:用户授权记录 * @param clientId 应用id * @param loginId 账号id - * @param scope 权限列表(多个逗号隔开) + * @param scopes 权限列表 */ - public void saveGrantScope(String clientId, Object loginId, String scope) { - if( ! SaFoxUtil.isEmpty(scope)) { + public void saveGrantScope(String clientId, Object loginId, List scopes) { + if( ! SaFoxUtil.isEmpty(scopes)) { long ttl = checkClientModel(clientId).getAccessTokenTimeout(); - SaManager.getSaTokenDao().set(splicingGrantScopeKey(clientId, loginId), scope, ttl); + String value = SaOAuth2Manager.getDataConverter().convertScopeListToString(scopes); + SaManager.getSaTokenDao().set(splicingGrantScopeKey(clientId, loginId), value, ttl); } } @@ -768,8 +770,9 @@ public class SaOAuth2Template { * @param loginId 账号id * @return 权限 */ - public String getGrantScope(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingGrantScopeKey(clientId, loginId)); + public List getGrantScope(String clientId, Object loginId) { + String value = SaManager.getSaTokenDao().get(splicingGrantScopeKey(clientId, loginId)); + return SaOAuth2Manager.getDataConverter().convertScopeStringToList(value); } // ------------------- delete数据 @@ -861,39 +864,39 @@ public class SaOAuth2Template { * 随机一个 Code * @param clientId 应用id * @param loginId 账号id - * @param scope 权限 + * @param scopes 权限 * @return Code */ - public String randomCode(String clientId, Object loginId, String scope) { + public String randomCode(String clientId, Object loginId, List scopes) { return SaFoxUtil.getRandomString(60); } /** * 随机一个 Access-Token * @param clientId 应用id * @param loginId 账号id - * @param scope 权限 + * @param scopes 权限 * @return Access-Token */ - public String randomAccessToken(String clientId, Object loginId, String scope) { + public String randomAccessToken(String clientId, Object loginId, List scopes) { return SaFoxUtil.getRandomString(60); } /** * 随机一个 Refresh-Token * @param clientId 应用id * @param loginId 账号id - * @param scope 权限 + * @param scopes 权限 * @return Refresh-Token */ - public String randomRefreshToken(String clientId, Object loginId, String scope) { + public String randomRefreshToken(String clientId, Object loginId, List scopes) { return SaFoxUtil.getRandomString(60); } /** * 随机一个 Client-Token * @param clientId 应用id - * @param scope 权限 + * @param scopes 权限 * @return Client-Token */ - public String randomClientToken(String clientId, String scope) { + public String randomClientToken(String clientId, List scopes) { return SaFoxUtil.getRandomString(60); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Util.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Util.java index d40000e0..d7886714 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Util.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Util.java @@ -19,6 +19,8 @@ import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.oauth2.data.model.*; import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor; +import java.util.List; + /** * Sa-Token-OAuth2 模块 工具类 * @@ -135,11 +137,11 @@ public class SaOAuth2Util { /** * 构建Model:Client-Token * @param clientId 应用id - * @param scope 授权范围 + * @param scopes 授权范围 * @return Client-Token Model */ - public static ClientTokenModel generateClientToken(String clientId, String scope) { - return SaOAuth2ServerProcessor.instance.oauth2Template.generateClientToken(clientId, scope); + public static ClientTokenModel generateClientToken(String clientId, List scopes) { + return SaOAuth2ServerProcessor.instance.oauth2Template.generateClientToken(clientId, scopes); } /** @@ -178,20 +180,20 @@ public class SaOAuth2Util { * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope * @param loginId 账号id * @param clientId 应用id - * @param scope 权限 + * @param scopes 权限 * @return 是否已经授权 */ - public static boolean isGrant(Object loginId, String clientId, String scope) { - return SaOAuth2ServerProcessor.instance.oauth2Template.isGrant(loginId, clientId, scope); + public static boolean isGrant(Object loginId, String clientId, List scopes) { + return SaOAuth2ServerProcessor.instance.oauth2Template.isGrant(loginId, clientId, scopes); } /** * 校验:该Client是否签约了指定的Scope * @param clientId 应用id - * @param scope 权限(多个用逗号隔开) + * @param scopes 权限(多个用逗号隔开) */ - public static void checkContract(String clientId, String scope) { - SaOAuth2ServerProcessor.instance.oauth2Template.checkContract(clientId, scope); + public static void checkContract(String clientId, List scopes) { + SaOAuth2ServerProcessor.instance.oauth2Template.checkContract(clientId, scopes); } /** @@ -217,10 +219,10 @@ public class SaOAuth2Util { * 校验:clientId 与 clientSecret 是否正确,并且是否签约了指定 scopes * @param clientId 应用id * @param clientSecret 秘钥 - * @param scopes 权限(多个用逗号隔开) + * @param scopes 权限 * @return SaClientModel对象 */ - public static SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, String scopes) { + public static SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, List scopes) { return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes); } @@ -264,10 +266,10 @@ public class SaOAuth2Util { * 持久化:用户授权记录 * @param clientId 应用id * @param loginId 账号id - * @param scope 权限列表(多个逗号隔开) + * @param scopes 权限列表 */ - public static void saveGrantScope(String clientId, Object loginId, String scope) { - SaOAuth2ServerProcessor.instance.oauth2Template.saveGrantScope(clientId, loginId, scope); + public static void saveGrantScope(String clientId, Object loginId, List scopes) { + SaOAuth2ServerProcessor.instance.oauth2Template.saveGrantScope(clientId, loginId, scopes); } @@ -315,7 +317,7 @@ public class SaOAuth2Util { * @param loginId 账号id * @return 权限 */ - public static String getGrantScope(String clientId, Object loginId) { + public static List getGrantScope(String clientId, Object loginId) { return SaOAuth2ServerProcessor.instance.oauth2Template.getGrantScope(clientId, loginId); }