From 5b3a64b9fc6845b191c337598c4fc8d5d4a68d74 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 14 May 2025 03:30:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor(oauth2):=20=E4=BC=98=E5=8C=96=E8=B0=83?= =?UTF-8?q?=E6=95=B4=20sa-token-oauth2=20=E6=A8=A1=E5=9D=97=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84=E5=8F=8A=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/dev33/satoken/temp/SaTempTemplate.java | 70 +--- .../cn/dev33/satoken/util/SaTtlMethods.java | 119 ++++++ .../oauth2/h5/SaOAuth2ServerH5Controller.java | 12 +- .../java/com/pj/test/Test2Controller.java | 25 ++ .../oauth2/config/SaOAuth2OidcConfig.java | 27 +- .../oauth2/config/SaOAuth2ServerConfig.java | 186 +++++----- .../dev33/satoken/oauth2/dao/SaOAuth2Dao.java | 349 +++++++++--------- .../data/convert/SaOAuth2DataConverter.java | 46 ++- .../SaOAuth2DataConverterDefaultImpl.java | 100 +++-- .../data/generate/SaOAuth2DataGenerate.java | 14 +- .../SaOAuth2DataGenerateDefaultImpl.java | 137 +++---- .../data/loader/SaOAuth2DataLoader.java | 2 +- .../oauth2/data/model/AccessTokenModel.java | 41 +- .../oauth2/data/model/ClientTokenModel.java | 23 +- .../satoken/oauth2/data/model/CodeModel.java | 4 +- .../oauth2/data/model/RefreshTokenModel.java | 23 +- .../data/model/loader/SaClientModel.java | 13 +- .../model/request/ClientIdAndSecretModel.java | 1 + .../data/model/request/RequestAuthModel.java | 22 +- .../data/resolver/SaOAuth2DataResolver.java | 1 - .../SaOAuth2DataResolverDefaultImpl.java | 6 +- .../AuthorizationCodeGrantTypeHandler.java | 7 +- .../handler/PasswordGrantTypeHandler.java | 6 +- .../SaOAuth2GrantTypeHandlerInterface.java | 2 +- .../processor/SaOAuth2ServerProcessor.java | 14 +- .../satoken/oauth2/scope/CommonScope.java | 2 +- .../scope/handler/OidcScopeHandler.java | 6 +- .../SaOAuth2ScopeHandlerInterface.java | 2 +- .../oauth2/strategy/SaOAuth2Strategy.java | 31 +- .../oauth2/template/SaOAuth2Template.java | 29 +- .../satoken/oauth2/template/SaOAuth2Util.java | 9 + 31 files changed, 765 insertions(+), 564 deletions(-) create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/util/SaTtlMethods.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/temp/SaTempTemplate.java b/sa-token-core/src/main/java/cn/dev33/satoken/temp/SaTempTemplate.java index 1af75553..d80f681c 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/temp/SaTempTemplate.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/temp/SaTempTemplate.java @@ -22,6 +22,7 @@ import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.raw.SaRawSessionDelegator; import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaTtlMethods; import java.util.*; @@ -35,7 +36,7 @@ import java.util.*; * @author click33 * @since 1.42.0 */ -public class SaTempTemplate { +public class SaTempTemplate implements SaTtlMethods { /** *默认命名空间 @@ -256,14 +257,14 @@ public class SaTempTemplate { if(session == null) { session = rawSessionDelegator.getSessionById(value, false); if(session == null) { - return newTempTokenMap(); + return newTokenIndexMap(); } } // 重新整理索引列表 - Map tempTokenNewList = newTempTokenMap(); + Map tempTokenNewList = newTokenIndexMap(); ArrayList tempTokenTtlList = new ArrayList<>(); - Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap); + Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTokenIndexMap); for (Map.Entry entry : tempTokenMap.entrySet()) { long ttl = expireTimeToTtl(entry.getValue()); if(ttl != SaTokenDao.NOT_VALUE_EXPIRE) { @@ -306,7 +307,7 @@ public class SaTempTemplate { * @param timeout / */ protected void addTempTokenIndex(SaSession session, String token, long timeout) { - Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap); + Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTokenIndexMap); if(! tempTokenMap.containsKey(token)) { tempTokenMap.put(token, ttlToExpireTime(timeout)); session.set(TEMP_TOKEN_MAP, tempTokenMap); @@ -319,21 +320,13 @@ public class SaTempTemplate { * @param token / */ protected void deleteTempTokenIndex(SaSession session, String token) { - Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTempTokenMap); + Map tempTokenMap = session.get(TEMP_TOKEN_MAP, this::newTokenIndexMap); if(tempTokenMap.containsKey(token)) { tempTokenMap.remove(token); session.set(TEMP_TOKEN_MAP, tempTokenMap); } } - /** - * 获取一个新的 TempTokenMap 集合 - * @return / - */ - protected Map newTempTokenMap() { - return new LinkedHashMap<>(); - } - // -------- 元操作 @@ -364,55 +357,6 @@ public class SaTempTemplate { } } - /** - * 过期时间转 ttl (秒) 获取最大 ttl 值 - * @param tempTokenTtlList / - * @return / - */ - protected long getMaxTtl(ArrayList tempTokenTtlList) { - long maxTtl = 0; - for (long ttl : tempTokenTtlList) { - if(ttl == SaTokenDao.NEVER_EXPIRE) { - maxTtl = SaTokenDao.NEVER_EXPIRE; - break; - } - if(ttl > maxTtl) { - maxTtl = ttl; - } - } - return maxTtl; - } - - /** - * 过期时间转 ttl (秒) - * @param expireTime / - * @return / - */ - protected long expireTimeToTtl(long expireTime) { - if(expireTime == SaTokenDao.NEVER_EXPIRE) { - return SaTokenDao.NEVER_EXPIRE; - } - if(expireTime == SaTokenDao.NOT_VALUE_EXPIRE) { - return SaTokenDao.NOT_VALUE_EXPIRE; - } - return (expireTime - System.currentTimeMillis()) / 1000; - } - - /** - * ttl (秒) 转 过期时间 - * @param ttl / - * @return / - */ - protected long ttlToExpireTime(long ttl) { - if(ttl == SaTokenDao.NEVER_EXPIRE) { - return SaTokenDao.NEVER_EXPIRE; - } - if(ttl == SaTokenDao.NOT_VALUE_EXPIRE) { - return SaTokenDao.NOT_VALUE_EXPIRE; - } - return ttl * 1000 + System.currentTimeMillis(); - } - /** * 获取:在存储临时 token 数据时,应该使用的 key * @param token token值 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTtlMethods.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTtlMethods.java new file mode 100644 index 00000000..e842ab76 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTtlMethods.java @@ -0,0 +1,119 @@ +/* + * 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.util; + +import cn.dev33.satoken.dao.SaTokenDao; + +import java.util.*; + +/** + * TTL 操作工具方法 + * + * @author click33 + * @since 1.43.0 + */ +public interface SaTtlMethods { + + /** + * 获取一个新的 Token 集合 + * @return / + */ + default List newTokenValueList() { + return new ArrayList<>(); + } + + /** + * 获取一个新的 TokenIndexMap 集合 + * @return / + */ + default Map newTokenIndexMap() { + return new LinkedHashMap<>(); + } + + /** + * 获取最大 ttl 值 + * @param ttlList / + * @return / + */ + default long getMaxTtl(ArrayList ttlList) { + long maxTtl = 0; + for (long ttl : ttlList) { + if(ttl == SaTokenDao.NEVER_EXPIRE) { + maxTtl = SaTokenDao.NEVER_EXPIRE; + break; + } + if(ttl > maxTtl) { + maxTtl = ttl; + } + } + return maxTtl; + } + + /** + * 获取最大 ttl 值:过期时间 (13位时间戳) 转 ttl (秒) + * @param expireTimeList / + * @return / + */ + default long getMaxTtlByExpireTime(Collection expireTimeList) { + long maxTtl = 0; + for (long expireTime : expireTimeList) { + long ttl = expireTimeToTtl(expireTime); + if(ttl == SaTokenDao.NEVER_EXPIRE) { + maxTtl = SaTokenDao.NEVER_EXPIRE; + break; + } + if(ttl > maxTtl) { + maxTtl = ttl; + } + } + return maxTtl; + } + + /** + * 过期时间 (13位时间戳) 转 (13位时间戳) ttl (秒) + * @param expireTime / + * @return / + */ + default long expireTimeToTtl(long expireTime) { + if(expireTime == SaTokenDao.NEVER_EXPIRE) { + return SaTokenDao.NEVER_EXPIRE; + } + if(expireTime == SaTokenDao.NOT_VALUE_EXPIRE) { + return SaTokenDao.NOT_VALUE_EXPIRE; + } + long currentTime = System.currentTimeMillis(); + if(expireTime < currentTime) { + return SaTokenDao.NOT_VALUE_EXPIRE; + } + return (expireTime - currentTime) / 1000; + } + + /** + * ttl (秒) 转 过期时间 (13位时间戳) + * @param ttl / + * @return / + */ + default long ttlToExpireTime(long ttl) { + if(ttl == SaTokenDao.NEVER_EXPIRE) { + return SaTokenDao.NEVER_EXPIRE; + } + if(ttl < 0) { + return SaTokenDao.NOT_VALUE_EXPIRE; + } + return ttl * 1000 + System.currentTimeMillis(); + } + +} diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/h5/SaOAuth2ServerH5Controller.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/h5/SaOAuth2ServerH5Controller.java index 92a91680..73600d09 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/h5/SaOAuth2ServerH5Controller.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/h5/SaOAuth2ServerH5Controller.java @@ -13,6 +13,7 @@ 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.oauth2.processor.SaOAuth2ServerProcessor; +import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; import cn.dev33.satoken.oauth2.template.SaOAuth2Template; import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.PostMapping; @@ -55,13 +56,16 @@ public class SaOAuth2ServerH5Controller { // 3、构建请求 Model RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, loginId); - // 4、校验:重定向域名是否合法 + // 4、开发者自定义的授权前置检查 + SaOAuth2Strategy.instance.userAuthorizeClientCheck.run(ra.loginId, ra.clientId); + + // 5、校验:重定向域名是否合法 oauth2Template.checkRedirectUri(ra.clientId, ra.redirectUri); - // 5、校验:此次申请的Scope,该Client是否已经签约 + // 6、校验:此次申请的Scope,该Client是否已经签约 oauth2Template.checkContractScope(ra.clientId, ra.scopes); - // 6、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 + // 7、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 boolean isNeedCarefulConfirm = oauth2Template.isNeedCarefulConfirm(ra.loginId, ra.clientId, ra.scopes); if(isNeedCarefulConfirm) { SaClientModel cm = oauth2Template.checkClientModel(ra.clientId); @@ -71,7 +75,7 @@ public class SaOAuth2ServerH5Controller { } } - // 7、判断授权类型,重定向到不同地址 + // 8、判断授权类型,重定向到不同地址 // 如果是 授权码式,则:开始重定向授权,下放code if(SaOAuth2Consts.ResponseType.code.equals(ra.responseType)) { CodeModel codeModel = dataGenerate.generateCode(ra); diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/test/Test2Controller.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/test/Test2Controller.java index 93710e0e..0a325f5f 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/test/Test2Controller.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/test/Test2Controller.java @@ -1,11 +1,13 @@ package com.pj.test; +import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.template.SaOAuth2Util; import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Map; /** * 测试 OAuth2 相关 token 增删查 @@ -24,6 +26,13 @@ public class Test2Controller { return SaResult.data(accessTokenValueList); } + // 测试:查询全部 Access-Token, 带过期时间 --- http://localhost:8000/test/getAccessTokenIndexMap?clientId=1001&loginId=10001 + @RequestMapping("/getAccessTokenIndexMap") + public SaResult getAccessTokenIndexMap(String clientId, long loginId) { + Map accessTokenIndexMap = SaOAuth2Manager.getDao().getAccessTokenIndexMap_FromAdjustAfter(clientId, loginId); + return SaResult.data(accessTokenIndexMap); + } + // 测试:回收指定 Access-Token --- http://localhost:8000/test/revokeAccessToken?access_token=xxxxxxxxxx @RequestMapping("/revokeAccessToken") public SaResult revokeAccessToken(String access_token) { @@ -38,6 +47,7 @@ public class Test2Controller { return SaResult.ok(); } + // 测试:查询全部 Refresh-Token --- http://localhost:8000/test/getRefreshTokenValueList?clientId=1001&loginId=10001 @RequestMapping("/getRefreshTokenValueList") public SaResult getRefreshTokenValueList(String clientId, long loginId) { @@ -45,6 +55,13 @@ public class Test2Controller { return SaResult.data(refreshTokenValueList); } + // 测试:查询全部 Refresh-Token, 带过期时间 --- http://localhost:8000/test/getRefreshTokenIndexMap?clientId=1001&loginId=10001 + @RequestMapping("/getRefreshTokenIndexMap") + public SaResult getRefreshTokenIndexMap(String clientId, long loginId) { + Map refreshTokenIndexMap = SaOAuth2Manager.getDao().getRefreshTokenIndexMap_FromAdjustAfter(clientId, loginId); + return SaResult.data(refreshTokenIndexMap); + } + // 测试:回收指定 Refresh-Token --- http://localhost:8000/test/revokeRefreshToken?refresh_token=xxxxxxxxxx @RequestMapping("/revokeRefreshToken") public SaResult revokeRefreshToken(String refresh_token) { @@ -59,6 +76,7 @@ public class Test2Controller { return SaResult.ok(); } + // 测试:查询全部 Client-Token --- http://localhost:8000/test/getClientTokenValueList?clientId=1001 @RequestMapping("/getClientTokenValueList") public SaResult getClientTokenValueList(String clientId) { @@ -66,6 +84,13 @@ public class Test2Controller { return SaResult.data(clientTokenValueList); } + // 测试:查询全部 Client-Token, 带过期时间 --- http://localhost:8000/test/getClientTokenIndexMap?clientId=1001&loginId=10001 + @RequestMapping("/getClientTokenIndexMap") + public SaResult getClientTokenIndexMap(String clientId, long loginId) { + Map rlientTokenIndexMap = SaOAuth2Manager.getDao().getClientTokenIndexMap_FromAdjustAfter(clientId, loginId); + return SaResult.data(rlientTokenIndexMap); + } + // 测试:回收指定 Client-Token --- http://localhost:8000/test/revokeClientToken?client_token=xxxxxxxxxxx @RequestMapping("/revokeClientToken") public SaResult revokeClientToken(String client_token) { diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2OidcConfig.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2OidcConfig.java index 2eb817d4..7af40261 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2OidcConfig.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2OidcConfig.java @@ -33,19 +33,42 @@ public class SaOAuth2OidcConfig implements Serializable { /** idToken 有效期(单位秒) 默认十分钟 */ public long idTokenTimeout = 60 * 10; + + /** + * 获取 iss 值,如不配置则自动计算 + * + * @return / + */ public String getIss() { - return iss; + return this.iss; } + /** + * 设置 iss 值,如不配置则自动计算 + * + * @param iss / + * @return 对象自身 + */ public SaOAuth2OidcConfig setIss(String iss) { this.iss = iss; return this; } + /** + * 获取 idToken 有效期(单位秒) 默认十分钟 + * + * @return / + */ public long getIdTokenTimeout() { - return idTokenTimeout; + return this.idTokenTimeout; } + /** + * 设置 idToken 有效期(单位秒) 默认十分钟 + * + * @param idTokenTimeout / + * @return 对象自身 + */ public SaOAuth2OidcConfig setIdTokenTimeout(long idTokenTimeout) { this.idTokenTimeout = idTokenTimeout; return this; diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2ServerConfig.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2ServerConfig.java index d5d5b455..c226e4bc 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2ServerConfig.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2ServerConfig.java @@ -44,24 +44,18 @@ public class SaOAuth2ServerConfig implements Serializable { /** 是否打开模式:凭证式(Client Credentials) */ public Boolean enableClientCredentials = true; - /** 是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */ - public Boolean isNewRefresh = false; - /** Code授权码 保存的时间(单位:秒) 默认五分钟 */ public long codeTimeout = 60 * 5; - /** Access-Token 保存的时间(单位:秒) 默认两个小时 */ + /** 全局默认配置所有应用:Access-Token 保存的时间(单位:秒) 默认两个小时 */ public long accessTokenTimeout = 60 * 60 * 2; - /** Refresh-Token 保存的时间(单位:秒) 默认30 天 */ + /** 全局默认配置所有应用:Refresh-Token 保存的时间(单位:秒) 默认30 天 */ public long refreshTokenTimeout = 60 * 60 * 24 * 30; - /** Client-Token 保存的时间(单位:秒) 默认两个小时 */ + /** 全局默认配置所有应用:Client-Token 保存的时间(单位:秒) 默认两个小时 */ public long clientTokenTimeout = 60 * 60 * 2; - /** Lower-Client-Token 保存的时间(单位:秒) 默认为 -1,代表延续 Client-Token 有效期 */ - public long lowerClientTokenTimeout = -1; - /** 全局默认配置所有应用:单个应用单个用户最多同时存在的 Access-Token 数量 */ public int maxAccessTokenCount = 12; @@ -71,6 +65,9 @@ public class SaOAuth2ServerConfig implements Serializable { /** 全局默认配置所有应用:单个应用最多同时存在的 Client-Token 数量 */ public int maxClientTokenCount = 12; + /** 全局默认配置所有应用:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */ + public Boolean isNewRefresh = false; + /** 默认 openid 生成算法中使用的摘要前缀 */ public String openidDigestPrefix = SaOAuth2Consts.OPENID_DEFAULT_DIGEST_PREFIX; @@ -83,7 +80,7 @@ public class SaOAuth2ServerConfig implements Serializable { /** 指定低级权限,多个用逗号隔开 */ public String lowerScope; - /** 模式4是否返回 AccessToken 字段 */ + /** 模式4是否返回 AccessToken 字段,以使其更符合 OAuth2 RFC 规范 */ public Boolean mode4ReturnAccessToken = false; /** 是否在返回值中隐藏默认的状态字段 (code、msg、data) */ @@ -115,15 +112,17 @@ public class SaOAuth2ServerConfig implements Serializable { // get set /** - * @return enableCode + * 是否打开模式:授权码(Authorization Code) + * @return enableAuthorizationCode */ public Boolean getEnableAuthorizationCode() { return enableAuthorizationCode; } /** - * @param enableAuthorizationCode 要设置的 enableAuthorizationCode - * @return / + * 设置是否打开模式:授权码(Authorization Code) + * @param enableAuthorizationCode 是否开启 + * @return 对象自身 */ public SaOAuth2ServerConfig setEnableAuthorizationCode(Boolean enableAuthorizationCode) { this.enableAuthorizationCode = enableAuthorizationCode; @@ -131,6 +130,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 是否打开模式:隐藏式(Implicit) * @return enableImplicit */ public Boolean getEnableImplicit() { @@ -138,8 +138,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param enableImplicit 要设置的 enableImplicit - * @return / + * 设置是否打开模式:隐藏式(Implicit) + * @param enableImplicit 是否开启 + * @return 对象自身 */ public SaOAuth2ServerConfig setEnableImplicit(Boolean enableImplicit) { this.enableImplicit = enableImplicit; @@ -147,6 +148,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 是否打开模式:密码式(Password) * @return enablePassword */ public Boolean getEnablePassword() { @@ -154,7 +156,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param enablePassword 要设置的 enablePassword + * 设置是否打开模式:密码式(Password) + * @param enablePassword 是否开启 + * @return 对象自身 */ public SaOAuth2ServerConfig setEnablePassword(Boolean enablePassword) { this.enablePassword = enablePassword; @@ -162,6 +166,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 是否打开模式:凭证式(Client Credentials) * @return enableClientCredentials */ public Boolean getEnableClientCredentials() { @@ -169,8 +174,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param enableClientCredentials 要设置的 enableClientCredentials - * @return / + * 设置是否打开模式:凭证式(Client Credentials) + * @param enableClientCredentials 是否开启 + * @return 对象自身 */ public SaOAuth2ServerConfig setEnableClientCredentials(Boolean enableClientCredentials) { this.enableClientCredentials = enableClientCredentials; @@ -178,6 +184,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 全局默认配置所有应用:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token * @return isNewRefresh */ public Boolean getIsNewRefresh() { @@ -185,8 +192,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param isNewRefresh 要设置的 isNewRefresh - * @return / + * 全局默认配置所有应用:设置是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token + * @param isNewRefresh 是否开启 + * @return 对象自身 */ public SaOAuth2ServerConfig setIsNewRefresh(Boolean isNewRefresh) { this.isNewRefresh = isNewRefresh; @@ -194,6 +202,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * Code授权码 保存的时间(单位:秒) 默认五分钟 * @return codeTimeout */ public long getCodeTimeout() { @@ -201,7 +210,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param codeTimeout 要设置的 codeTimeout + * 设置Code授权码保存的时间(单位:秒) + * @param codeTimeout 保存时间(秒) * @return 对象自身 */ public SaOAuth2ServerConfig setCodeTimeout(long codeTimeout) { @@ -210,6 +220,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 全局默认配置所有应用:Access-Token 保存的时间(单位:秒) 默认两个小时 * @return accessTokenTimeout */ public long getAccessTokenTimeout() { @@ -217,7 +228,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param accessTokenTimeout 要设置的 accessTokenTimeout + * 全局默认配置所有应用:设置Access-Token保存的时间(单位:秒) + * @param accessTokenTimeout 保存时间(秒) * @return 对象自身 */ public SaOAuth2ServerConfig setAccessTokenTimeout(long accessTokenTimeout) { @@ -226,6 +238,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 全局默认配置所有应用:Refresh-Token 保存的时间(单位:秒) 默认30天 * @return refreshTokenTimeout */ public long getRefreshTokenTimeout() { @@ -233,7 +246,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param refreshTokenTimeout 要设置的 refreshTokenTimeout + * 全局默认配置所有应用:设置Refresh-Token保存的时间(单位:秒) + * @param refreshTokenTimeout 保存时间(秒) * @return 对象自身 */ public SaOAuth2ServerConfig setRefreshTokenTimeout(long refreshTokenTimeout) { @@ -242,6 +256,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 全局默认配置所有应用:Client-Token 保存的时间(单位:秒) 默认两个小时 * @return clientTokenTimeout */ public long getClientTokenTimeout() { @@ -249,7 +264,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param clientTokenTimeout 要设置的 clientTokenTimeout + * 全局默认配置所有应用:设置Client-Token保存的时间(单位:秒) + * @param clientTokenTimeout 保存时间(秒) * @return 对象自身 */ public SaOAuth2ServerConfig setClientTokenTimeout(long clientTokenTimeout) { @@ -258,22 +274,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @return lowerClientTokenTimeout - */ - public long getLowerClientTokenTimeout() { - return lowerClientTokenTimeout; - } - - /** - * @param lowerClientTokenTimeout 要设置的 lowerClientTokenTimeout - * @return 对象自身 - */ - public SaOAuth2ServerConfig setLowerClientTokenTimeout(long lowerClientTokenTimeout) { - this.lowerClientTokenTimeout = lowerClientTokenTimeout; - return this; - } - - /** + * 全局默认配置所有应用:单个应用单个用户最多同时存在的 Access-Token 数量 * @return maxAccessTokenCount */ public int getMaxAccessTokenCount() { @@ -281,7 +282,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param maxAccessTokenCount 要设置的 maxAccessTokenCount + * 设置单个应用单个用户最多同时存在的 Access-Token 数量 + * @param maxAccessTokenCount 最大数量 * @return 对象自身 */ public SaOAuth2ServerConfig setMaxAccessTokenCount(int maxAccessTokenCount) { @@ -291,15 +293,15 @@ public class SaOAuth2ServerConfig implements Serializable { /** * 全局默认配置所有应用:单个应用单个用户最多同时存在的 Refresh-Token 数量 - * @return / + * @return maxRefreshTokenCount */ public int getMaxRefreshTokenCount() { return maxRefreshTokenCount; } /** - * 全局默认配置所有应用:单个应用单个用户最多同时存在的 Refresh-Token 数量 - * @param maxRefreshTokenCount / + * 设置单个应用单个用户最多同时存在的 Refresh-Token 数量 + * @param maxRefreshTokenCount 最大数量 * @return 对象自身 */ public SaOAuth2ServerConfig setMaxRefreshTokenCount(int maxRefreshTokenCount) { @@ -308,16 +310,16 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * 全局默认配置所有应用:单个应用单个用户最多同时存在的 Client-Token 数量 - * @return / + * 全局默认配置所有应用:单个应用最多同时存在的 Client-Token 数量 + * @return maxClientTokenCount */ public int getMaxClientTokenCount() { return maxClientTokenCount; } /** - * 全局默认配置所有应用:单个应用单个用户最多同时存在的 Client-Token 数量 - * @param maxClientTokenCount / + * 设置单个应用最多同时存在的 Client-Token 数量 + * @param maxClientTokenCount 最大数量 * @return 对象自身 */ public SaOAuth2ServerConfig setMaxClientTokenCount(int maxClientTokenCount) { @@ -326,6 +328,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 默认 openid 生成算法中使用的摘要前缀 * @return openidDigestPrefix */ public String getOpenidDigestPrefix() { @@ -333,7 +336,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param openidDigestPrefix 要设置的 openidDigestPrefix + * 设置默认 openid 生成算法中使用的摘要前缀 + * @param openidDigestPrefix 摘要前缀 * @return 对象自身 */ public SaOAuth2ServerConfig setOpenidDigestPrefix(String openidDigestPrefix) { @@ -342,6 +346,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 默认 unionid 生成算法中使用的摘要前缀 * @return unionidDigestPrefix */ public String getUnionidDigestPrefix() { @@ -349,7 +354,8 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param unionidDigestPrefix 要设置的 unionidDigestPrefix + * 设置默认 unionid 生成算法中使用的摘要前缀 + * @param unionidDigestPrefix 摘要前缀 * @return 对象自身 */ public SaOAuth2ServerConfig setUnionidDigestPrefix(String unionidDigestPrefix) { @@ -358,19 +364,17 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * 获取 指定高级权限,多个用逗号隔开 - * - * @return higherScope 指定高级权限,多个用逗号隔开 + * 指定高级权限,多个用逗号隔开 + * @return higherScope */ public String getHigherScope() { - return this.higherScope; + return higherScope; } /** - * 设置 指定高级权限,多个用逗号隔开 - * - * @param higherScope 指定高级权限,多个用逗号隔开 - * @return / + * 设置高级权限,多个用逗号隔开 + * @param higherScope 权限字符串 + * @return 对象自身 */ public SaOAuth2ServerConfig setHigherScope(String higherScope) { this.higherScope = higherScope; @@ -378,19 +382,17 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * 获取 指定低级权限,多个用逗号隔开 - * - * @return lowerScope 指定低级权限,多个用逗号隔开 + * 指定低级权限,多个用逗号隔开 + * @return lowerScope */ public String getLowerScope() { - return this.lowerScope; + return lowerScope; } /** - * 设置 指定低级权限,多个用逗号隔开 - * - * @param lowerScope 指定低级权限,多个用逗号隔开 - * @return / + * 设置低级权限,多个用逗号隔开 + * @param lowerScope 权限字符串 + * @return 对象自身 */ public SaOAuth2ServerConfig setLowerScope(String lowerScope) { this.lowerScope = lowerScope; @@ -398,6 +400,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 模式4是否返回 AccessToken 字段,以使其更符合 OAuth2 RFC 规范 * @return mode4ReturnAccessToken */ public Boolean getMode4ReturnAccessToken() { @@ -405,7 +408,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param mode4ReturnAccessToken 要设置的 mode4ReturnAccessToken + * 设置模式4是否返回 AccessToken 字段,以使其更符合 OAuth2 RFC 规范 + * @param mode4ReturnAccessToken 是否返回 + * @return 对象自身 */ public SaOAuth2ServerConfig setMode4ReturnAccessToken(Boolean mode4ReturnAccessToken) { this.mode4ReturnAccessToken = mode4ReturnAccessToken; @@ -413,6 +418,7 @@ public class SaOAuth2ServerConfig implements Serializable { } /** + * 是否在返回值中隐藏默认的状态字段 (code、msg、data) * @return hideStatusField */ public Boolean getHideStatusField() { @@ -420,7 +426,9 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * @param hideStatusField 要设置的 hideStatusField + * 设置是否在返回值中隐藏默认的状态字段 (code、msg、data) + * @param hideStatusField 是否隐藏 + * @return 对象自身 */ public SaOAuth2ServerConfig setHideStatusField(Boolean hideStatusField) { this.hideStatusField = hideStatusField; @@ -428,19 +436,17 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * 获取 oidc 相关配置 - * - * @return oidc 相关配置 + * 获取oidc相关配置 + * @return oidc配置对象 */ public SaOAuth2OidcConfig getOidc() { - return this.oidc; + return oidc; } /** - * 设置 oidc 相关配置 - * - * @param oidc / - * @return / + * 设置oidc相关配置 + * @param oidc oidc配置对象 + * @return 对象自身 */ public SaOAuth2ServerConfig setOidc(SaOAuth2OidcConfig oidc) { this.oidc = oidc; @@ -448,16 +454,17 @@ public class SaOAuth2ServerConfig implements Serializable { } /** - * 获取 client 列表 - * @return / + * 获取client列表 + * @return client列表 */ public Map getClients() { return clients; } /** - * 写入 client 列表 - * @return / + * 设置client列表 + * @param clients client列表 + * @return 对象自身 */ public SaOAuth2ServerConfig setClients(Map clients) { this.clients = clients; @@ -466,7 +473,7 @@ public class SaOAuth2ServerConfig implements Serializable { @Override public String toString() { - return "SaOAuth2ServerConfig{" + + return "SaOAuth2ServerConfig {" + "enableAuthorizationCode=" + enableAuthorizationCode + ", enableImplicit=" + enableImplicit + ", enablePassword=" + enablePassword + @@ -476,20 +483,19 @@ public class SaOAuth2ServerConfig implements Serializable { ", accessTokenTimeout=" + accessTokenTimeout + ", refreshTokenTimeout=" + refreshTokenTimeout + ", clientTokenTimeout=" + clientTokenTimeout + - ", lowerClientTokenTimeout=" + lowerClientTokenTimeout + ", maxAccessTokenCount=" + maxAccessTokenCount + ", maxRefreshTokenCount=" + maxRefreshTokenCount + ", maxClientTokenCount=" + maxClientTokenCount + - ", openidDigestPrefix='" + openidDigestPrefix + - ", unionidDigestPrefix='" + unionidDigestPrefix + - ", higherScope='" + higherScope + - ", lowerScope='" + lowerScope + - ", mode4ReturnAccessToken='" + mode4ReturnAccessToken + - ", hideStatusField='" + hideStatusField + - ", oidc='" + oidc + + ", openidDigestPrefix=" + openidDigestPrefix + + ", unionidDigestPrefix=" + unionidDigestPrefix + + ", higherScope=" + higherScope + + ", lowerScope=" + lowerScope + + ", mode4ReturnAccessToken=" + mode4ReturnAccessToken + + ", hideStatusField=" + hideStatusField + + ", oidc=" + oidc + + ", clients=" + clients + '}'; } - } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/dao/SaOAuth2Dao.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/dao/SaOAuth2Dao.java index 59b7e2e2..f155a742 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/dao/SaOAuth2Dao.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/dao/SaOAuth2Dao.java @@ -27,23 +27,26 @@ import cn.dev33.satoken.oauth2.data.model.RefreshTokenModel; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.session.raw.SaRawSessionDelegator; import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaTtlMethods; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import static cn.dev33.satoken.oauth2.template.SaOAuth2Util.checkClientModel; /** - * Sa-Token OAuth2 数据持久层 + * Sa-Token OAuth2 数据持久层 (在 SaTokenDao 之上再封装一层,方便 OAuth2 模块整体的数据读写操作) * * @author click33 * @since 1.39.0 */ -public class SaOAuth2Dao { +public class SaOAuth2Dao implements SaTtlMethods { // ------------------- 索引操作公共代码 /** - * Raw Session 读写委托 + * Raw Session 读写委托 (存储 Access-Token、Refresh-Token、Client-Token 索引) */ public SaRawSessionDelegator oauth2RSD = new SaRawSessionDelegator("oauth2"); @@ -63,10 +66,11 @@ public class SaOAuth2Dao { public static final String CLIENT_TOKEN_MAP = "__HD_CLIENT_TOKEN_MAP"; /** - * 获取 Access-Token 索引 RawSession + * 获取:保存 Access-Token 索引时使用的 RawSession + * * @param clientId 应用 id * @param loginId 账号 id - * @param isCreate 如果尚未创建,是否理解创建 + * @param isCreate 如果尚未创建,是否立即创建 * @return / */ protected SaSession getRawSessionByAccessToken(String clientId, Object loginId, boolean isCreate) { @@ -75,10 +79,11 @@ public class SaOAuth2Dao { } /** - * 获取 refresh_token 索引 RawSession + * 获取:保存 Refresh-Token 索引时使用的 RawSession + * * @param clientId 应用 id * @param loginId 账号 id - * @param isCreate 如果尚未创建,是否理解创建 + * @param isCreate 如果尚未创建,是否立即创建 * @return / */ protected SaSession getRawSessionByRefreshToken(String clientId, Object loginId, boolean isCreate) { @@ -87,9 +92,10 @@ public class SaOAuth2Dao { } /** - * 获取 client_token 索引 RawSession + * 获取:保存 Client-Token 索引时使用的 RawSession + * * @param clientId 应用 id - * @param isCreate 如果尚未创建,是否理解创建 + * @param isCreate 如果尚未创建,是否立即创建 * @return / */ protected SaSession getRawSessionByClientToken(String clientId, boolean isCreate) { @@ -98,7 +104,7 @@ public class SaOAuth2Dao { } /** - * 在 RawSession 上添加 token 索引 + * 在 RawSession 上添加 token 索引,并完整调整索引列表 * * @param session 待操作的 RawSession * @param tokenIndexMapSaveKey 在 session 上保存 token 索引列表使用的 key @@ -107,19 +113,19 @@ public class SaOAuth2Dao { * @param maxTokenCount 允许的最多 token 数量,超出的将被删除 (-1=不限制) * @param removeFun 执行删除 token 的函数 */ - protected void addTokenIndex(SaSession session, String tokenIndexMapSaveKey, String token, long timeout, int maxTokenCount, SaParamFunction removeFun) { + protected void addTokenIndex_AndAdjust(SaSession session, String tokenIndexMapSaveKey, String token, long timeout, int maxTokenCount, SaParamFunction removeFun) { Map tokenIndexMap = session.get(tokenIndexMapSaveKey, this::newTokenIndexMap); if(! tokenIndexMap.containsKey(token)) { // 添加 tokenIndexMap.put(token, ttlToExpireTime(timeout)); // 剔除过期的 - tokenIndexMap = removeExpiredIndex(tokenIndexMap); + tokenIndexMap = _removeExpiredIndex(tokenIndexMap); // 删掉溢出的 - tokenIndexMap = removeOverflowIndex(tokenIndexMap, maxTokenCount, removeFun); + tokenIndexMap = _removeOverflowIndex(tokenIndexMap, maxTokenCount, removeFun); // 保存 session.set(tokenIndexMapSaveKey, tokenIndexMap); // 更新 TTL - long maxTtl = getMaxTtl(tokenIndexMap.values()); + long maxTtl = getMaxTtlByExpireTime(tokenIndexMap.values()); if(maxTtl != 0) { session.updateTimeout(maxTtl); } @@ -127,12 +133,13 @@ public class SaOAuth2Dao { } /** - * 在 RawSession 上删除 token 索引 + * 在 RawSession 上删除 token 索引,并尝试注销 RawSession + * * @param session 待操作的 RawSession * @param tokenIndexMapSaveKey 在 session 上保存 token 索引列表使用的 key * @param token 待删除的 token */ - protected void deleteTokenIndex(SaSession session, String tokenIndexMapSaveKey, String token) { + protected void deleteTokenIndex_AndTryLogout(SaSession session, String tokenIndexMapSaveKey, String token) { Map tokenIndexMap = session.get(tokenIndexMapSaveKey, this::newTokenIndexMap); tokenIndexMap.remove(token); // 如果删除后还有记录,就再次保存 @@ -150,8 +157,8 @@ public class SaOAuth2Dao { * @param tokenIndexMap token 索引列表 * @return 调整后的索引列表 */ - protected Map removeExpiredIndex(Map tokenIndexMap) { - Map newTokenList = newTokenIndexMap(); + protected Map _removeExpiredIndex(Map tokenIndexMap) { + Map newTokenList = newTokenIndexMap(); for (Map.Entry entry : tokenIndexMap.entrySet()) { long ttl = expireTimeToTtl(entry.getValue()); if(ttl != SaTokenDao.NOT_VALUE_EXPIRE) { @@ -169,7 +176,7 @@ public class SaOAuth2Dao { * @param removeFun 执行删除 token 的函数 * @return 调整后的索引列表 */ - protected Map removeOverflowIndex(Map tokenIndexMap, int maxTokenCount, SaParamFunction removeFun) { + protected Map _removeOverflowIndex(Map tokenIndexMap, int maxTokenCount, SaParamFunction removeFun) { // 如果当前数量未超过限制,直接返回 if (tokenIndexMap.size() <= maxTokenCount || maxTokenCount == SaTokenDao.NEVER_EXPIRE) { @@ -203,25 +210,25 @@ public class SaOAuth2Dao { } /** - * 获取 Token 列表 + * 从 RawSession 获取 Token 索引列表(获取之前会完整调整索引列表,保证获取的都是有效 token) * * @param session 待操作的 RawSession * @param tokenIndexMapSaveKey 在 session 上保存 token 索引列表使用的 key * @return / */ - protected List getTokenValueList(SaSession session, String tokenIndexMapSaveKey) { + protected Map getTokenIndexMap_FromAdjustAfter(SaSession session, String tokenIndexMapSaveKey) { if(session == null) { - return new ArrayList<>(); + return newTokenIndexMap(); } // 根据 ttl 值过滤一遍 Map tokenIndexMap = session.get(tokenIndexMapSaveKey, this::newTokenIndexMap); - Map newTokenIndexMap = removeExpiredIndex(tokenIndexMap); + Map newTokenIndexMap = _removeExpiredIndex(tokenIndexMap); // 如果调整后集合长度归零了,说明 token 已全部过期,直接注销此 RawSession if(newTokenIndexMap.isEmpty()) { session.logout(); - return new ArrayList<>(); + return newTokenIndexMap(); } // 没有归零,但是长度变小了,说明有过期的 token,需要重写写入一遍 @@ -230,15 +237,26 @@ public class SaOAuth2Dao { } // 转 List 返回 - return new ArrayList<>(newTokenIndexMap.keySet()); + return newTokenIndexMap; + } + + /** + * 从 RawSession 获取 Token 列表(获取之前会完整调整索引列表,保证获取的都是有效 token) + * + * @param session 待操作的 RawSession + * @param tokenIndexMapSaveKey 在 session 上保存 token 索引列表使用的 key + * @return / + */ + protected List getTokenValueList_FromAdjustAfter(SaSession session, String tokenIndexMapSaveKey) { + return new ArrayList<>(getTokenIndexMap_FromAdjustAfter(session, tokenIndexMapSaveKey).keySet()); } // ------------------- code 操作 /** - * 持久化:Code-Model - * @param c . + * 保存:CodeModel + * @param c / */ public void saveCode(CodeModel c) { if(c == null) { @@ -248,8 +266,8 @@ public class SaOAuth2Dao { } /** - * 删除:Code - * @param code 值 + * 删除:CodeModel + * @param code / */ public void deleteCode(String code) { if(code != null) { @@ -258,9 +276,9 @@ public class SaOAuth2Dao { } /** - * 获取:Code Model - * @param code . - * @return . + * 获取:CodeModel + * @param code / + * @return / */ public CodeModel getCode(String code) { if(code == null) { @@ -273,8 +291,8 @@ public class SaOAuth2Dao { // ------------------- code 索引 /** - * 持久化:Code-索引 - * @param c . + * 保存:Code 索引 + * @param c / */ public void saveCodeIndex(CodeModel c) { if(c == null) { @@ -284,7 +302,7 @@ public class SaOAuth2Dao { } /** - * 删除:Code索引 + * 删除:Code 索引 * @param clientId 应用id * @param loginId 账号id */ @@ -296,18 +314,18 @@ public class SaOAuth2Dao { * 获取:Code Value * @param clientId 应用id * @param loginId 账号id - * @return . + * @return / */ public String getCodeValue(String clientId, Object loginId) { return getSaTokenDao().get(splicingCodeIndexKey(clientId, loginId)); } - // ------------------- access_token Model + // ------------------- Access-Token Model /** - * 持久化:AccessToken-Model - * @param at . + * 保存:AccessTokenModel + * @param at / */ public void saveAccessToken(AccessTokenModel at) { if(at == null) { @@ -317,7 +335,7 @@ public class SaOAuth2Dao { } /** - * 删除:Access-Token + * 删除:AccessTokenModel * @param accessToken 值 */ public void deleteAccessToken(String accessToken) { @@ -327,9 +345,9 @@ public class SaOAuth2Dao { } /** - * 获取:Access-Token Model - * @param accessToken . - * @return . + * 获取:AccessTokenModel + * @param accessToken / + * @return / */ public AccessTokenModel getAccessToken(String accessToken) { if(accessToken == null) { @@ -339,20 +357,20 @@ public class SaOAuth2Dao { } - // ------------------- access_token 索引 + // ------------------- Access-Token 索引 /** - * 持久化:Access-Token 索引 + * 保存:Access-Token 索引,并完整调整索引列表 * * @param at / * @param maxAccessTokenCount 允许的最多 Access-Token 数量,超出的将被删除 (-1=不限制) */ - public void saveAccessTokenIndex(AccessTokenModel at, int maxAccessTokenCount) { + public void saveAccessTokenIndex_AndAdjust(AccessTokenModel at, int maxAccessTokenCount) { if(at == null) { return; } SaSession session = getRawSessionByAccessToken(at.clientId, at.loginId, true); - addTokenIndex(session, ACCESS_TOKEN_MAP, at.accessToken, at.getExpiresIn(), maxAccessTokenCount, this::deleteAccessToken); + addTokenIndex_AndAdjust(session, ACCESS_TOKEN_MAP, at.accessToken, at.getExpiresIn(), maxAccessTokenCount, this::deleteAccessToken); } /** @@ -362,12 +380,12 @@ public class SaOAuth2Dao { * @param loginId 账号id * @param accessToken 值 */ - public void deleteAccessTokenIndexBySingleData(String clientId, Object loginId, String accessToken) { + public void deleteAccessTokenIndex_BySingleData(String clientId, Object loginId, String accessToken) { SaSession session = getRawSessionByAccessToken(clientId, loginId, false); if(session == null) { return; } - deleteTokenIndex(session, ACCESS_TOKEN_MAP, accessToken); + deleteTokenIndex_AndTryLogout(session, ACCESS_TOKEN_MAP, accessToken); } /** @@ -380,22 +398,34 @@ public class SaOAuth2Dao { } /** - * 获取 Access-Token 列表:此应用下 对 某个用户 签发的所有 Access-token + * 获取 Access-Token 索引列表(获取之前会完整调整索引列表,保证获取的都是有效 AccessToken 索引) * * @param clientId 应用id * @param loginId 账号id * @return / */ - public List getAccessTokenValueList(String clientId, Object loginId) { + public Map getAccessTokenIndexMap_FromAdjustAfter(String clientId, Object loginId) { SaSession session = getRawSessionByAccessToken(clientId, loginId, false); - return getTokenValueList(session, ACCESS_TOKEN_MAP); + return getTokenIndexMap_FromAdjustAfter(session, ACCESS_TOKEN_MAP); + } + + /** + * 获取 Access-Token 列表(获取之前会完整调整索引列表,保证获取的都是有效 AccessToken) + * + * @param clientId 应用id + * @param loginId 账号id + * @return / + */ + public List getAccessTokenValueList_FromAdjustAfter(String clientId, Object loginId) { + SaSession session = getRawSessionByAccessToken(clientId, loginId, false); + return getTokenValueList_FromAdjustAfter(session, ACCESS_TOKEN_MAP); } - // ------------------- refresh_token Model + // ------------------- Refresh-Token Model /** - * 持久化:RefreshToken-Model + * 保存:RefreshTokenModel * @param rt . */ public void saveRefreshToken(RefreshTokenModel rt) { @@ -406,7 +436,7 @@ public class SaOAuth2Dao { } /** - * 删除:Refresh-Token + * 删除:RefreshTokenModel * @param refreshToken 值 */ public void deleteRefreshToken(String refreshToken) { @@ -416,9 +446,9 @@ public class SaOAuth2Dao { } /** - * 获取:Refresh-Token Model - * @param refreshToken . - * @return . + * 获取:RefreshTokenModel + * @param refreshToken / + * @return / */ public RefreshTokenModel getRefreshToken(String refreshToken) { if(refreshToken == null) { @@ -428,20 +458,20 @@ public class SaOAuth2Dao { } - // ------------------- refresh_token 索引 + // ------------------- Refresh-Token 索引 /** - * 持久化:Refresh-Token 索引 + * 保存:Refresh-Token 索引 * * @param rt / * @param maxRefreshTokenCount 允许的最多 Refresh-Token 数量,超出的将被删除 (-1=不限制) */ - public void saveRefreshTokenIndex(RefreshTokenModel rt, int maxRefreshTokenCount) { + public void saveRefreshTokenIndex_AndAdjust(RefreshTokenModel rt, int maxRefreshTokenCount) { if(rt == null) { return; } SaSession session = getRawSessionByRefreshToken(rt.clientId, rt.loginId, true); - addTokenIndex(session, REFRESH_TOKEN_MAP, rt.refreshToken, rt.getExpiresIn(), maxRefreshTokenCount, this::deleteRefreshToken); + addTokenIndex_AndAdjust(session, REFRESH_TOKEN_MAP, rt.refreshToken, rt.getExpiresIn(), maxRefreshTokenCount, this::deleteRefreshToken); } /** @@ -451,12 +481,12 @@ public class SaOAuth2Dao { * @param loginId 账号id * @param refreshToken 值 */ - public void deleteRefreshTokenIndexBySingleData(String clientId, Object loginId, String refreshToken) { + public void deleteRefreshTokenIndex_BySingleData(String clientId, Object loginId, String refreshToken) { SaSession session = getRawSessionByRefreshToken(clientId, loginId, false); if(session == null) { return; } - deleteTokenIndex(session, REFRESH_TOKEN_MAP, refreshToken); + deleteTokenIndex_AndTryLogout(session, REFRESH_TOKEN_MAP, refreshToken); } /** @@ -469,22 +499,34 @@ public class SaOAuth2Dao { } /** - * 获取 Refresh-Token 列表:此应用下 对 某个用户 签发的所有 Refresh-token + * 获取 Refresh-Token 索引列表(获取之前会完整调整索引列表,保证获取的都是有效 RefreshToken 索引) * * @param clientId 应用id * @param loginId 账号id * @return / */ - public List getRefreshTokenValueList(String clientId, Object loginId) { + public Map getRefreshTokenIndexMap_FromAdjustAfter(String clientId, Object loginId) { SaSession session = getRawSessionByRefreshToken(clientId, loginId, false); - return getTokenValueList(session, REFRESH_TOKEN_MAP); + return getTokenIndexMap_FromAdjustAfter(session, REFRESH_TOKEN_MAP); + } + + /** + * 获取 Refresh-Token 列表(获取之前会完整调整索引列表,保证获取的都是有效 RefreshToken) + * + * @param clientId 应用id + * @param loginId 账号id + * @return / + */ + public List getRefreshTokenValueList_FromAdjustAfter(String clientId, Object loginId) { + SaSession session = getRawSessionByRefreshToken(clientId, loginId, false); + return getTokenValueList_FromAdjustAfter(session, REFRESH_TOKEN_MAP); } - // ------------------- client_token Model + // ------------------- Client-Token Model /** - * 持久化:ClientToken-Model + * 保存:ClientTokenModel * @param ct . */ public void saveClientToken(ClientTokenModel ct) { @@ -495,7 +537,7 @@ public class SaOAuth2Dao { } /** - * 删除:Client-Token + * 删除:ClientTokenModel * @param clientToken 值 */ public void deleteClientToken(String clientToken) { @@ -505,9 +547,9 @@ public class SaOAuth2Dao { } /** - * 获取:Client-Token Model - * @param clientToken . - * @return . + * 获取:ClientTokenModel + * @param clientToken / + * @return / */ public ClientTokenModel getClientToken(String clientToken) { if(clientToken == null) { @@ -517,20 +559,20 @@ public class SaOAuth2Dao { } - // ------------------- client_token 索引 + // ------------------- Client-Token 索引 /** - * 持久化:Client-Token 索引 + * 保存:Client-Token 索引 * * @param ct / * @param maxClientTokenCount 允许的最多 Client-Token 数量,超出的将被删除 (-1=不限制) */ - public void saveClientTokenIndex(ClientTokenModel ct, int maxClientTokenCount) { + public void saveClientTokenIndex_AndAdjust(ClientTokenModel ct, int maxClientTokenCount) { if(ct == null) { return; } SaSession session = getRawSessionByClientToken(ct.clientId, true); - addTokenIndex(session, CLIENT_TOKEN_MAP, ct.clientToken, ct.getExpiresIn(), maxClientTokenCount, this::deleteClientToken); + addTokenIndex_AndAdjust(session, CLIENT_TOKEN_MAP, ct.clientToken, ct.getExpiresIn(), maxClientTokenCount, this::deleteClientToken); } /** @@ -538,12 +580,12 @@ public class SaOAuth2Dao { * @param clientId 应用 id * @param clientToken 值 */ - public void deleteClientTokenIndexBySingleData(String clientId, String clientToken) { + public void deleteClientTokenIndex_BySingleData(String clientId, String clientToken) { SaSession session = getRawSessionByClientToken(clientId, false); if(session == null) { return; } - deleteTokenIndex(session, CLIENT_TOKEN_MAP, clientToken); + deleteTokenIndex_AndTryLogout(session, CLIENT_TOKEN_MAP, clientToken); } /** @@ -556,21 +598,33 @@ public class SaOAuth2Dao { } /** - * 获取 Client-Token 列表:此应用下 对 某个用户 签发的所有 Client-token + * 获取 Client-Token 索引列表(获取之前会完整调整索引列表,保证获取的都是有效 ClientToken 索引) + * + * @param clientId 应用id + * @param loginId 账号id + * @return / + */ + public Map getClientTokenIndexMap_FromAdjustAfter(String clientId, Object loginId) { + SaSession session = getRawSessionByClientToken(clientId, false); + return getTokenIndexMap_FromAdjustAfter(session, CLIENT_TOKEN_MAP); + } + + /** + * 获取 Client-Token 列表(获取之前会完整调整索引列表,保证获取的都是有效 ClientToken) * * @param clientId 应用id * @return / */ - public List getClientTokenValueList(String clientId) { + public List getClientTokenValueList_FromAdjustAfter(String clientId) { SaSession session = getRawSessionByClientToken(clientId, false); - return getTokenValueList(session, CLIENT_TOKEN_MAP); + return getTokenValueList_FromAdjustAfter(session, CLIENT_TOKEN_MAP); } // ------------------- GrantScope /** - * 持久化:用户授权记录 + * 保存:用户授权记录 * @param clientId 应用id * @param loginId 账号id * @param scopes 权限列表 @@ -607,7 +661,7 @@ public class SaOAuth2Dao { // ------------------- State /** - * 持久化:state + * 保存:state * @param state / */ public void saveState(String state) { @@ -641,8 +695,8 @@ public class SaOAuth2Dao { // ------------------- 其它 /** - * 持久化:nonce-索引 - * @param c . + * 保存:nonce-索引 + * @param c / */ public void saveCodeNonceIndex(CodeModel c) { if(c == null || SaFoxUtil.isEmpty(c.nonce)) { @@ -667,7 +721,7 @@ public class SaOAuth2Dao { // ------------------- 拼接key /** - * 拼接key:Code持久化 + * 拼接 key:Code 保存 * @param code 授权码 * @return key */ @@ -676,7 +730,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:Code 索引 + * 拼接 key:Code 索引 * @param clientId 应用id * @param loginId 账号id * @return key @@ -686,7 +740,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:Access-Token持久化 + * 拼接 key:Access-Token 保存 * @param accessToken accessToken * @return key */ @@ -695,7 +749,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:Access-Token RSD Value + * 拼接 key:Access-Token RSD Value * @param clientId 应用id * @param loginId 账号id * @return key @@ -705,7 +759,16 @@ public class SaOAuth2Dao { } /** - * 拼接key:Refresh-Token RSD Value + * 拼接 key:Refresh-Token 保存 + * @param refreshToken refreshToken + * @return key + */ + public String splicingRefreshTokenSaveKey(String refreshToken) { + return getSaTokenConfig().getTokenName() + ":oauth2:refresh-token:" + refreshToken; + } + + /** + * 拼接 key:Refresh-Token RSD Value * @param clientId 应用id * @param loginId 账号id * @return key @@ -715,25 +778,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:Client-Token RSD Value - * @param clientId 应用id - * @return key - */ - public String splicingClientTokenRSDValue(String clientId) { - return "client-token:" + clientId; - } - - /** - * 拼接key:Refresh-Token持久化 - * @param refreshToken refreshToken - * @return key - */ - public String splicingRefreshTokenSaveKey(String refreshToken) { - return getSaTokenConfig().getTokenName() + ":oauth2:refresh-token:" + refreshToken; - } - - /** - * 拼接key:Client-Token持久化 + * 拼接 key:Client-Token 保存 * @param clientToken clientToken * @return key */ @@ -742,7 +787,16 @@ public class SaOAuth2Dao { } /** - * 拼接key:用户授权记录 + * 拼接 key:Client-Token RSD Value + * @param clientId 应用id + * @return key + */ + public String splicingClientTokenRSDValue(String clientId) { + return "client-token:" + clientId; + } + + /** + * 拼接 key:用户授权记录 * @param clientId 应用id * @param loginId 账号id * @return key @@ -752,7 +806,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:state 参数持久化 + * 拼接 key:state 参数保存 * @param state / * @return key */ @@ -761,7 +815,7 @@ public class SaOAuth2Dao { } /** - * 拼接key:code-nonce 索引 参数持久化 + * 拼接 key:code-nonce 索引 参数保存 * @param code 授权码 * @return key */ @@ -770,73 +824,6 @@ public class SaOAuth2Dao { } - // -------- 工具方法 - - /** - * 获取一个新的 TokenMap 集合 - * @return / - */ - protected Map newTokenIndexMap() { - return new LinkedHashMap<>(); - } - - /** - * 过期时间转 ttl (秒) 获取最大 ttl 值 - * @param expireTimeList / - * @return / - */ - protected long getMaxTtl(Collection expireTimeList) { - long maxTtl = 0; - for (long expireTime : expireTimeList) { - long ttl = expireTimeToTtl(expireTime); - if(ttl == SaTokenDao.NEVER_EXPIRE) { - maxTtl = SaTokenDao.NEVER_EXPIRE; - break; - } - if(ttl > maxTtl) { - maxTtl = ttl; - } - } - return maxTtl; - } - - /** - * 过期时间转 ttl (秒) - * @param expireTime / - * @return / - */ - protected long expireTimeToTtl(long expireTime) { - if(expireTime == SaTokenDao.NEVER_EXPIRE) { - return SaTokenDao.NEVER_EXPIRE; - } - if(expireTime == SaTokenDao.NOT_VALUE_EXPIRE) { - return SaTokenDao.NOT_VALUE_EXPIRE; - } - // TODO temp-token 模块与 apikey 模块是否也应该修改为这个逻辑 ? - long currentTime = System.currentTimeMillis(); - if(expireTime < currentTime) { - return SaTokenDao.NOT_VALUE_EXPIRE; - } - return (expireTime - currentTime) / 1000; - } - - /** - * ttl (秒) 转 过期时间 - * @param ttl / - * @return / - */ - protected long ttlToExpireTime(long ttl) { - if(ttl == SaTokenDao.NEVER_EXPIRE) { - return SaTokenDao.NEVER_EXPIRE; - } - if(ttl == SaTokenDao.NOT_VALUE_EXPIRE) { - return SaTokenDao.NOT_VALUE_EXPIRE; - } - return ttl * 1000 + System.currentTimeMillis(); - } - - - // -------- bean 对象代理 /** 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 e66097d1..236dcf71 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 @@ -16,8 +16,11 @@ package cn.dev33.satoken.oauth2.data.convert; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; import cn.dev33.satoken.oauth2.data.model.CodeModel; 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 java.util.List; @@ -51,31 +54,58 @@ public interface SaOAuth2DataConverter { List convertRedirectUriStringToList(String redirectUris); /** - * 将 Code 转换为 Access-Token + * 根据 RequestAuthModel 构建一个 CodeModel + * @param ra RequestAuthModel + * @return CodeModel 对象 + */ + CodeModel convertRequestAuthToCode(RequestAuthModel ra); + + /** + * 根据 RequestAuthModel 构建一个 AccessTokenModel + * @param ra RequestAuthModel + * @param accessTokenTimeout Access-Token 有效期 (单位:秒) + * @return AccessTokenModel 对象 + */ + AccessTokenModel convertRequestAuthToAccessToken(RequestAuthModel ra, long accessTokenTimeout); + + /** + * 根据 Code 构建一个 Access-Token * @param cm CodeModel对象 + * @param accessTokenTimeout Access-Token 有效期 (单位:秒) * @return AccessToken对象 */ - AccessTokenModel convertCodeToAccessToken(CodeModel cm); + AccessTokenModel convertCodeToAccessToken(CodeModel cm, long accessTokenTimeout); /** - * 将 Access-Token 转换为 Refresh-Token + * 根据 Access-Token 构建一个 Refresh-Token * @param at / + * @param refreshTokenTimeout Refresh-Token 有效期 (单位:秒) * @return / */ - RefreshTokenModel convertAccessTokenToRefreshToken(AccessTokenModel at); + RefreshTokenModel convertAccessTokenToRefreshToken(AccessTokenModel at, long refreshTokenTimeout); /** - * 将 Refresh-Token 转换为 Access-Token + * 根据 Refresh-Token 构建一个 Access-Token * @param rt / + * @param accessTokenTimeout Access-Token 有效期 (单位:秒) * @return / */ - AccessTokenModel convertRefreshTokenToAccessToken(RefreshTokenModel rt); + AccessTokenModel convertRefreshTokenToAccessToken(RefreshTokenModel rt, long accessTokenTimeout); /** - * 根据 Refresh-Token 创建一个新的 Refresh-Token + * 根据 Refresh-Token 构建一个新的 Refresh-Token * @param rt / + * @param refreshTokenTimeout Refresh-Token 有效期 (单位:秒) * @return / */ - RefreshTokenModel convertRefreshTokenToRefreshToken(RefreshTokenModel rt); + RefreshTokenModel convertRefreshTokenToRefreshToken(RefreshTokenModel rt, long refreshTokenTimeout); + + /** + * 根据 SaClientModel 构建一个 ClientTokenModel + * @param clientModel / + * @param scopes 权限列表 + * @return / + */ + ClientTokenModel convertSaClientToClientToken(SaClientModel clientModel, List scopes); } 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 0baac953..23e5b71d 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 @@ -15,15 +15,17 @@ */ package cn.dev33.satoken.oauth2.data.convert; -import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.consts.GrantType; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; import cn.dev33.satoken.oauth2.data.model.CodeModel; 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.strategy.SaOAuth2Strategy; import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaTtlMethods; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -35,7 +37,7 @@ import java.util.List; * @author click33 * @since 1.39.0 */ -public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { +public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter, SaTtlMethods { /** * 转换 scope 数据格式:String -> List @@ -72,10 +74,47 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { } /** - * 将 Code 转换为 Access-Token + * 根据 RequestAuthModel 构建一个 CodeModel + * @param ra RequestAuthModel + * @return CodeModel 对象 */ @Override - public AccessTokenModel convertCodeToAccessToken(CodeModel cm) { + public CodeModel convertRequestAuthToCode(RequestAuthModel ra){ + String codeValue = SaOAuth2Strategy.instance.createCodeValue.execute(ra.clientId, ra.loginId, ra.scopes); + CodeModel cm = new CodeModel(); + cm.code = codeValue; + cm.clientId = ra.clientId; + cm.scopes = ra.scopes; + cm.loginId = ra.loginId; + cm.redirectUri = ra.redirectUri; + cm.nonce = ra.getNonce(); + return cm; + } + + /** + * 根据 RequestAuthModel 构建一个 AccessTokenModel + * @param ra RequestAuthModel + * @return AccessTokenModel 对象 + */ + @Override + public AccessTokenModel convertRequestAuthToAccessToken(RequestAuthModel ra, long accessTokenTimeout) { + String newAtValue = SaOAuth2Strategy.instance.createAccessToken.execute(ra.clientId, ra.loginId, ra.scopes); + AccessTokenModel at = new AccessTokenModel(); + at.accessToken = newAtValue; + at.clientId = ra.clientId; + at.loginId = ra.loginId; + at.scopes = ra.scopes; + at.tokenType = SaOAuth2Consts.TokenType.bearer; + at.expiresTime = ttlToExpireTime(accessTokenTimeout); + at.extraData = new LinkedHashMap<>(); + return at; + } + + /** + * 根据 Code 构建一个 Access-Token + */ + @Override + public AccessTokenModel convertCodeToAccessToken(CodeModel cm, long accessTokenTimeout) { AccessTokenModel at = new AccessTokenModel(); at.accessToken = SaOAuth2Strategy.instance.createAccessToken.execute(cm.clientId, cm.loginId, cm.scopes); at.clientId = cm.clientId; @@ -83,40 +122,31 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { at.scopes = cm.scopes; at.tokenType = SaOAuth2Consts.TokenType.bearer; at.grantType = GrantType.authorization_code; - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(cm.clientId); - at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000); + at.expiresTime = ttlToExpireTime(accessTokenTimeout); at.extraData = new LinkedHashMap<>(); return at; } /** - * 将 Access-Token 转换为 Refresh-Token - * @param at . - * @return . + * 根据 Access-Token 构建一个 Refresh-Token */ @Override - public RefreshTokenModel convertAccessTokenToRefreshToken(AccessTokenModel at) { + public RefreshTokenModel convertAccessTokenToRefreshToken(AccessTokenModel at, long refreshTokenTimeout) { RefreshTokenModel rt = new RefreshTokenModel(); rt.refreshToken = SaOAuth2Strategy.instance.createRefreshToken.execute(at.clientId, at.loginId, at.scopes); rt.clientId = at.clientId; rt.loginId = at.loginId; rt.scopes = at.scopes; - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(at.clientId); - rt.expiresTime = System.currentTimeMillis() + (clientModel.getRefreshTokenTimeout() * 1000); + rt.expiresTime = ttlToExpireTime(refreshTokenTimeout); rt.extraData = new LinkedHashMap<>(at.extraData); - // 改变 at 属性 -// at.refreshToken = rt.refreshToken; -// at.refreshExpiresTime = rt.expiresTime; return rt; } /** - * 将 Refresh-Token 转换为 Access-Token - * @param rt . - * @return . + * 根据 Refresh-Token 构建一个 Access-Token */ @Override - public AccessTokenModel convertRefreshTokenToAccessToken(RefreshTokenModel rt) { + public AccessTokenModel convertRefreshTokenToAccessToken(RefreshTokenModel rt, long accessTokenTimeout) { AccessTokenModel at = new AccessTokenModel(); at.accessToken = SaOAuth2Strategy.instance.createAccessToken.execute(rt.clientId, rt.loginId, rt.scopes); at.refreshToken = rt.refreshToken; @@ -126,23 +156,19 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { at.tokenType = SaOAuth2Consts.TokenType.bearer; at.grantType = GrantType.refresh_token; at.extraData = new LinkedHashMap<>(rt.extraData); - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(rt.clientId); - at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000); + at.expiresTime = ttlToExpireTime(accessTokenTimeout); at.refreshExpiresTime = rt.expiresTime; return at; } /** - * 根据 Refresh-Token 创建一个新的 Refresh-Token - * @param rt . - * @return . + * 根据 Refresh-Token 构建一个新的 Refresh-Token */ @Override - public RefreshTokenModel convertRefreshTokenToRefreshToken(RefreshTokenModel rt) { + public RefreshTokenModel convertRefreshTokenToRefreshToken(RefreshTokenModel rt, long refreshTokenTimeout) { RefreshTokenModel newRt = new RefreshTokenModel(); newRt.refreshToken = SaOAuth2Strategy.instance.createRefreshToken.execute(rt.clientId, rt.loginId, rt.scopes); - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(rt.clientId); - newRt.expiresTime = System.currentTimeMillis() + (clientModel.getRefreshTokenTimeout() * 1000); + newRt.expiresTime = ttlToExpireTime(refreshTokenTimeout); newRt.clientId = rt.clientId; newRt.scopes = rt.scopes; newRt.loginId = rt.loginId; @@ -150,5 +176,25 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { return newRt; } + /** + * 根据 SaClientModel 构建一个 ClientTokenModel + * @param clientModel / + * @param scopes 权限列表 + * @return / + */ + @Override + public ClientTokenModel convertSaClientToClientToken(SaClientModel clientModel, List scopes) { + String clientTokenValue = SaOAuth2Strategy.instance.createClientToken.execute(clientModel.getClientId(), scopes); + ClientTokenModel ct = new ClientTokenModel(clientTokenValue, clientModel.getClientId(), scopes); + ct.clientToken = clientTokenValue; + ct.clientId = clientModel.getClientId(); + ct.scopes = scopes; + ct.tokenType = SaOAuth2Consts.TokenType.bearer; + ct.expiresTime = ttlToExpireTime(clientModel.getClientTokenTimeout()); + ct.grantType = GrantType.client_credentials; + ct.extraData = new LinkedHashMap<>(); + return ct; + } + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerate.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerate.java index cdac1eb4..30758323 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerate.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/generate/SaOAuth2DataGenerate.java @@ -32,28 +32,28 @@ import java.util.function.Consumer; public interface SaOAuth2DataGenerate { /** - * 构建Model:Code授权码 + * 构建 Model:Code授权码 * @param ra 请求参数Model * @return 授权码Model */ CodeModel generateCode(RequestAuthModel ra); /** - * 构建Model:Access-Token (根据 code 授权码) + * 构建 Model:Access-Token (根据 code 授权码) * @param code 授权码Model * @return AccessToken Model */ AccessTokenModel generateAccessToken(String code); /** - * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token + * 刷新 Model:根据 Refresh-Token 生成一个新的 Access-Token * @param refreshToken Refresh-Token值 * @return 新的 Access-Token */ AccessTokenModel refreshAccessToken(String refreshToken); /** - * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) + * 构建 Model:Access-Token (根据 RequestAuthModel 构建,用于隐藏式 and 密码式) * @param ra 请求参数Model * @param isCreateRt 是否生成对应的Refresh-Token * @param appendWork 对生成的 AccessTokenModel 进行追加操作 @@ -62,7 +62,7 @@ public interface SaOAuth2DataGenerate { AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt, Consumer appendWork); /** - * 构建Model:Client-Token + * 构建 Model:Client-Token * @param clientId 应用id * @param scopes 授权范围 * @return Client-Token Model @@ -70,7 +70,7 @@ public interface SaOAuth2DataGenerate { ClientTokenModel generateClientToken(String clientId, List scopes); /** - * 构建URL:下放Code URL (Authorization Code 授权码) + * 构建 URL:下放Code URL (Authorization Code 授权码) * @param redirectUri 下放地址 * @param code code参数 * @param state state参数 @@ -79,7 +79,7 @@ public interface SaOAuth2DataGenerate { String buildRedirectUri(String redirectUri, String code, String state); /** - * 构建URL:下放Access-Token URL (implicit 隐藏式) + * 构建 URL:下放Access-Token URL (implicit 隐藏式) * @param redirectUri 下放地址 * @param token token * @param state state参数 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 42a69243..45546d60 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 @@ -16,7 +16,6 @@ package cn.dev33.satoken.oauth2.data.generate; import cn.dev33.satoken.oauth2.SaOAuth2Manager; -import cn.dev33.satoken.oauth2.consts.GrantType; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.dao.SaOAuth2Dao; import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter; @@ -32,8 +31,8 @@ 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; +import cn.dev33.satoken.util.SaTtlMethods; -import java.util.LinkedHashMap; import java.util.List; import java.util.function.Consumer; @@ -43,10 +42,10 @@ import java.util.function.Consumer; * @author click33 * @since 1.39.0 */ -public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { +public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate, SaTtlMethods { /** - * 构建Model:Code授权码 + * 构建 Model:Code授权码 * @param ra 请求参数Model * @return 授权码Model */ @@ -55,18 +54,17 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { SaOAuth2Dao dao = SaOAuth2Manager.getDao(); - // 删除旧Code + // 删除旧 Code dao.deleteCode(dao.getCodeValue(ra.clientId, ra.loginId)); - // 生成新Code - String codeValue = SaOAuth2Strategy.instance.createCodeValue.execute(ra.clientId, ra.loginId, ra.scopes); - CodeModel cm = new CodeModel(codeValue, ra.clientId, ra.scopes, ra.loginId, ra.redirectUri, ra.getNonce()); + // 生成新 Code + CodeModel cm = SaOAuth2Manager.getDataConverter().convertRequestAuthToCode(ra); - // 保存新Code + // 保存新 Code dao.saveCode(cm); dao.saveCodeIndex(cm); - // 保存code-nonce + // 保存 code -> nonce dao.saveCodeNonceIndex(cm); // 返回 @@ -74,7 +72,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { } /** - * 构建Model:Access-Token (根据 code 授权码) + * 构建 Model:Access-Token (根据 code 授权码) * @param code 授权码 * @return AccessToken Model */ @@ -91,30 +89,32 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { // 2、开发者自定义的授权前置检查 SaOAuth2Strategy.instance.userAuthorizeClientCheck.run(cm.loginId, cm.clientId); - // 3、生成token - AccessTokenModel at = dataConverter.convertCodeToAccessToken(cm); + // 3、生成新 Access-Token + SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(cm.clientId); + AccessTokenModel at = dataConverter.convertCodeToAccessToken(cm, clientModel.getAccessTokenTimeout()); SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); - RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at); + + // 4、生成新 Refresh-Token + RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at, clientModel.getRefreshTokenTimeout()); at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; - // 4、保存token - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(cm.clientId); + // 5、保存 Access-Token、Refresh-Token dao.saveAccessToken(at); - dao.saveAccessTokenIndex(at, clientModel.getMaxAccessTokenCount()); + dao.saveAccessTokenIndex_AndAdjust(at, clientModel.getMaxAccessTokenCount()); dao.saveRefreshToken(rt); - dao.saveRefreshTokenIndex(rt, clientModel.getMaxRefreshTokenCount()); + dao.saveRefreshTokenIndex_AndAdjust(rt, clientModel.getMaxRefreshTokenCount()); - // 5、删除此Code + // 6、删除 Code (一个 code 只可以使用一次) dao.deleteCode(code); dao.deleteCodeIndex(cm.clientId, cm.loginId); - // 6、返回 Access-Token + // 7、返回 Access-Token return at; } /** - * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token + * 刷新 Model:根据 Refresh-Token 生成一个新的 Access-Token * @param refreshToken Refresh-Token值 * @return 新的 Access-Token */ @@ -123,42 +123,35 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { SaOAuth2Dao dao = SaOAuth2Manager.getDao(); - // 获取 Refresh-Token 信息 + // 1、获取 Refresh-Token 信息 RefreshTokenModel rt = dao.getRefreshToken(refreshToken); SaOAuth2RefreshTokenException.throwBy(rt == null, "无效 refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30111); - // 开发者自定义的授权前置检查 + // 2、开发者自定义的授权前置检查 SaOAuth2Strategy.instance.userAuthorizeClientCheck.run(rt.loginId, rt.clientId); - // 如果配置了 [每次刷新产生新的Refresh-Token] + // 3、如果配置了 isNewRefresh=true,则生成一个新的 Refresh-Token SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(rt.clientId); if(clientModel.getIsNewRefresh()) { - // 删除旧 Refresh-Token -// dao.deleteRefreshToken(rt.refreshToken); - - // 创建并保存新的 Refresh-Token - rt = SaOAuth2Manager.getDataConverter().convertRefreshTokenToRefreshToken(rt); + rt = SaOAuth2Manager.getDataConverter().convertRefreshTokenToRefreshToken(rt, clientModel.getRefreshTokenTimeout()); dao.saveRefreshToken(rt); - dao.saveRefreshTokenIndex(rt, clientModel.getMaxRefreshTokenCount()); + dao.saveRefreshTokenIndex_AndAdjust(rt, clientModel.getMaxRefreshTokenCount()); } - // 删除旧 Access-Token -// dao.deleteAccessToken(dao.getAccessTokenList(rt.clientId, rt.loginId)); - - // 生成新 Access-Token - AccessTokenModel at = SaOAuth2Manager.getDataConverter().convertRefreshTokenToAccessToken(rt); + // 4、生成新 Access-Token + AccessTokenModel at = SaOAuth2Manager.getDataConverter().convertRefreshTokenToAccessToken(rt, clientModel.getAccessTokenTimeout()); SaOAuth2Strategy.instance.refreshAccessTokenWorkByScope.accept(at); - // 保存新 Access-Token + // 5、保存新 Access-Token dao.saveAccessToken(at); - dao.saveAccessTokenIndex(at, clientModel.getMaxAccessTokenCount()); + dao.saveAccessTokenIndex_AndAdjust(at, clientModel.getMaxAccessTokenCount()); - // 返回新 Access-Token + // 6、返回新 Access-Token return at; } /** - * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) + * 构建 Model:Access-Token (根据 RequestAuthModel 构建,用于隐藏式 and 密码式) * @param ra 请求参数Model * @param isCreateRt 是否生成对应的Refresh-Token * @param appendWork 对生成的 AccessTokenModel 进行追加操作 @@ -169,51 +162,39 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { public AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt, Consumer appendWork) { SaOAuth2Dao dao = SaOAuth2Manager.getDao(); - - // 1、删除 旧Token -// dao.deleteAccessToken(dao.getAccessTokenList(ra.clientId, ra.loginId)); -// if(isCreateRt) { -// dao.deleteRefreshToken(dao.getRefreshTokenValue(ra.clientId, ra.loginId)); -// } + SaOAuth2DataConverter dataConverter = SaOAuth2Manager.getDataConverter(); // 1、开发者自定义的授权前置检查 SaOAuth2Strategy.instance.userAuthorizeClientCheck.run(ra.loginId, ra.clientId); - // 2、生成 新Access-Token - String newAtValue = SaOAuth2Strategy.instance.createAccessToken.execute(ra.clientId, ra.loginId, ra.scopes); - AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scopes); - at.tokenType = SaOAuth2Consts.TokenType.bearer; + // 2、生成 Access-Token + SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(ra.clientId); + AccessTokenModel at = dataConverter.convertRequestAuthToAccessToken(ra, clientModel.getAccessTokenTimeout()); if(appendWork != null) { appendWork.accept(at); } - - // 3、根据权限构建额外参数 - at.extraData = new LinkedHashMap<>(); SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); - SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(ra.clientId); - at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000); - - // 3、生成&保存 Refresh-Token + // 3、生成 & 保存 Refresh-Token if(isCreateRt) { - RefreshTokenModel rt = SaOAuth2Manager.getDataConverter().convertAccessTokenToRefreshToken(at); + RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at, clientModel.getRefreshTokenTimeout()); at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; dao.saveRefreshToken(rt); - dao.saveRefreshTokenIndex(rt, clientModel.getMaxRefreshTokenCount()); + dao.saveRefreshTokenIndex_AndAdjust(rt, clientModel.getMaxRefreshTokenCount()); } - // 5、保存 新Access-Token + // 4、保存 Access-Token dao.saveAccessToken(at); - dao.saveAccessTokenIndex(at, clientModel.getMaxAccessTokenCount()); + dao.saveAccessTokenIndex_AndAdjust(at, clientModel.getMaxAccessTokenCount()); - // 6、返回 新Access-Token + // 5、返回 Access-Token return at; } /** - * 构建Model:Client-Token + * 构建 Model:Client-Token * @param clientId 应用id * @param scopes 授权范围 * @return Client-Token Model @@ -223,39 +204,23 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { SaOAuth2Dao dao = SaOAuth2Manager.getDao(); - // 1、删掉旧 Lower-Client-Token -// dao.deleteClientToken(dao.getLowerClientTokenValue(clientId)); + // 1、如果配置了 Lower-Client-Token 的 ttl ,则需要更新一下 + SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(clientId); - // 2、将旧Client-Token 标记为新 Lower-Client-Token -// ClientTokenModel oldCt = dao.getClientToken(dao.getClientTokenValue(clientId)); -// dao.saveLowerClientTokenIndex(oldCt); - - // 2.5、如果配置了 Lower-Client-Token 的 ttl ,则需要更新一下 - SaClientModel cm = SaOAuth2Manager.getDataLoader().getClientModelNotNull(clientId); -// if(oldCt != null && cm.getLowerClientTokenTimeout() != -1) { -// oldCt.expiresTime = System.currentTimeMillis() + (cm.getLowerClientTokenTimeout() * 1000); -// dao.saveClientToken(oldCt); -// } - - // 3、生成新 Client-Token - String clientTokenValue = SaOAuth2Strategy.instance.createClientToken.execute(clientId, scopes); - ClientTokenModel ct = new ClientTokenModel(clientTokenValue, clientId, scopes); - ct.tokenType = SaOAuth2Consts.TokenType.bearer; - ct.expiresTime = System.currentTimeMillis() + (cm.getClientTokenTimeout() * 1000); - ct.grantType = GrantType.client_credentials; - ct.extraData = new LinkedHashMap<>(); + // 2、生成 Client-Token + ClientTokenModel ct = SaOAuth2Manager.getDataConverter().convertSaClientToClientToken(clientModel, scopes); SaOAuth2Strategy.instance.workClientTokenByScope.accept(ct); - // 3、保存新Client-Token + // 3、保存 Client-Token dao.saveClientToken(ct); - dao.saveClientTokenIndex(ct, cm.getMaxClientTokenCount()); + dao.saveClientTokenIndex_AndAdjust(ct, clientModel.getMaxClientTokenCount()); // 4、返回 return ct; } /** - * 构建URL:下放Code URL (Authorization Code 授权码) + * 构建 URL:下放Code URL (Authorization Code 授权码) * @param redirectUri 下放地址 * @param code code参数 * @param state state参数 @@ -272,7 +237,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { } /** - * 构建URL:下放Access-Token URL (implicit 隐藏式) + * 构建 URL:下放Access-Token URL (implicit 隐藏式) * @param redirectUri 下放地址 * @param token token * @param state state参数 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java index 48a5e424..55f2f749 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java @@ -57,7 +57,7 @@ public interface SaOAuth2DataLoader { } /** - * 根据 ClientId 和 LoginId 获取openid + * 根据 ClientId 和 LoginId 获取 openid * * @param clientId 应用id * @param loginId 账号id 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 c8976e7a..00856441 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 @@ -104,6 +104,28 @@ public class AccessTokenModel implements Serializable { this.scopes = scopes; } + // 额外追加方法 + + /** + * 获取:此 Access-Token 的剩余有效期(秒) + * @return / + */ + public long getExpiresIn() { + long s = (expiresTime - System.currentTimeMillis()) / 1000; + return s < 1 ? -2 : s; + } + + /** + * 获取:此 Refresh-Token 的剩余有效期(秒) + * @return / + */ + public long getRefreshExpiresIn() { + long s = (refreshExpiresTime - System.currentTimeMillis()) / 1000; + return s < 1 ? -2 : s; + } + + + // get set public String getAccessToken() { return accessToken; @@ -221,24 +243,5 @@ public class AccessTokenModel implements Serializable { '}'; } - // 追加只读属性 - - /** - * 获取:此 Access-Token 的剩余有效期(秒) - * @return / - */ - public long getExpiresIn() { - long s = (expiresTime - System.currentTimeMillis()) / 1000; - return s < 1 ? -2 : s; - } - - /** - * 获取:此 Refresh-Token 的剩余有效期(秒) - * @return / - */ - public long getRefreshExpiresIn() { - long s = (refreshExpiresTime - System.currentTimeMillis()) / 1000; - return s < 1 ? -2 : s; - } } 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 d7cb4238..0da0d20f 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 @@ -86,6 +86,20 @@ public class ClientTokenModel implements Serializable { this.scopes = scopes; } + // 额外追加方法 + + /** + * 获取:此 Client-Token 的剩余有效期(秒) + * @return / + */ + public long getExpiresIn() { + long s = (expiresTime - System.currentTimeMillis()) / 1000; + return s < 1 ? -2 : s; + } + + + // get set + public String getClientToken() { return clientToken; } @@ -172,13 +186,4 @@ public class ClientTokenModel implements Serializable { '}'; } - /** - * 获取:此 Client-Token 的剩余有效期(秒) - * @return / - */ - public long getExpiresIn() { - long s = (expiresTime - System.currentTimeMillis()) / 1000; - return s < 1 ? -2 : s; - } - } 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 395fad0c..aa04ba5e 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 @@ -69,13 +69,15 @@ public class CodeModel implements Serializable { public CodeModel() { this.createTime = System.currentTimeMillis(); } + /** * 构建一个 * @param code 授权码 * @param clientId 应用id * @param scopes 请求授权范围 * @param loginId 对应的账号id - * @param redirectUri 重定向地址 + * @param redirectUri 重定向地址 + * @param nonce 随机数 */ public CodeModel(String code, String clientId, List scopes, Object loginId, String redirectUri, String nonce) { this(); 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 893646f9..b9c96853 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 @@ -69,6 +69,20 @@ public class RefreshTokenModel implements Serializable { } + // 额外追加方法 + + /** + * 获取:此 Refresh-Token 的剩余有效期(秒) + * @return / + */ + public long getExpiresIn() { + long s = (expiresTime - System.currentTimeMillis()) / 1000; + return s < 1 ? -2 : s; + } + + + // get set + public String getRefreshToken() { return refreshToken; } @@ -145,13 +159,4 @@ public class RefreshTokenModel implements Serializable { "]"; } - /** - * 获取:此 Refresh-Token 的剩余有效期(秒) - * @return / - */ - public long getExpiresIn() { - long s = (expiresTime - System.currentTimeMillis()) / 1000; - return s < 1 ? -2 : s; - } - } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java index 54b06f9d..a80c2290 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java @@ -15,8 +15,7 @@ */ package cn.dev33.satoken.oauth2.data.model.loader; -import cn.dev33.satoken.oauth2.SaOAuth2Manager; -import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig; +import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; import java.io.Serializable; import java.util.ArrayList; @@ -92,15 +91,7 @@ public class SaClientModel implements Serializable { public SaClientModel() { - SaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig(); - this.isNewRefresh = config.getIsNewRefresh(); - this.accessTokenTimeout = config.getAccessTokenTimeout(); - this.refreshTokenTimeout = config.getRefreshTokenTimeout(); - this.clientTokenTimeout = config.getClientTokenTimeout(); - this.lowerClientTokenTimeout = config.getLowerClientTokenTimeout(); - this.maxAccessTokenCount = config.getMaxAccessTokenCount(); - this.maxRefreshTokenCount = config.getMaxRefreshTokenCount(); - this.maxClientTokenCount = config.getMaxClientTokenCount(); + SaOAuth2Strategy.instance.setSaClientModelDefaultFields.run(this); } public SaClientModel(String clientId, String clientSecret, List contractScopes, List allowRedirectUris) { this(); diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/ClientIdAndSecretModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/ClientIdAndSecretModel.java index 299f55bb..685897cf 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/ClientIdAndSecretModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/ClientIdAndSecretModel.java @@ -84,4 +84,5 @@ public class ClientIdAndSecretModel implements Serializable { ", clientSecret='" + clientSecret + '\'' + '}'; } + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java index c07d3bf5..68bcbb00 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java @@ -15,13 +15,13 @@ */ package cn.dev33.satoken.oauth2.data.model.request; -import java.io.Serializable; -import java.util.List; - import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.util.SaFoxUtil; +import java.io.Serializable; +import java.util.List; + /** * 请求授权参数的 Model * @@ -181,7 +181,7 @@ public class RequestAuthModel implements Serializable { } /** - * 检查此Model参数是否有效 + * 数据自检 * @return 对象自身 */ public RequestAuthModel checkModel() { @@ -200,5 +200,17 @@ public class RequestAuthModel implements Serializable { return this; } - + @Override + public String toString() { + return "RequestAuthModel{" + + "clientId='" + clientId + '\'' + + ", scopes=" + scopes + + ", loginId=" + loginId + + ", redirectUri='" + redirectUri + '\'' + + ", responseType='" + responseType + '\'' + + ", state='" + state + '\'' + + ", nonce='" + nonce + '\'' + + '}'; + } + } 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 37aacbd1..6514b449 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 @@ -66,7 +66,6 @@ public interface SaOAuth2DataResolver { */ RequestAuthModel readRequestAuthModel(SaRequest req, Object loginId); - /** * 构建返回值: 获取 token * @param at token信息 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 73c1c970..596db101 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 @@ -77,7 +77,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { */ @Override public String readAccessToken(SaRequest request) { - // 优先从请求参数中获取 + // 优先从请求参数中获取,可以读取到的话直接返回 String accessToken = request.getParam(Param.access_token); if(SaFoxUtil.isNotEmpty(accessToken)) { return accessToken; @@ -104,7 +104,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { */ @Override public String readClientToken(SaRequest request) { - // 优先从请求参数中获取 + // 优先从请求参数中获取,可以读取到的话直接返回 String clientToken = request.getParam(Param.client_token); if(SaFoxUtil.isNotEmpty(clientToken)) { return clientToken; @@ -172,7 +172,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { Map map = new LinkedHashMap<>(); map.put("token_type", ct.tokenType); map.put("client_token", ct.clientToken); - if(SaOAuth2Manager.getServerConfig().mode4ReturnAccessToken) { + if(SaOAuth2Manager.getServerConfig().getMode4ReturnAccessToken()) { map.put("access_token", ct.clientToken); } map.put("expires_in", ct.getExpiresIn()); diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/AuthorizationCodeGrantTypeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/AuthorizationCodeGrantTypeHandler.java index 00de1e0c..7bc21067 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/AuthorizationCodeGrantTypeHandler.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/AuthorizationCodeGrantTypeHandler.java @@ -17,8 +17,8 @@ package cn.dev33.satoken.oauth2.granttype.handler; import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.oauth2.SaOAuth2Manager; -import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.consts.GrantType; +import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel; @@ -39,9 +39,7 @@ public class AuthorizationCodeGrantTypeHandler implements SaOAuth2GrantTypeHandl @Override public AccessTokenModel getAccessToken(SaRequest req, String clientId, List scopes) { - // 获取参数 ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req); -// String clientId = clientIdAndSecret.clientId; String clientSecret = clientIdAndSecret.clientSecret; String code = req.getParamNotNull(SaOAuth2Consts.Param.code); String redirectUri = req.getParam(SaOAuth2Consts.Param.redirect_uri); @@ -50,8 +48,7 @@ public class AuthorizationCodeGrantTypeHandler implements SaOAuth2GrantTypeHandl SaOAuth2Manager.getTemplate().checkGainTokenParam(code, clientId, clientSecret, redirectUri); // 构建 Access-Token、返回 - AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().generateAccessToken(code); - return accessTokenModel; + return SaOAuth2Manager.getDataGenerate().generateAccessToken(code); } } \ No newline at end of file diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/PasswordGrantTypeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/PasswordGrantTypeHandler.java index 2b800bba..259acc22 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/PasswordGrantTypeHandler.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/PasswordGrantTypeHandler.java @@ -49,20 +49,20 @@ public class PasswordGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterfa String username = req.getParamNotNull(SaOAuth2Consts.Param.username); String password = req.getParamNotNull(SaOAuth2Consts.Param.password); - // 3、调用API 开始登录,如果没能成功登录,则直接退出 + // 2、调用API 开始登录,如果没能成功登录,则直接退出 PasswordAuthResult passwordAuthResult = loginByUsernamePassword(username, password); Object loginId = passwordAuthResult.getLoginId(); if(loginId == null) { throw new SaOAuth2Exception("登录失败").setCode(SaOAuth2ErrorCode.CODE_30161); } - // 4、构建 ra 对象 + // 3、构建 ra 对象 RequestAuthModel ra = new RequestAuthModel(); ra.clientId = clientId; ra.loginId = loginId; ra.scopes = scopes; - // 5、生成 Access-Token + // 4、生成 Access-Token AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true, atm -> atm.grantType = GrantType.password); return at; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/SaOAuth2GrantTypeHandlerInterface.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/SaOAuth2GrantTypeHandlerInterface.java index 48d47a19..4cbafba4 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/SaOAuth2GrantTypeHandlerInterface.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/granttype/handler/SaOAuth2GrantTypeHandlerInterface.java @@ -21,7 +21,7 @@ import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import java.util.List; /** - * 所有 OAuth2 GrantType 处理器的父接口 + * 所有 OAuth2 GrantType 处理器的父接口,如果要自定义 GrantType 处理器,必须实现此接口 * * @author click33 * @since 1.39.0 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 8e4e5566..15ceb87a 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 @@ -182,8 +182,12 @@ public class SaOAuth2ServerProcessor { */ public Object refresh() { SaRequest req = SaHolder.getRequest(); + + // 校验 grant_type 必须为 refresh_token String grantType = req.getParamNotNull(Param.grant_type); SaOAuth2Exception.throwBy(!grantType.equals(GrantType.refresh_token), "无效 grant_type:" + grantType, SaOAuth2ErrorCode.CODE_30126); + + // 刷新 Access-Token AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(req); return SaOAuth2Manager.getDataResolver().buildRefreshTokenReturnValue(accessTokenModel); } @@ -205,7 +209,7 @@ public class SaOAuth2ServerProcessor { // 如果 Access-Token 不存在,直接返回 if(oauth2Template.getAccessToken(accessToken) == null) { - return SaResult.ok("access_token不存在:" + accessToken); + return SaResult.ok("access_token 不存在:" + accessToken); } // 校验参数 @@ -223,9 +227,7 @@ public class SaOAuth2ServerProcessor { * @return 处理结果 */ public Object doLogin() { - // 获取变量 SaRequest req = SaHolder.getRequest(); - return SaOAuth2Strategy.instance.doLoginHandle.apply(req.getParam(Param.name), req.getParam(Param.pwd)); } @@ -262,10 +264,10 @@ public class SaOAuth2ServerProcessor { // -------- 情况2:需要返回最终的 redirect_uri 地址 - // s3、构建请求 Model + // 构建请求 Model RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, loginId); - // 7、判断授权类型,构建不同的重定向地址 + // 判断授权类型,构建不同的重定向地址 // 如果是 授权码式,则:开始重定向授权,下放code if(ResponseType.code.equals(ra.responseType)) { CodeModel codeModel = dataGenerate.generateCode(ra); @@ -311,7 +313,7 @@ public class SaOAuth2ServerProcessor { String clientSecret = clientIdAndSecret.clientSecret; List scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(req.getParam(Param.scope)); - //校验 ClientScope + // 校验 ClientScope oauth2Template.checkContractScope(clientId, scopes); // 校验 ClientSecret diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/CommonScope.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/CommonScope.java index ac13ba3c..f062beb9 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/CommonScope.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/CommonScope.java @@ -16,7 +16,7 @@ package cn.dev33.satoken.oauth2.scope; /** - * OAuth2 常见权限定义 + * OAuth2 常见 Scope 定义 * * @author click33 * @since 1.39.0 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OidcScopeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OidcScopeHandler.java index f5108472..a7c4e905 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OidcScopeHandler.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OidcScopeHandler.java @@ -120,8 +120,8 @@ public class OidcScopeHandler implements SaOAuth2ScopeHandlerInterface { public String getNonce() { String nonce = SaHolder.getRequest().getParam(SaOAuth2Consts.Param.nonce); if(SaFoxUtil.isEmpty(nonce)) { - //通过code查找nonce - //为了避免其它handler可能会用到nonce,任由其自然过期,只取用不删除 + // 通过 code 查找nonce + // 为了避免其它 handler 可能会用到 nonce, 任由其自然过期,只取用不删除 nonce = SaOAuth2Manager.getDao().getNonce(SaHolder.getRequest().getParam(SaOAuth2Consts.Param.code)); } if(SaFoxUtil.isEmpty(nonce)) { @@ -136,7 +136,7 @@ public class OidcScopeHandler implements SaOAuth2ScopeHandlerInterface { * @return / */ public IdTokenModel workExtraData(IdTokenModel idToken) { - // + // 留给开发者扩展 return idToken; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeHandlerInterface.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeHandlerInterface.java index 04f8b847..711095c5 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeHandlerInterface.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeHandlerInterface.java @@ -19,7 +19,7 @@ import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; /** - * 所有 OAuth2 权限处理器的父接口 + * 所有 OAuth2 权限处理器的父接口,如果要自定义 Scope 处理器,必须实现此接口 * * @author click33 * @since 1.39.0 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 d6d013d2..0aa33954 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 @@ -16,6 +16,7 @@ package cn.dev33.satoken.oauth2.strategy; import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.fun.SaParamFunction; import cn.dev33.satoken.fun.SaTwoParamFunction; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig; @@ -64,12 +65,12 @@ public final class SaOAuth2Strategy { // ------------------ 权限处理器 ------------------ /** - * 权限处理器集合 + * Scope 权限处理器集合 */ public Map scopeHandlerMap = new LinkedHashMap<>(); /** - * 注册所有默认的权限处理器 + * 注册所有默认的 Scope 权限处理器 */ public void registerDefaultScopeHandler() { scopeHandlerMap.put(CommonScope.OPENID, new OpenIdScopeHandler()); @@ -156,7 +157,7 @@ public final class SaOAuth2Strategy { public Map grantTypeHandlerMap = new LinkedHashMap<>(); /** - * 注册所有默认的权限处理器 + * 注册所有默认的 grant_type 处理器 */ public void registerDefaultGrantTypeHandler() { grantTypeHandlerMap.put(GrantType.authorization_code, new AuthorizationCodeGrantTypeHandler()); @@ -165,7 +166,7 @@ public final class SaOAuth2Strategy { } /** - * 注册一个权限处理器 + * 注册一个 grant_type 处理器 */ public void registerGrantTypeHandler(SaOAuth2GrantTypeHandlerInterface handler) { grantTypeHandlerMap.put(handler.getHandlerGrantType(), handler); @@ -173,7 +174,7 @@ public final class SaOAuth2Strategy { } /** - * 移除一个权限处理器 + * 移除一个 grant_type 处理器 */ public void removeGrantTypeHandler(String scope) { scopeHandlerMap.remove(scope); @@ -183,13 +184,14 @@ public final class SaOAuth2Strategy { * 根据 grantType 构造一个 AccessTokenModel */ public SaOAuth2GrantTypeAuthFunction grantTypeAuth = (req) -> { + // 先校验提供的 grant_type 是否有效 String grantType = req.getParamNotNull(SaOAuth2Consts.Param.grant_type); SaOAuth2GrantTypeHandlerInterface grantTypeHandler = grantTypeHandlerMap.get(grantType); if(grantTypeHandler == null) { throw new SaOAuth2Exception("无效 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); } - // 看看全局是否开启了此 grantType + // 针对 authorization_code 与 password 两种特殊 grant_type,需要判断全局是否开启 SaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig(); if(grantType.equals(GrantType.authorization_code) && !config.getEnableAuthorizationCode() ) { throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); @@ -208,7 +210,7 @@ public final class SaOAuth2Strategy { throw new SaOAuth2Exception("应用未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30141); } - // 调用 处理器 + // 调用 处理器构建 Access-Token return grantTypeHandler.getAccessToken(req, clientIdAndSecretModel.getClientId(), scopes); }; @@ -269,5 +271,20 @@ public final class SaOAuth2Strategy { }; + // ------------------ 其它 ------------------ + + /** + * 在创建 SaClientModel 时,设置其默认字段 + */ + public SaParamFunction setSaClientModelDefaultFields = (clientModel) -> { + SaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig(); + clientModel.accessTokenTimeout = config.getAccessTokenTimeout(); + clientModel.refreshTokenTimeout = config.getRefreshTokenTimeout(); + clientModel.clientTokenTimeout = config.getClientTokenTimeout(); + clientModel.maxAccessTokenCount = config.getMaxAccessTokenCount(); + clientModel.maxRefreshTokenCount = config.getMaxRefreshTokenCount(); + clientModel.maxClientTokenCount = config.getMaxClientTokenCount(); + clientModel.isNewRefresh = config.getIsNewRefresh(); + }; } 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 31891201..a4b5c3e1 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 @@ -37,7 +37,7 @@ import java.util.List; */ public class SaOAuth2Template { - // ----------------- ClientModel 相关 ----------------- + // ----------------- SaClientModel 相关 ----------------- /** * 获取 ClientModel,根据 clientId @@ -65,7 +65,7 @@ public class SaOAuth2Template { } /** - * 校验:clientId 与 clientSecret 是否正确 + * 校验:clientId 与 clientSecret 是否正确,正确返回 SaClientModel,不正确抛出异常 * @param clientId 应用id * @param clientSecret 秘钥 * @return SaClientModel对象 @@ -287,6 +287,15 @@ public class SaOAuth2Template { return !isGrantScope(loginId, clientId, scopes); } + /** + * 删除:指定 loginId 针对指定 Client 的授权信息 + * @param loginId 账号id + * @param clientId 应用id + */ + public void deleteGrantScope(Object loginId, String clientId) { + SaOAuth2Manager.getDao().deleteGrantScope(clientId, loginId); + } + // --------- 请求数据校验相关 @@ -337,10 +346,10 @@ public class SaOAuth2Template { RefreshTokenModel rt = dao.getRefreshToken(refreshToken); SaOAuth2RefreshTokenException.throwBy(rt == null, "无效 refresh_token: " + refreshToken, refreshToken, SaOAuth2ErrorCode.CODE_30111); - // 校验:ClientId是否一致 + // 校验:ClientId 是否一致 SaOAuth2ClientModelException.throwBy( ! rt.clientId.equals(clientId), "无效 client_id: " + clientId, clientId, SaOAuth2ErrorCode.CODE_30122); - // 校验:Secret是否正确 + // 校验:Secret 是否正确 String dbSecret = checkClientModel(clientId).clientSecret; SaOAuth2ClientModelException.throwBy(dbSecret == null || ! dbSecret.equals(clientSecret), "无效 client_secret: " + clientSecret, clientId, SaOAuth2ErrorCode.CODE_30115); @@ -435,7 +444,7 @@ public class SaOAuth2Template { * @return / */ public List getAccessTokenValueList(String clientId, Object loginId) { - return SaOAuth2Manager.getDao().getAccessTokenValueList(clientId, loginId); + return SaOAuth2Manager.getDao().getAccessTokenValueList_FromAdjustAfter(clientId, loginId); } /** @@ -503,7 +512,7 @@ public class SaOAuth2Template { // 删 at、索引 SaOAuth2Dao dao = SaOAuth2Manager.getDao(); dao.deleteAccessToken(accessToken); - dao.deleteAccessTokenIndexBySingleData(at.clientId, at.loginId, accessToken); + dao.deleteAccessTokenIndex_BySingleData(at.clientId, at.loginId, accessToken); } /** @@ -560,7 +569,7 @@ public class SaOAuth2Template { * @return / */ public List getRefreshTokenValueList(String clientId, Object loginId) { - return SaOAuth2Manager.getDao().getRefreshTokenValueList(clientId, loginId); + return SaOAuth2Manager.getDao().getRefreshTokenValueList_FromAdjustAfter(clientId, loginId); } /** @@ -577,7 +586,7 @@ public class SaOAuth2Template { // 删 rt、索引 SaOAuth2Dao dao = SaOAuth2Manager.getDao(); dao.deleteRefreshToken(refreshToken); - dao.deleteRefreshTokenIndexBySingleData(rt.clientId, rt.loginId, refreshToken); + dao.deleteRefreshTokenIndex_BySingleData(rt.clientId, rt.loginId, refreshToken); } /** @@ -643,7 +652,7 @@ public class SaOAuth2Template { * @return / */ public List getClientTokenValueList(String clientId) { - return SaOAuth2Manager.getDao().getClientTokenValueList(clientId); + return SaOAuth2Manager.getDao().getClientTokenValueList_FromAdjustAfter(clientId); } /** @@ -693,7 +702,7 @@ public class SaOAuth2Template { // 删 ct、删索引 SaOAuth2Dao dao = SaOAuth2Manager.getDao(); dao.deleteClientToken(clientToken); - dao.deleteClientTokenIndexBySingleData(ct.clientId, clientToken); + dao.deleteClientTokenIndex_BySingleData(ct.clientId, clientToken); } /** 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 ffcbf239..61ad461e 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 @@ -128,6 +128,15 @@ public class SaOAuth2Util { return SaOAuth2Manager.getTemplate().isGrantScope(loginId, clientId, scopes); } + /** + * 删除:指定 loginId 针对指定 Client 的授权信息 + * @param loginId 账号id + * @param clientId 应用id + */ + public static void deleteGrantScope(Object loginId, String clientId) { + SaOAuth2Manager.getTemplate().deleteGrantScope(loginId, clientId); + } + // ----------------- Code 相关 -----------------