From c813f2d41f14af517e2822ceb9e987dc4da8d851 Mon Sep 17 00:00:00 2001 From: hefangqiang Date: Tue, 30 Nov 2021 16:19:52 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9A=E5=9B=9E=E6=94=B6accesstoken=20index=20=E5=85=A5?= =?UTF-8?q?=E5=8F=82=E4=BC=A0=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/logic/SaOAuth2Template.java | 596 +++++++++--------- 1 file changed, 298 insertions(+), 298 deletions(-) diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java index dca5f2f2..ddeedc44 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java @@ -17,15 +17,15 @@ import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; /** - * Sa-Token-OAuth2 模块 代码实现 + * Sa-Token-OAuth2 模块 代码实现 * @author kong * */ public class SaOAuth2Template { - // ------------------- 获取数据 (开发者必须重写的函数) + // ------------------- 获取数据 (开发者必须重写的函数) /** - * 根据id获取Client信息 + * 根据id获取Client信息 * @param clientId 应用id * @return ClientModel */ @@ -33,20 +33,20 @@ public class SaOAuth2Template { return null; } /** - * 根据ClientId 和 LoginId 获取openid - * @param clientId 应用id - * @param loginId 账号id - * @return 此账号在此Client下的openid + * 根据ClientId 和 LoginId 获取openid + * @param clientId 应用id + * @param loginId 账号id + * @return 此账号在此Client下的openid */ public String getOpenid(String clientId, Object loginId) { return null; } - - // ------------------- 资源校验API + + // ------------------- 资源校验API /** - * 根据id获取Client信息, 如果Client为空,则抛出异常 + * 根据id获取Client信息, 如果Client为空,则抛出异常 * @param clientId 应用id - * @return ClientModel + * @return ClientModel */ public SaClientModel checkClientModel(String clientId) { SaClientModel clientModel = getClientModel(clientId); @@ -56,8 +56,8 @@ public class SaOAuth2Template { return clientModel; } /** - * 获取 Access-Token,如果AccessToken为空则抛出异常 - * @param accessToken . + * 获取 Access-Token,如果AccessToken为空则抛出异常 + * @param accessToken . * @return . */ public AccessTokenModel checkAccessToken(String accessToken) { @@ -66,8 +66,8 @@ public class SaOAuth2Template { return at; } /** - * 获取 Client-Token,如果ClientToken为空则抛出异常 - * @param clientToken . + * 获取 Client-Token,如果ClientToken为空则抛出异常 + * @param clientToken . * @return . */ public ClientTokenModel checkClientToken(String clientToken) { @@ -76,17 +76,17 @@ public class SaOAuth2Template { return ct; } /** - * 获取 Access-Token 所代表的LoginId - * @param accessToken Access-Token - * @return LoginId + * 获取 Access-Token 所代表的LoginId + * @param accessToken Access-Token + * @return LoginId */ public Object getLoginIdByAccessToken(String accessToken) { return checkAccessToken(accessToken).loginId; } /** - * 校验:指定 Access-Token 是否具有指定 Scope + * 校验:指定 Access-Token 是否具有指定 Scope * @param accessToken Access-Token - * @param scopes 需要校验的权限列表 + * @param scopes 需要校验的权限列表 */ public void checkScope(String accessToken, String... scopes) { if(scopes == null || scopes.length == 0) { @@ -98,13 +98,13 @@ public class SaOAuth2Template { SaOAuth2Exception.throwBy(scopeList.contains(scope) == false, "该 Access-Token 不具备 Scope:" + scope); } } - - // ------------------- generate 构建数据 + + // ------------------- generate 构建数据 /** - * 构建Model:请求Model - * @param req SaRequest对象 - * @param loginId 账号id - * @return RequestAuthModel对象 + * 构建Model:请求Model + * @param req SaRequest对象 + * @param loginId 账号id + * @return RequestAuthModel对象 */ public RequestAuthModel generateRequestAuth(SaRequest req, Object loginId) { RequestAuthModel ra = new RequestAuthModel(); @@ -117,210 +117,210 @@ public class SaOAuth2Template { return ra; } /** - * 构建Model:Code授权码 - * @param ra 请求参数Model + * 构建Model:Code授权码 + * @param ra 请求参数Model * @return 授权码Model */ public CodeModel generateCode(RequestAuthModel ra) { - - // 删除旧Code + + // 删除旧Code deleteCode(getCodeValue(ra.clientId, ra.loginId)); - // 生成新Code + // 生成新Code String code = randomCode(ra.clientId, ra.loginId, ra.scope); CodeModel cm = new CodeModel(code, ra.clientId, ra.scope, ra.loginId, ra.redirectUri); - - // 保存新Code + + // 保存新Code saveCode(cm); saveCodeIndex(cm); - - // 返回 + + // 返回 return cm; } /** - * 构建Model:Access-Token + * 构建Model:Access-Token * @param code 授权码Model * @return AccessToken Model */ public AccessTokenModel generateAccessToken(String code) { - // 1、先校验 + // 1、先校验 CodeModel cm = getCode(code); SaOAuth2Exception.throwBy(cm == null, "无效code"); - - // 2、删除旧Token + + // 2、删除旧Token deleteAccessToken(getAccessTokenValue(cm.clientId, cm.loginId)); deleteRefreshToken(getRefreshTokenValue(cm.clientId, cm.loginId)); - - // 3、生成token - AccessTokenModel at = converCodeToAccessToken(cm); + + // 3、生成token + AccessTokenModel at = converCodeToAccessToken(cm); RefreshTokenModel rt = converAccessTokenToRefreshToken(at); at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; - // 4、保存token + // 4、保存token saveAccessToken(at); saveAccessTokenIndex(at); saveRefreshToken(rt); saveRefreshTokenIndex(rt); - - // 5、删除此Code + + // 5、删除此Code deleteCode(code); deleteCodeIndex(cm.clientId, cm.loginId); - + // 6、返回 Access-Token return at; } /** - * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token - * @param refreshToken Refresh-Token值 - * @return 新的 Access-Token + * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token + * @param refreshToken Refresh-Token值 + * @return 新的 Access-Token */ public AccessTokenModel refreshAccessToken(String refreshToken) { - - // 获取 Refresh-Token 信息 + + // 获取 Refresh-Token 信息 RefreshTokenModel rt = getRefreshToken(refreshToken); - SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken); - + SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken); + // 如果配置了[每次刷新产生新的Refresh-Token] if(SaOAuth2Manager.getConfig().getIsNewRefresh()) { - // 删除旧 Refresh-Token + // 删除旧 Refresh-Token deleteRefreshToken(rt.refreshToken); - - // 创建并保持新的 Refresh-Token + + // 创建并保持新的 Refresh-Token rt = converRefreshTokenToRefreshToken(rt); saveRefreshToken(rt); saveRefreshTokenIndex(rt); } - - // 删除旧 Access-Token + + // 删除旧 Access-Token deleteAccessToken(getAccessTokenValue(rt.clientId, rt.loginId)); - - // 生成新 Access-Token + + // 生成新 Access-Token AccessTokenModel at = converRefreshTokenToAccessToken(rt); - - // 保存新 Access-Token + + // 保存新 Access-Token saveAccessToken(at); - saveAccessTokenIndex(at); - - // 返回新 Access-Token + saveAccessTokenIndex(at); + + // 返回新 Access-Token return at; } /** - * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) - * @param ra 请求参数Model - * @param isCreateRt 是否生成对应的Refresh-Token - * @return Access-Token Model + * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) + * @param ra 请求参数Model + * @param isCreateRt 是否生成对应的Refresh-Token + * @return Access-Token Model */ public AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt) { - - // 1、删除 旧Token + + // 1、删除 旧Token deleteAccessToken(getAccessTokenValue(ra.clientId, ra.loginId)); if(isCreateRt) { - deleteRefreshToken(getRefreshTokenValue(ra.clientId, ra.loginId)); + deleteRefreshToken(getRefreshTokenValue(ra.clientId, ra.loginId)); } - // 2、生成 新Access-Token + // 2、生成 新Access-Token String newAtValue = randomAccessToken(ra.clientId, ra.loginId, ra.scope); AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scope); at.openid = getOpenid(ra.clientId, ra.loginId); at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); - // 3、生成&保存 Refresh-Token + // 3、生成&保存 Refresh-Token if(isCreateRt) { RefreshTokenModel rt = converAccessTokenToRefreshToken(at); saveRefreshToken(rt); saveRefreshTokenIndex(rt); } - // 5、保存 新Access-Token + // 5、保存 新Access-Token saveAccessToken(at); saveAccessTokenIndex(at); - - // 6、返回 新Access-Token + + // 6、返回 新Access-Token return at; } /** - * 构建Model:Client-Token - * @param clientId 应用id - * @param scope 授权范围 - * @return Client-Token Model + * 构建Model:Client-Token + * @param clientId 应用id + * @param scope 授权范围 + * @return Client-Token Model */ public ClientTokenModel generateClientToken(String clientId, String scope) { - // 1、删掉 Past-Token + // 1、删掉 Past-Token deleteClientToken(getPastTokenValue(clientId)); - - // 2、将Client-Token 标记 Past-Token + + // 2、将Client-Token 标记 Past-Token String ctValue = getClientTokenValue(clientId); - savePastTokenIndex(getClientToken(ctValue)); - - // 3、生成新Token + savePastTokenIndex(getClientToken(ctValue)); + + // 3、生成新Token ClientTokenModel ct = new ClientTokenModel(randomClientToken(clientId, scope), clientId, scope); ct.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getClientTokenTimeout() * 1000); - - // 3、保存新Token + + // 3、保存新Token saveClientToken(ct); - saveClientTokenIndex(ct); - - // 4、返回 + saveClientTokenIndex(ct); + + // 4、返回 return ct; } /** * 构建URL:下放Code URL (Authorization Code 授权码) - * @param redirectUri 下放地址 + * @param redirectUri 下放地址 * @param code code参数 - * @param state state参数 - * @return 构建完毕的URL + * @param state state参数 + * @return 构建完毕的URL */ public String buildRedirectUri(String redirectUri, String code, String state) { String url = SaFoxUtil.joinParam(redirectUri, Param.code, code); if(SaFoxUtil.isEmpty(state) == false) { - url = SaFoxUtil.joinParam(url, Param.state, state); + url = SaFoxUtil.joinParam(url, Param.state, state); } return url; } /** * 构建URL:下放Access-Token URL (implicit 隐藏式) - * @param redirectUri 下放地址 + * @param redirectUri 下放地址 * @param token token - * @param state state参数 - * @return 构建完毕的URL + * @param state state参数 + * @return 构建完毕的URL */ public String buildImplicitRedirectUri(String redirectUri, String token, String state) { String url = SaFoxUtil.joinSharpParam(redirectUri, Param.token, token); if(SaFoxUtil.isEmpty(state) == false) { - url = SaFoxUtil.joinSharpParam(url, Param.state, state); + url = SaFoxUtil.joinSharpParam(url, Param.state, state); } return url; } /** - * 回收 Access-Token - * @param accessToken Access-Token值 + * 回收 Access-Token + * @param accessToken Access-Token值 */ public void revokeAccessToken(String accessToken) { - - // 如果查不到任何东西, 直接返回 + + // 如果查不到任何东西, 直接返回 AccessTokenModel at = getAccessToken(accessToken); if(at == null) { return; } - - // 删除 Access-Token + + // 删除 Access-Token deleteAccessToken(accessToken); - deleteAccessTokenIndex(at.clientId, at.accessToken); - + deleteAccessTokenIndex(at.clientId, at.loginId); + // 删除对应的 Refresh-Token String refreshToken = getRefreshTokenValue(at.clientId, at.loginId); deleteRefreshToken(refreshToken); deleteRefreshTokenIndex(at.clientId, at.loginId); } - - // ------------------- check 数据校验 + + // ------------------- check 数据校验 /** - * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope - * @param loginId 账号id - * @param clientId 应用id - * @param scope 权限 + * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope + * @param loginId 账号id + * @param clientId 应用id + * @param scope 权限 * @return 是否已经授权 */ public boolean isGrant(Object loginId, String clientId, String scope) { @@ -329,9 +329,9 @@ public class SaOAuth2Template { return scopeList.size() == 0 || grantScopeList.containsAll(scopeList); } /** - * 校验:该Client是否签约了指定的Scope + * 校验:该Client是否签约了指定的Scope * @param clientId 应用id - * @param scope 权限(多个用逗号隔开) + * @param scope 权限(多个用逗号隔开) */ public void checkContract(String clientId, String scope) { List clientScopeList = SaFoxUtil.convertStringToList(checkClientModel(clientId).contractScope); @@ -341,33 +341,33 @@ public class SaOAuth2Template { } } /** - * 校验:该Client使用指定url作为回调地址,是否合法 - * @param clientId 应用id + * 校验:该Client使用指定url作为回调地址,是否合法 + * @param clientId 应用id * @param url 指定url */ public void checkRightUrl(String clientId, String url) { - // 1、是否是一个有效的url + // 1、是否是一个有效的url if(SaFoxUtil.isUrl(url) == false) { throw new SaOAuth2Exception("无效redirect_url:" + url); } - - // 2、截取掉?后面的部分 + + // 2、截取掉?后面的部分 int qIndex = url.indexOf("?"); if(qIndex != -1) { url = url.substring(0, qIndex); } - - // 3、是否在[允许地址列表]之中 - List allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl); + + // 3、是否在[允许地址列表]之中 + List allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl); if(SaStrategy.me.hasElement.apply(allowList, url) == false) { throw new SaOAuth2Exception("非法redirect_url:" + url); } } /** * 校验:clientId 与 clientSecret 是否正确 - * @param clientId 应用id - * @param clientSecret 秘钥 - * @return SaClientModel对象 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @return SaClientModel对象 */ public SaClientModel checkClientSecret(String clientId, String clientSecret) { SaClientModel cm = checkClientModel(clientId); @@ -375,63 +375,63 @@ public class SaOAuth2Template { return cm; } /** - * 校验:使用 code 获取 token 时提供的参数校验 + * 校验:使用 code 获取 token 时提供的参数校验 * @param code 授权码 - * @param clientId 应用id - * @param clientSecret 秘钥 - * @param redirectUri 重定向地址 - * @return CodeModel对象 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @param redirectUri 重定向地址 + * @return CodeModel对象 */ public CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) { - - // 校验:Code是否存在 + + // 校验:Code是否存在 CodeModel cm = getCode(code); SaOAuth2Exception.throwBy(cm == null, "无效code: " + code); - // 校验:ClientId是否一致 + // 校验:ClientId是否一致 SaOAuth2Exception.throwBy(cm.clientId.equals(clientId) == false, "无效client_id: " + clientId); - - // 校验:Secret是否正确 + + // 校验:Secret是否正确 String dbSecret = checkClientModel(clientId).clientSecret; SaOAuth2Exception.throwBy(dbSecret == null || dbSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret); - - // 如果提供了redirectUri,则校验其是否与请求Code时提供的一致 + + // 如果提供了redirectUri,则校验其是否与请求Code时提供的一致 if(SaFoxUtil.isEmpty(redirectUri) == false) { SaOAuth2Exception.throwBy(redirectUri.equals(cm.redirectUri) == false, "无效redirect_uri: " + redirectUri); } - - // 返回CodeMdoel + + // 返回CodeMdoel return cm; } /** - * 校验:使用 Refresh-Token 刷新 Access-Token 时提供的参数校验 - * @param clientId 应用id - * @param clientSecret 秘钥 + * 校验:使用 Refresh-Token 刷新 Access-Token 时提供的参数校验 + * @param clientId 应用id + * @param clientSecret 秘钥 * @param refreshToken Refresh-Token - * @return CodeModel对象 + * @return CodeModel对象 */ public RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) { - - // 校验:Refresh-Token是否存在 + + // 校验:Refresh-Token是否存在 RefreshTokenModel rt = getRefreshToken(refreshToken); SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken); - // 校验:ClientId是否一致 + // 校验:ClientId是否一致 SaOAuth2Exception.throwBy(rt.clientId.equals(clientId) == false, "无效client_id: " + clientId); - - // 校验:Secret是否正确 + + // 校验:Secret是否正确 String dbSecret = checkClientModel(clientId).clientSecret; SaOAuth2Exception.throwBy(dbSecret == null || dbSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret); - - // 返回Refresh-Token + + // 返回Refresh-Token return rt; } /** - * 校验:Access-Token、clientId、clientSecret 三者是否匹配成功 - * @param clientId 应用id - * @param clientSecret 秘钥 - * @param accessToken Access-Token - * @return SaClientModel对象 + * 校验:Access-Token、clientId、clientSecret 三者是否匹配成功 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @param accessToken Access-Token + * @return SaClientModel对象 */ public AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) { AccessTokenModel at = checkAccessToken(accessToken); @@ -439,16 +439,16 @@ public class SaOAuth2Template { checkClientSecret(clientId, clientSecret); return at; } - - // ------------------- conver 数据转换 + + // ------------------- conver 数据转换 /** - * 将 Code 转换为 Access-Token + * 将 Code 转换为 Access-Token * @param cm CodeModel对象 - * @return AccessToken对象 + * @return AccessToken对象 */ public AccessTokenModel converCodeToAccessToken(CodeModel cm) { AccessTokenModel at = new AccessTokenModel(); - at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scope); + at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scope); // at.refreshToken = randomRefreshToken(cm.clientId, cm.loginId, cm.scope); at.clientId = cm.clientId; at.loginId = cm.loginId; @@ -459,7 +459,7 @@ public class SaOAuth2Template { return at; } /** - * 将 Access-Token 转换为 Refresh-Token + * 将 Access-Token 转换为 Refresh-Token * @param at . * @return . */ @@ -471,13 +471,13 @@ public class SaOAuth2Template { rt.scope = at.scope; rt.openid = at.openid; rt.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); - // 改变at属性 + // 改变at属性 at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; return rt; } /** - * 将 Refresh-Token 转换为 Access-Token + * 将 Refresh-Token 转换为 Access-Token * @param rt . * @return . */ @@ -494,7 +494,7 @@ public class SaOAuth2Template { return at; } /** - * 根据 Refresh-Token 创建一个新的 Refresh-Token + * 根据 Refresh-Token 创建一个新的 Refresh-Token * @param rt . * @return . */ @@ -509,10 +509,10 @@ public class SaOAuth2Template { return newRt; } - // ------------------- save 数据 + // ------------------- save 数据 /** - * 持久化:Code-Model - * @param c . + * 持久化:Code-Model + * @param c . */ public void saveCode(CodeModel c) { if(c == null) { @@ -521,8 +521,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().setObject(splicingCodeSaveKey(c.code), c, SaOAuth2Manager.getConfig().getCodeTimeout()); } /** - * 持久化:Code-索引 - * @param c . + * 持久化:Code-索引 + * @param c . */ public void saveCodeIndex(CodeModel c) { if(c == null) { @@ -531,8 +531,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingCodeIndexKey(c.clientId, c.loginId), c.code, SaOAuth2Manager.getConfig().getCodeTimeout()); } /** - * 持久化:AccessToken-Model - * @param at . + * 持久化:AccessToken-Model + * @param at . */ public void saveAccessToken(AccessTokenModel at) { if(at == null) { @@ -541,8 +541,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().setObject(splicingAccessTokenSaveKey(at.accessToken), at, at.getExpiresIn()); } /** - * 持久化:AccessToken-索引 - * @param at . + * 持久化:AccessToken-索引 + * @param at . */ public void saveAccessTokenIndex(AccessTokenModel at) { if(at == null) { @@ -551,8 +551,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingAccessTokenIndexKey(at.clientId, at.loginId), at.accessToken, at.getExpiresIn()); } /** - * 持久化:RefreshToken-Model - * @param rt . + * 持久化:RefreshToken-Model + * @param rt . */ public void saveRefreshToken(RefreshTokenModel rt) { if(rt == null) { @@ -561,8 +561,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().setObject(splicingRefreshTokenSaveKey(rt.refreshToken), rt, rt.getExpiresIn()); } /** - * 持久化:RefreshToken-索引 - * @param rt . + * 持久化:RefreshToken-索引 + * @param rt . */ public void saveRefreshTokenIndex(RefreshTokenModel rt) { if(rt == null) { @@ -571,8 +571,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingRefreshTokenIndexKey(rt.clientId, rt.loginId), rt.refreshToken, rt.getExpiresIn()); } /** - * 持久化:ClientToken-Model - * @param ct . + * 持久化:ClientToken-Model + * @param ct . */ public void saveClientToken(ClientTokenModel ct) { if(ct == null) { @@ -581,8 +581,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().setObject(splicingClientTokenSaveKey(ct.clientToken), ct, ct.getExpiresIn()); } /** - * 持久化:ClientToken-索引 - * @param ct . + * 持久化:ClientToken-索引 + * @param ct . */ public void saveClientTokenIndex(ClientTokenModel ct) { if(ct == null) { @@ -591,8 +591,8 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingClientTokenIndexKey(ct.clientId), ct.clientToken, ct.getExpiresIn()); } /** - * 持久化:Past-Token-索引 - * @param ct . + * 持久化:Past-Token-索引 + * @param ct . */ public void savePastTokenIndex(ClientTokenModel ct) { if(ct == null) { @@ -601,10 +601,10 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingPastTokenIndexKey(ct.clientId), ct.clientToken, ct.getExpiresIn()); } /** - * 持久化:用户授权记录 - * @param clientId 应用id - * @param loginId 账号id - * @param scope 权限列表(多个逗号隔开) + * 持久化:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限列表(多个逗号隔开) */ public void saveGrantScope(String clientId, Object loginId, String scope) { if(SaFoxUtil.isEmpty(scope) == false) { @@ -612,10 +612,10 @@ public class SaOAuth2Template { SaManager.getSaTokenDao().set(splicingGrantScopeKey(clientId, loginId), scope, ttl); } } - - // ------------------- get 数据 + + // ------------------- get 数据 /** - * 获取:Code Model + * 获取:Code Model * @param code . * @return . */ @@ -626,17 +626,17 @@ public class SaOAuth2Template { return (CodeModel)SaManager.getSaTokenDao().getObject(splicingCodeSaveKey(code)); } /** - * 获取:Code Value - * @param clientId 应用id - * @param loginId 账号id + * 获取:Code Value + * @param clientId 应用id + * @param loginId 账号id * @return . */ public String getCodeValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingCodeIndexKey(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingCodeIndexKey(clientId, loginId)); } /** - * 获取:Access-Token Model - * @param accessToken . + * 获取:Access-Token Model + * @param accessToken . * @return . */ public AccessTokenModel getAccessToken(String accessToken) { @@ -646,18 +646,18 @@ public class SaOAuth2Template { return (AccessTokenModel)SaManager.getSaTokenDao().getObject(splicingAccessTokenSaveKey(accessToken)); } /** - * 获取:Access-Token Value - * @param clientId 应用id - * @param loginId 账号id + * 获取:Access-Token Value + * @param clientId 应用id + * @param loginId 账号id * @return . */ public String getAccessTokenValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingAccessTokenIndexKey(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingAccessTokenIndexKey(clientId, loginId)); } /** - * 获取:Refresh-Token Model - * @param refreshToken . - * @return . + * 获取:Refresh-Token Model + * @param refreshToken . + * @return . */ public RefreshTokenModel getRefreshToken(String refreshToken) { if(refreshToken == null) { @@ -666,17 +666,17 @@ public class SaOAuth2Template { return (RefreshTokenModel)SaManager.getSaTokenDao().getObject(splicingRefreshTokenSaveKey(refreshToken)); } /** - * 获取:Refresh-Token Value - * @param clientId 应用id - * @param loginId 账号id + * 获取:Refresh-Token Value + * @param clientId 应用id + * @param loginId 账号id * @return . */ public String getRefreshTokenValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingRefreshTokenIndexKey(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingRefreshTokenIndexKey(clientId, loginId)); } /** - * 获取:Client-Token Model - * @param clientToken . + * 获取:Client-Token Model + * @param clientToken . * @return . */ public ClientTokenModel getClientToken(String clientToken) { @@ -686,35 +686,35 @@ public class SaOAuth2Template { return (ClientTokenModel)SaManager.getSaTokenDao().getObject(splicingClientTokenSaveKey(clientToken)); } /** - * 获取:Client-Token Value - * @param clientId 应用id + * 获取:Client-Token Value + * @param clientId 应用id * @return . */ public String getClientTokenValue(String clientId) { - return SaManager.getSaTokenDao().get(splicingClientTokenIndexKey(clientId)); + return SaManager.getSaTokenDao().get(splicingClientTokenIndexKey(clientId)); } /** - * 获取:Past-Token Value - * @param clientId 应用id + * 获取:Past-Token Value + * @param clientId 应用id * @return . */ public String getPastTokenValue(String clientId) { - return SaManager.getSaTokenDao().get(splicingPastTokenIndexKey(clientId)); + return SaManager.getSaTokenDao().get(splicingPastTokenIndexKey(clientId)); } /** - * 获取:用户授权记录 - * @param clientId 应用id - * @param loginId 账号id - * @return 权限 + * 获取:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @return 权限 */ public String getGrantScope(String clientId, Object loginId) { return SaManager.getSaTokenDao().get(splicingGrantScopeKey(clientId, loginId)); } - - // ------------------- delete数据 + + // ------------------- delete数据 /** - * 删除:Code - * @param code 值 + * 删除:Code + * @param code 值 */ public void deleteCode(String code) { if(code != null) { @@ -722,16 +722,16 @@ public class SaOAuth2Template { } } /** - * 删除:Code索引 - * @param clientId 应用id - * @param loginId 账号id + * 删除:Code索引 + * @param clientId 应用id + * @param loginId 账号id */ public void deleteCodeIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingCodeIndexKey(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingCodeIndexKey(clientId, loginId)); } /** - * 删除:Access-Token - * @param accessToken 值 + * 删除:Access-Token + * @param accessToken 值 */ public void deleteAccessToken(String accessToken) { if(accessToken != null) { @@ -739,16 +739,16 @@ public class SaOAuth2Template { } } /** - * 删除:Access-Token索引 - * @param clientId 应用id - * @param loginId 账号id + * 删除:Access-Token索引 + * @param clientId 应用id + * @param loginId 账号id */ public void deleteAccessTokenIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingAccessTokenIndexKey(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingAccessTokenIndexKey(clientId, loginId)); } /** - * 删除:Refresh-Token - * @param refreshToken 值 + * 删除:Refresh-Token + * @param refreshToken 值 */ public void deleteRefreshToken(String refreshToken) { if(refreshToken != null) { @@ -756,16 +756,16 @@ public class SaOAuth2Template { } } /** - * 删除:Refresh-Token索引 - * @param clientId 应用id - * @param loginId 账号id + * 删除:Refresh-Token索引 + * @param clientId 应用id + * @param loginId 账号id */ public void deleteRefreshTokenIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingRefreshTokenIndexKey(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingRefreshTokenIndexKey(clientId, loginId)); } /** - * 删除:Client-Token - * @param clientToken 值 + * 删除:Client-Token + * @param clientToken 值 */ public void deleteClientToken(String clientToken) { if(clientToken != null) { @@ -773,80 +773,80 @@ public class SaOAuth2Template { } } /** - * 删除:Client-Token索引 - * @param clientId 应用id + * 删除:Client-Token索引 + * @param clientId 应用id */ public void deleteClientTokenIndex(String clientId) { - SaManager.getSaTokenDao().delete(splicingClientTokenIndexKey(clientId)); + SaManager.getSaTokenDao().delete(splicingClientTokenIndexKey(clientId)); } /** - * 删除:Past-Token索引 - * @param clientId 应用id + * 删除:Past-Token索引 + * @param clientId 应用id */ public void deletePastTokenIndex(String clientId) { - SaManager.getSaTokenDao().delete(splicingPastTokenIndexKey(clientId)); + SaManager.getSaTokenDao().delete(splicingPastTokenIndexKey(clientId)); } /** - * 删除:用户授权记录 - * @param clientId 应用id - * @param loginId 账号id + * 删除:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id */ public void deleteGrantScope(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingGrantScopeKey(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingGrantScopeKey(clientId, loginId)); } - - // ------------------- Random数据 + + // ------------------- Random数据 /** - * 随机一个 Code - * @param clientId 应用id - * @param loginId 账号id + * 随机一个 Code + * @param clientId 应用id + * @param loginId 账号id * @param scope 权限 - * @return Code + * @return Code */ public String randomCode(String clientId, Object loginId, String scope) { - return SaFoxUtil.getRandomString(60); + return SaFoxUtil.getRandomString(60); } /** - * 随机一个 Access-Token - * @param clientId 应用id - * @param loginId 账号id + * 随机一个 Access-Token + * @param clientId 应用id + * @param loginId 账号id * @param scope 权限 - * @return Access-Token + * @return Access-Token */ public String randomAccessToken(String clientId, Object loginId, String scope) { return SaFoxUtil.getRandomString(60); } /** - * 随机一个 Refresh-Token - * @param clientId 应用id - * @param loginId 账号id + * 随机一个 Refresh-Token + * @param clientId 应用id + * @param loginId 账号id * @param scope 权限 - * @return Refresh-Token + * @return Refresh-Token */ public String randomRefreshToken(String clientId, Object loginId, String scope) { return SaFoxUtil.getRandomString(60); } /** - * 随机一个 Client-Token - * @param clientId 应用id + * 随机一个 Client-Token + * @param clientId 应用id * @param scope 权限 - * @return Client-Token + * @return Client-Token */ public String randomClientToken(String clientId, String scope) { return SaFoxUtil.getRandomString(60); } - - // ------------------- 拼接key - /** - * 拼接key:Code持久化 + + // ------------------- 拼接key + /** + * 拼接key:Code持久化 * @param code 授权码 * @return key */ public String splicingCodeSaveKey(String code) { return SaManager.getConfig().getTokenName() + ":oauth2:code:" + code; } - /** - * 拼接key:Code索引 + /** + * 拼接key:Code索引 * @param clientId 应用id * @param loginId 账号id * @return key @@ -854,16 +854,16 @@ public class SaOAuth2Template { public String splicingCodeIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:code-index:" + clientId + ":" + loginId; } - /** - * 拼接key:Access-Token持久化 + /** + * 拼接key:Access-Token持久化 * @param accessToken accessToken * @return key */ public String splicingAccessTokenSaveKey(String accessToken) { return SaManager.getConfig().getTokenName() + ":oauth2:access-token:" + accessToken; } - /** - * 拼接key:Access-Token索引 + /** + * 拼接key:Access-Token索引 * @param clientId 应用id * @param loginId 账号id * @return key @@ -871,16 +871,16 @@ public class SaOAuth2Template { public String splicingAccessTokenIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:access-token-index:" + clientId + ":" + loginId; } - /** - * 拼接key:Refresh-Token持久化 + /** + * 拼接key:Refresh-Token持久化 * @param refreshToken refreshToken * @return key */ public String splicingRefreshTokenSaveKey(String refreshToken) { return SaManager.getConfig().getTokenName() + ":oauth2:refresh-token:" + refreshToken; } - /** - * 拼接key:Refresh-Token索引 + /** + * 拼接key:Refresh-Token索引 * @param clientId 应用id * @param loginId 账号id * @return key @@ -888,38 +888,38 @@ public class SaOAuth2Template { public String splicingRefreshTokenIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:refresh-token-index:" + clientId + ":" + loginId; } - /** - * 拼接key:Client-Token持久化 + /** + * 拼接key:Client-Token持久化 * @param clientToken clientToken * @return key */ public String splicingClientTokenSaveKey(String clientToken) { return SaManager.getConfig().getTokenName() + ":oauth2:client-token:" + clientToken; } - /** - * 拼接key:Past-Token 索引 - * @param clientId clientId + /** + * 拼接key:Past-Token 索引 + * @param clientId clientId * @return key */ public String splicingClientTokenIndexKey(String clientId) { return SaManager.getConfig().getTokenName() + ":oauth2:client-token-indedx:" + clientId; } - /** - * 拼接key:Past-Token 索引 - * @param clientId clientId - * @return key + /** + * 拼接key:Past-Token 索引 + * @param clientId clientId + * @return key */ public String splicingPastTokenIndexKey(String clientId) { return SaManager.getConfig().getTokenName() + ":oauth2:past-token-indedx:" + clientId; } - /** - * 拼接key:用户授权记录 + /** + * 拼接key:用户授权记录 * @param clientId 应用id * @param loginId 账号id - * @return key + * @return key */ public String splicingGrantScopeKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:grant-scope:" + clientId + ":" + loginId; } - + } From 8cbb0a8646ccad938704d3ab63a6766ac2bda9c5 Mon Sep 17 00:00:00 2001 From: fxbin Date: Thu, 2 Dec 2021 17:34:16 +0800 Subject: [PATCH 2/7] =?UTF-8?q?jwt=20=E6=89=A9=E5=B1=95=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/dev33/satoken/stp/SaLoginModel.java | 22 ++++++ .../java/cn/dev33/satoken/stp/StpLogic.java | 51 +++++++++++-- .../java/cn/dev33/satoken/stp/StpUtil.java | 19 +++++ .../java/cn/dev33/satoken/jwt/SaJwtUtil.java | 75 ++++++++++++++++++- .../dev33/satoken/jwt/StpLogicJwtForMix.java | 21 ++++++ .../satoken/jwt/StpLogicJwtForStateless.java | 24 +++++- 6 files changed, 205 insertions(+), 7 deletions(-) diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java index f3989153..144c157d 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/SaLoginModel.java @@ -5,6 +5,8 @@ import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.util.SaTokenConsts; +import java.util.Map; + /** * 调用 `StpUtil.login()` 时的 [配置参数 Model ] * @author kong @@ -27,6 +29,11 @@ public class SaLoginModel { */ public Long timeout; + /** + * jwt扩展信息 + */ + public Map expandInfoMap; + /** * @return 参考 {@link #device} @@ -76,6 +83,21 @@ public class SaLoginModel { return this; } + /** + * @return 参考 {@link #expandInfoMap} + */ + public Map getExpandInfoMap() { + return expandInfoMap; + } + + /** + * @param expandInfoMap 参考 {@link #expandInfoMap} + * @return 对象自身 + */ + public SaLoginModel setExpandInfoMap(Map expandInfoMap) { + this.expandInfoMap = expandInfoMap; + return this; + } /** * @return Cookie时长 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 33312b0c..362ccf43 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -1,9 +1,6 @@ package cn.dev33.satoken.stp; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Consumer; import cn.dev33.satoken.SaManager; @@ -94,6 +91,18 @@ public class StpLogic { return SaStrategy.me.createToken.apply(loginId, loginType); } + /** + * 创建一个TokenValue + * @param loginId loginId + * @param device 设备标识 + * @param expandInfoMap 扩展信息 + * @param timeout 过期时间 + * @return 生成的tokenValue + */ + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaStrategy.me.createToken.apply(loginId, loginType); + } + /** * 在当前会话写入当前TokenValue * @param tokenValue token值 @@ -260,6 +269,15 @@ public class StpLogic { login(id, new SaLoginModel().setDevice(device)); } + /** + * 会话登录,并指定扩展信息 for Jwt + * @param id 账号id,建议的类型:(long | int | String) + * @param expandInfoMap 扩展数据 + */ + public void login(Object id, Map expandInfoMap) { + login(id, new SaLoginModel().setExpandInfoMap(expandInfoMap)); + } + /** * 会话登录,并指定是否 [记住我] * @param id 账号id,建议的类型:(long | int | String) @@ -301,7 +319,7 @@ public class StpLogic { } // 如果至此,仍未成功创建tokenValue, 则开始生成一个 if(tokenValue == null) { - tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout()); + tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getExpandInfoMap(), loginModel.getTimeout()); } // ------ 3. 获取 User-Session , 续期 @@ -659,6 +677,18 @@ public class StpLogic { } return getLoginIdNotHandle(tokenValue); } + + /** + * 获取指定Token对应的扩展信息,如果未登录,则返回 null + * @param tokenValue token + * @return 账号id + */ + public Object getExpandInfoByToken(String tokenValue) { + if(tokenValue == null) { + return null; + } + return getExpandInfoNotHandle(tokenValue); + } /** * 获取指定Token对应的账号id (不做任何特殊处理) @@ -668,6 +698,17 @@ public class StpLogic { public String getLoginIdNotHandle(String tokenValue) { return getSaTokenDao().get(splicingKeyTokenValue(tokenValue)); } + + /** + * 获取指定Token对应的扩展信息 (不做任何特殊处理) + * @param tokenValue token值 + * @return 账号id + */ + public Object getExpandInfoNotHandle(String tokenValue) { + return getSaTokenDao().get(splicingKeyTokenValue(tokenValue)); + } + + // ---- 其它操作 /** diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java index 2f46bc22..8c76e64b 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpUtil.java @@ -1,6 +1,7 @@ package cn.dev33.satoken.stp; import java.util.List; +import java.util.Map; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaFunction; @@ -114,6 +115,15 @@ public class StpUtil { stpLogic.login(id, device); } + /** + * 会话登录,并指定登录设备 + * @param id 账号id,建议的类型:(long | int | String) + * @param expandInfoMap 扩展信息 + */ + public static void login(Object id, Map expandInfoMap) { + stpLogic.login(id, expandInfoMap); + } + /** * 会话登录,并指定是否 [记住我] * @param id 账号id,建议的类型:(long | int | String) @@ -286,6 +296,15 @@ public class StpUtil { public static Object getLoginIdByToken(String tokenValue) { return stpLogic.getLoginIdByToken(tokenValue); } + + /** + * 获取指定Token对应的扩展数据,如果未登录,则返回 null + * @param tokenValue token + * @return 账号id + */ + public static Object getExpandInfoByToken(String tokenValue) { + return stpLogic.getExpandInfoByToken(tokenValue); + } // =================== User-Session 相关 =================== diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java index bed4996f..7f19b001 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtUtil.java @@ -4,10 +4,13 @@ import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.util.SaFoxUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.json.JSONObject; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTException; +import java.util.Map; + /** * jwt操作工具类封装 * @author kong @@ -33,7 +36,12 @@ public class SaJwtUtil { /** * key:有效截止期 (时间戳) */ - public static final String EFF = "eff"; + public static final String EFF = "eff"; + + /** + * key: 扩展数据 + */ + public static final String EXPAND = "expand"; /** * 当有效期被设为此值时,代表永不过期 @@ -97,6 +105,44 @@ public class SaJwtUtil { return token; } + /** + * 创建 jwt (全参数方式) + * @param loginType 账号类型 + * @param loginId 账号id + * @param device 设备标识 + * @param expandInfoMap 扩展数据 + * @param timeout token有效期 (单位 秒) + * @param keyt 秘钥 + * @return jwt-token + */ + public static String createToken(String loginType, Object loginId, String device, + Map expandInfoMap, long timeout, String keyt) { + + // 秘钥不可以为空 + SaTokenException.throwByNull(keyt, "请配置jwt秘钥"); + + // 计算有效期 + long effTime = timeout; + if(timeout != NEVER_EXPIRE) { + effTime = timeout * 1000 + System.currentTimeMillis(); + } + + JWT jwt = JWT.create() + .setPayload(LOGIN_TYPE, loginType) + .setPayload(LOGIN_ID, loginId) + .setPayload(DEVICE, device) + .setPayload(EFF, effTime); + + // 设定扩展数据 + if (CollectionUtil.isNotEmpty(expandInfoMap)) { + jwt.setPayload(EXPAND, expandInfoMap); + } + + // 返回 + return jwt.setKey(keyt.getBytes()).sign(); + } + + /** * jwt 解析(校验签名和密码) * @param token Jwt-Token值 @@ -188,6 +234,33 @@ public class SaJwtUtil { } } + /** + * getExpandMap + * + * @since 2021/11/23 5:05 下午 + * @param token token值 + * @param keyt 秘钥 + * @return 值 + */ + public static Object getExpandInfo(String token, String keyt) { + return getPayloads(token, keyt).get(EXPAND); + } + + /** + * 获取 jwt 代表的账号id (未登录时返回null) + * @param token Token值 + * @param keyt 秘钥 + * @return 值 + */ + public static Object getExpandInfoOrNull(String token, String keyt) { + try { + return getPayloads(token, keyt).get(EXPAND); + } catch (NotLoginException e) { + return null; + } + } + + /** * 获取 jwt 剩余有效期 * @param token JwtToken值 diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java index c6db47b0..9c7e5e78 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForMix.java @@ -1,6 +1,7 @@ package cn.dev33.satoken.jwt; import java.util.List; +import java.util.Map; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.dao.SaTokenDao; @@ -54,6 +55,11 @@ public class StpLogicJwtForMix extends StpLogic { return SaJwtUtil.createToken(loginType, loginId, device, timeout, jwtSecretKey()); } + @Override + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaJwtUtil.createToken(loginType, loginId, device, expandInfoMap, timeout, jwtSecretKey()); + } + /** * 获取当前会话的Token信息 * @return token信息 @@ -95,6 +101,21 @@ public class StpLogicJwtForMix extends StpLogic { } } + @Override + public Object getExpandInfoNotHandle(String tokenValue) { + // 先验证 loginType,如果不符,则视为无效token,返回null + String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE); + if(getLoginType().equals(loginType) == false) { + return null; + } + // 获取 expandInfo + try { + return SaJwtUtil.getExpandInfo(tokenValue, jwtSecretKey()); + } catch (NotLoginException e) { + return null; + } + } + /** * 会话注销 */ diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java index cbfb2760..d8d89eff 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java @@ -11,6 +11,8 @@ import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; +import java.util.Map; + /** * Sa-Token 整合 jwt -- stateless 无状态 * @author kong @@ -55,6 +57,11 @@ public class StpLogicJwtForStateless extends StpLogic { return SaJwtUtil.createToken(loginType, loginId, device, timeout, jwtSecretKey()); } + @Override + public String createTokenValue(Object loginId, String device, Map expandInfoMap, long timeout) { + return SaJwtUtil.createToken(loginType, loginId, device, expandInfoMap, timeout, jwtSecretKey()); + } + /** * 获取当前会话的Token信息 * @return token信息 @@ -89,7 +96,7 @@ public class StpLogicJwtForStateless extends StpLogic { loginModel.build(getConfig()); // ------ 2、生成一个token - String tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout()); + String tokenValue = createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getExpandInfoMap(), loginModel.getTimeout()); // 3、在当前会话写入tokenValue setTokenValue(tokenValue, loginModel.getCookieTimeout()); @@ -117,6 +124,21 @@ public class StpLogicJwtForStateless extends StpLogic { } } + @Override + public Object getExpandInfoNotHandle(String tokenValue) { + // 先验证 loginType,如果不符,则视为无效token,返回null + String loginType = SaJwtUtil.getPayloadsNotCheck(tokenValue, jwtSecretKey()).getStr(SaJwtUtil.LOGIN_TYPE); + if(getLoginType().equals(loginType) == false) { + return null; + } + // 获取 expandInfoMap + try { + return SaJwtUtil.getExpandInfo(tokenValue, jwtSecretKey()); + } catch (NotLoginException e) { + return null; + } + } + /** * 会话注销 */ From 03fed93422fab335478843d1a69f894a0fdbb207 Mon Sep 17 00:00:00 2001 From: Lee Date: Thu, 16 Dec 2021 22:14:33 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=B0=86=E6=A3=80=E6=B5=8B=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E7=B1=BB=E5=9E=8B=E4=BA=A4=E7=BB=99client=E5=8E=BB?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E8=80=8C=E9=9D=9E=E5=85=A8=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../satoken/oauth2/config/SaOAuth2Config.java | 8 +++++-- .../satoken/oauth2/logic/SaOAuth2Consts.java | 1 + .../satoken/oauth2/logic/SaOAuth2Handle.java | 11 +++++---- .../oauth2/logic/SaOAuth2Template.java | 20 +++++++++------- .../satoken/oauth2/logic/SaOAuth2Util.java | 10 ++++++++ .../satoken/oauth2/model/SaClientModel.java | 24 ++++++++++++++++++- 6 files changed, 59 insertions(+), 15 deletions(-) diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java index 06ecd72a..15017790 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java @@ -1,11 +1,11 @@ package cn.dev33.satoken.oauth2.config; +import cn.dev33.satoken.util.SaResult; + import java.io.Serializable; import java.util.function.BiFunction; import java.util.function.Supplier; -import cn.dev33.satoken.util.SaResult; - /** * Sa-Token-OAuth2 配置类 Model * @author kong @@ -16,15 +16,19 @@ public class SaOAuth2Config implements Serializable { private static final long serialVersionUID = -6541180061782004705L; /** 是否打开模式:授权码(Authorization Code) */ + @Deprecated public Boolean isCode = true; /** 是否打开模式:隐藏式(Implicit) */ + @Deprecated public Boolean isImplicit = false; /** 是否打开模式:密码式(Password) */ + @Deprecated public Boolean isPassword = false; /** 是否打开模式:凭证式(Client Credentials) */ + @Deprecated public Boolean isClient = false; /** 是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */ diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java index afd8108e..a538be55 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java @@ -59,6 +59,7 @@ public class SaOAuth2Consts { public static String refresh_token = "refresh_token"; public static String password = "password"; public static String client_credentials = "client_credentials"; + public static String implicit = "implicit"; } /** 表示OK的返回结果 */ diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java index d7c5332f..0956bae4 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java @@ -35,10 +35,13 @@ public class SaOAuth2Handle { SaResponse res = SaHolder.getResponse(); SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); + //读取client_id,此参数在所有模式中必填 + String clientId = req.getParamNotNull(Param.client_id); + // ------------------ 路由分发 ------------------ // 模式一:Code授权码 - if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.code) && cfg.isCode) { + if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.code) && (SaOAuth2Util.supportType(clientId,GrantType.authorization_code) || cfg.isCode)) { return authorize(req, res, cfg); } @@ -68,17 +71,17 @@ public class SaOAuth2Handle { } // 模式二:隐藏式 - if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token) && cfg.isImplicit) { + if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token) && (SaOAuth2Util.supportType(clientId,GrantType.implicit) || cfg.isImplicit)) { return authorize(req, res, cfg); } // 模式三:密码式 - if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.password) && cfg.isPassword) { + if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.password) && (SaOAuth2Util.supportType(clientId,GrantType.password) || cfg.isPassword)) { return password(req, res, cfg); } // 模式四:凭证式 - if(req.isPath(Api.client_token) && req.isParam(Param.grant_type, GrantType.client_credentials) && cfg.isClient) { + if(req.isPath(Api.client_token) && req.isParam(Param.grant_type, GrantType.client_credentials) && (SaOAuth2Util.supportType(clientId,GrantType.client_credentials) || cfg.isClient)) { return clientToken(req, res, cfg); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java index ddeedc44..32736cd7 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java @@ -1,21 +1,17 @@ package cn.dev33.satoken.oauth2.logic; -import java.util.List; - import cn.dev33.satoken.SaManager; import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Param; -import cn.dev33.satoken.oauth2.model.AccessTokenModel; -import cn.dev33.satoken.oauth2.model.ClientTokenModel; -import cn.dev33.satoken.oauth2.model.CodeModel; -import cn.dev33.satoken.oauth2.model.RefreshTokenModel; -import cn.dev33.satoken.oauth2.model.RequestAuthModel; -import cn.dev33.satoken.oauth2.model.SaClientModel; +import cn.dev33.satoken.oauth2.model.*; import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; +import java.util.Arrays; +import java.util.List; + /** * Sa-Token-OAuth2 模块 代码实现 * @author kong @@ -922,4 +918,12 @@ public class SaOAuth2Template { return SaManager.getConfig().getTokenName() + ":oauth2:grant-scope:" + clientId + ":" + loginId; } + /** + * 检查是否支持的type类型 + */ + public Boolean supportType(String clientId,String type){ + SaClientModel saClientModel = checkClientModel(clientId); + return Arrays.asList(saClientModel.getAllowType().split(",")).contains(type); + } + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java index 068f9001..3e5a747f 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java @@ -292,6 +292,16 @@ public class SaOAuth2Util { public static String getGrantScope(String clientId, Object loginId) { return saOAuth2Template.getGrantScope(clientId, loginId); } + + /** + * 获取:检查是否支持的授权类型 + * @param clientId 应用id + * @param type 授权类型 + * @return 是否 + */ + public static Boolean supportType(String clientId, String type) { + return saOAuth2Template.supportType(clientId, type); + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java index 952f696c..c2672618 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java @@ -31,15 +31,21 @@ public class SaClientModel implements Serializable { */ public String allowUrl; + /** + * 应用允许授权的所有URL, 多个用逗号隔开 + */ + public String allowType; + public SaClientModel() { } - public SaClientModel(String clientId, String clientSecret, String contractScope, String allowUrl) { + public SaClientModel(String clientId, String clientSecret, String contractScope, String allowUrl,String allowType) { super(); this.clientId = clientId; this.clientSecret = clientSecret; this.contractScope = contractScope; this.allowUrl = allowUrl; + this.allowType = allowType; } /** @@ -105,6 +111,22 @@ public class SaClientModel implements Serializable { this.allowUrl = allowUrl; return this; } + + /** + * @return 应用允许的授权模式, 多个用逗号隔开 + */ + public String getAllowType() { + return allowType; + } + + /** + * @param allowType 应用允许的授权模式, 多个用逗号隔开 + * @return 对象自身 + */ + public SaClientModel setAllowType(String allowType) { + this.allowType = allowType; + return this; + } @Override public String toString() { From cda9542cbddfda63a3c6b4f11a8fa73964384981 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Sat, 18 Dec 2021 20:17:08 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8D=90=E8=B5=A0?= =?UTF-8?q?=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-doc/doc/more/sa-token-donate.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sa-token-doc/doc/more/sa-token-donate.md b/sa-token-doc/doc/more/sa-token-donate.md index 93a3282b..bf8fbe99 100644 --- a/sa-token-doc/doc/more/sa-token-donate.md +++ b/sa-token-doc/doc/more/sa-token-donate.md @@ -19,6 +19,11 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永 | 赞助人 | 赞助金额 | 留言 | 时间 | | :-------- | :-------- | :-------- | :-------- | +| 周周周杨 | ¥ 10 | 感谢您的开源项目! | 2021-12-18 | +| MrXionGe | ¥ 10 | SA加油~~ | 2021-12-17 | +| duyiliu | ¥ 10 | 化繁为简,是门艺术。 | 2021-12-16 | +| liu | ¥ 50 | 感谢您的开源项目! | 2021-12-15 | +| fuhouyin | ¥ 10 | 感谢您的开源项目! | 2021-12-01 | | 图灵谷 | ¥ 20 | 感谢您的开源项目! | 2021-11-30 | | luyuan | ¥ 20 | 感谢您的开源项目! | 2021-11-29 | | xiaoyan | ¥ 200 | 感谢您的开源项目! | 2021-11-26 | @@ -45,7 +50,7 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永 | njx33 | ¥ 10 | 感谢您的开源项目! | 2020-12-17 | | zhangjiaxiaozhuo | ¥ 10 | 感谢您的开源项目! | 2020-12-15 | | 知知 | ¥ 10 | 感谢您的开源项目! | 2020-12-15 | -| 省长 | ¥ 10 | java中最好用的权限认证框架! | 2020-12-15 | +| 省长 | ¥ 10 | java中最好用的权限认证框架! | 2020-12-15 | 感谢每一位小伙伴的热心支持! From 2c20e7baa06839763fe827494eafe0fa893e7aa8 Mon Sep 17 00:00:00 2001 From: dongchunyu <1540770111@qq.com> Date: Wed, 22 Dec 2021 15:44:45 +0800 Subject: [PATCH 5/7] =?UTF-8?q?fix=EF=BC=9A=E4=BC=98=E5=8C=96=E4=BC=A0?= =?UTF-8?q?=E9=80=92token=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../context/dubbo/filter/SaTokenDubboConsumerFilter.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java b/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java index 451c9f3a..6fe016b5 100644 --- a/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java +++ b/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java @@ -32,8 +32,13 @@ public class SaTokenDubboConsumerFilter implements Filter { RpcContext.getContext().setAttachment(SaIdUtil.ID_TOKEN, SaIdUtil.getToken()); } - // 1. 调用前,向下传递会话Token - RpcContext.getContext().setAttachment(SaTokenConsts.JUST_CREATED, StpUtil.getTokenValueNotCut()); + // 1. 调用前,向下传递会话Token + String tokenValueNotCut = null; + try { + tokenValueNotCut = StpUtil.getTokenValueNotCut(); + } finally { + RpcContext.getContext().setAttachment(SaTokenConsts.JUST_CREATED, tokenValueNotCut); + } // 2. 开始调用 Result invoke = invoker.invoke(invocation); From 254be3b3e50083127303c92e3c7d01344adebd69 Mon Sep 17 00:00:00 2001 From: dongchunyu <1540770111@qq.com> Date: Wed, 22 Dec 2021 15:48:55 +0800 Subject: [PATCH 6/7] =?UTF-8?q?fix=EF=BC=9A=E4=BC=98=E5=8C=96=E4=BC=A0?= =?UTF-8?q?=E9=80=92token=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../context/dubbo/filter/SaTokenDubboConsumerFilter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java b/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java index 6fe016b5..e183eb96 100644 --- a/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java +++ b/sa-token-plugin/sa-token-context-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java @@ -1,5 +1,6 @@ package cn.dev33.satoken.context.dubbo.filter; +import cn.dev33.satoken.exception.SaTokenException; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.Filter; @@ -36,6 +37,8 @@ public class SaTokenDubboConsumerFilter implements Filter { String tokenValueNotCut = null; try { tokenValueNotCut = StpUtil.getTokenValueNotCut(); + } catch (SaTokenException exception){ + } finally { RpcContext.getContext().setAttachment(SaTokenConsts.JUST_CREATED, tokenValueNotCut); } From 05c8a6b6f2ef4ae8762cccac10d5d4ac3bfec0c5 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Thu, 23 Dec 2021 01:21:16 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=AC=E5=8F=B8?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-doc/index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sa-token-doc/index.html b/sa-token-doc/index.html index c5db4c17..63142e17 100644 --- a/sa-token-doc/index.html +++ b/sa-token-doc/index.html @@ -263,6 +263,9 @@ + + +