diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java index cd7337e6..77240d0a 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java @@ -3,6 +3,8 @@ package com.pj.oauth2; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import com.ejlchina.okhttps.OkHttps; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.pj.utils.SoMap; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; @@ -32,7 +34,7 @@ public class SaOAuthClientController { // 根据Code码进行登录,获取 Access-Token 和 openid @RequestMapping("/codeLogin") - public SaResult codeLogin(String code) { + public SaResult codeLogin(String code) throws JsonProcessingException { // 调用Server端接口,获取 Access-Token 以及其他信息 String str = OkHttps.sync(serverUrl + "/oauth2/token") .addBodyPara("grant_type", "authorization_code") @@ -43,7 +45,7 @@ public class SaOAuthClientController { .getBody() .toString(); SoMap so = SoMap.getSoMap().setJsonString(str); - System.out.println("返回结果: " + so); + System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so)); // code不等于200 代表请求失败 if(so.getInt("code") != 200) { @@ -61,7 +63,7 @@ public class SaOAuthClientController { // 根据 Refresh-Token 去刷新 Access-Token @RequestMapping("/refresh") - public SaResult refresh(String refreshToken) { + public SaResult refresh(String refreshToken) throws JsonProcessingException { // 调用Server端接口,通过 Refresh-Token 刷新出一个新的 Access-Token String str = OkHttps.sync(serverUrl + "/oauth2/refresh") .addBodyPara("grant_type", "refresh_token") @@ -72,7 +74,7 @@ public class SaOAuthClientController { .getBody() .toString(); SoMap so = SoMap.getSoMap().setJsonString(str); - System.out.println("返回结果: " + so); + System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so)); // code不等于200 代表请求失败 if(so.getInt("code") != 200) { @@ -85,7 +87,7 @@ public class SaOAuthClientController { // 模式三:密码式-授权登录 @RequestMapping("/passwordLogin") - public SaResult passwordLogin(String username, String password) { + public SaResult passwordLogin(String username, String password) throws JsonProcessingException { // 模式三:密码式-授权登录 String str = OkHttps.sync(serverUrl + "/oauth2/token") .addBodyPara("grant_type", "password") @@ -97,7 +99,7 @@ public class SaOAuthClientController { .getBody() .toString(); SoMap so = SoMap.getSoMap().setJsonString(str); - System.out.println("返回结果: " + so); + System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so)); // code不等于200 代表请求失败 if(so.getInt("code") != 200) { @@ -115,7 +117,7 @@ public class SaOAuthClientController { // 模式四:获取应用的 Client-Token @RequestMapping("/clientToken") - public SaResult clientToken() { + public SaResult clientToken() throws JsonProcessingException { // 调用Server端接口 String str = OkHttps.sync(serverUrl + "/oauth2/client_token") .addBodyPara("grant_type", "client_credentials") @@ -125,7 +127,7 @@ public class SaOAuthClientController { .getBody() .toString(); SoMap so = SoMap.getSoMap().setJsonString(str); - System.out.println("返回结果: " + so); + System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so)); // code不等于200 代表请求失败 if(so.getInt("code") != 200) { @@ -145,7 +147,7 @@ public class SaOAuthClientController { // 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息 @RequestMapping("/getUserinfo") - public SaResult getUserinfo(String accessToken) { + public SaResult getUserinfo(String accessToken) throws JsonProcessingException { // 调用Server端接口,查询开放的资源 String str = OkHttps.sync(serverUrl + "/oauth2/userinfo") .addBodyPara("access_token", accessToken) @@ -153,7 +155,7 @@ public class SaOAuthClientController { .getBody() .toString(); SoMap so = SoMap.getSoMap().setJsonString(str); - System.out.println("返回结果: " + so); + System.out.println("返回结果: " + new ObjectMapper().writeValueAsString(so)); // code不等于200 代表请求失败 if(so.getInt("code") != 200) { diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java index 468d9bcd..61eba47b 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2DataLoaderImpl.java @@ -24,7 +24,7 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader { .setClientId("1001") .setClientSecret("aaaa-bbbb-cccc-dddd-eeee") .setAllowUrl("*") - .setContractScopes(Arrays.asList("userinfo")) + .setContractScopes(Arrays.asList("userinfo", "openid")) .setIsAutoMode(true); } return null; 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 74570f11..e886b125 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 @@ -73,14 +73,11 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { public AccessTokenModel convertCodeToAccessToken(CodeModel cm) { AccessTokenModel at = new AccessTokenModel(); at.accessToken = SaOAuth2Manager.getDataLoader().randomAccessToken(cm.clientId, cm.loginId, cm.scopes); - // at.refreshToken = randomRefreshToken(cm.clientId, cm.loginId, cm.scope); at.clientId = cm.clientId; at.loginId = cm.loginId; at.scopes = cm.scopes; - at.openid = SaOAuth2Manager.getDataLoader().getOpenid(cm.clientId, cm.loginId); SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(cm.clientId); at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000); - // at.refreshExpiresTime = System.currentTimeMillis() + (checkClientModel(cm.clientId).getRefreshTokenTimeout() * 1000); return at; } 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 b4a114a3..51c53c6c 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 @@ -18,9 +18,11 @@ package cn.dev33.satoken.oauth2.data.generate; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts; import cn.dev33.satoken.oauth2.dao.SaOAuth2Dao; +import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter; import cn.dev33.satoken.oauth2.data.model.*; import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; +import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; import cn.dev33.satoken.util.SaFoxUtil; import java.util.List; @@ -68,6 +70,7 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { public AccessTokenModel generateAccessToken(String code) { SaOAuth2Dao dao = SaOAuth2Manager.getDao(); + SaOAuth2DataConverter dataConverter = SaOAuth2Manager.getDataConverter(); // 1、先校验 CodeModel cm = dao.getCode(code); @@ -78,8 +81,9 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { dao.deleteRefreshToken(dao.getRefreshTokenValue(cm.clientId, cm.loginId)); // 3、生成token - AccessTokenModel at = SaOAuth2Manager.getDataConverter().convertCodeToAccessToken(cm); - RefreshTokenModel rt = SaOAuth2Manager.getDataConverter().convertAccessTokenToRefreshToken(at); + AccessTokenModel at = dataConverter.convertCodeToAccessToken(cm); + SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); + RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at); at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; 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 d16974f6..ff54085e 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 @@ -16,6 +16,7 @@ package cn.dev33.satoken.oauth2.data.model; import java.io.Serializable; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/strategy/SaScopeWorkFunction.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/strategy/SaScopeWorkFunction.java new file mode 100644 index 00000000..8068f266 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/function/strategy/SaScopeWorkFunction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.oauth2.function.strategy; + +import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; + +import java.util.function.Consumer; + +/** + * 函数式接口:AccessTokenModel 加工 + * + *

参数:AccessTokenModel

+ *

返回:无

+ * + * @author click33 + * @since 1.35.0 + */ +@FunctionalInterface +public interface SaScopeWorkFunction extends Consumer { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/processor/SaOAuth2ServerProcessor.java index 1a70e1c4..ab19bfec 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 @@ -25,6 +25,7 @@ import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Api; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.GrantType; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param; import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.ResponseType; +import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate; import cn.dev33.satoken.oauth2.data.model.*; import cn.dev33.satoken.oauth2.data.model.other.ClientIdAndSecretModel; import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode; @@ -140,6 +141,7 @@ public class SaOAuth2ServerProcessor { SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); + SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate(); // 1、如果尚未登录, 则先去登录 if( ! getStpLogic().isLogin()) { @@ -161,18 +163,19 @@ public class SaOAuth2ServerProcessor { return cfg.confirmView.apply(ra.clientId, ra.scopes); } + // 6、判断授权类型 // 如果是 授权码式,则:开始重定向授权,下放code if(ResponseType.code.equals(ra.responseType)) { - CodeModel codeModel = SaOAuth2Manager.getDataGenerate().generateCode(ra); - String redirectUri = SaOAuth2Manager.getDataGenerate().buildRedirectUri(ra.redirectUri, codeModel.code, ra.state); + CodeModel codeModel = dataGenerate.generateCode(ra); + String redirectUri = dataGenerate.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state); return res.redirect(redirectUri); } // 如果是 隐藏式,则:开始重定向授权,下放 token if(ResponseType.token.equals(ra.responseType)) { - AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, false); - String redirectUri = SaOAuth2Manager.getDataGenerate().buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state); + AccessTokenModel at = dataGenerate.generateAccessToken(ra, false); + String redirectUri = dataGenerate.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state); return res.redirect(redirectUri); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OpenIdScopeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OpenIdScopeHandler.java new file mode 100644 index 00000000..02d096d8 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/OpenIdScopeHandler.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.oauth2.scope.handler; + +import cn.dev33.satoken.oauth2.SaOAuth2Manager; +import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.scope.CommonScope; + +/** + * 所有OAuth2 权限处理器的父接口 + * + * @author click33 + * @since 1.39.0 + */ +public class OpenIdScopeHandler implements SaOAuth2ScopeAbstractHandler { + + /** + * 获取所要处理的权限 + */ + public String getHandlerScope() { + return CommonScope.OPENID; + } + + /** + * 所需要执行的方法 + */ + public void work(AccessTokenModel at) { + System.out.println("追加 openid " + at.accessToken); + at.openid = SaOAuth2Manager.getDataLoader().getOpenid(at.clientId, at.loginId); + } + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeAbstractHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeAbstractHandler.java new file mode 100644 index 00000000..f802eeb8 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeAbstractHandler.java @@ -0,0 +1,44 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.oauth2.scope.handler; + +import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; + +/** + * 所有OAuth2 权限处理器的父接口 + * + * @author click33 + * @since 1.39.0 + */ +public interface SaOAuth2ScopeAbstractHandler { + + /** + * 获取所要处理的权限 + * + * @return / + */ + String getHandlerScope(); + + /** + * 所需要执行的方法 + * + * @param at / + */ + default void work(AccessTokenModel at) { + + } + +} \ No newline at end of file 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 new file mode 100644 index 00000000..cdbb5ad3 --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java @@ -0,0 +1,94 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.oauth2.strategy; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.oauth2.function.strategy.SaScopeWorkFunction; +import cn.dev33.satoken.oauth2.scope.CommonScope; +import cn.dev33.satoken.oauth2.scope.handler.OpenIdScopeHandler; +import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeAbstractHandler; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Sa-Token OAuth2 相关策略 + * + * @author click33 + * @since 1.39.0 + */ +public final class SaOAuth2Strategy { + + private SaOAuth2Strategy() { + registerDefaultScopeHandler(); + } + + /** + * 全局单例引用 + */ + public static final SaOAuth2Strategy instance = new SaOAuth2Strategy(); + + + // ----------------------- 所有策略 + + /** + * 权限处理器集合 + */ + public Map scopeHandlerMap = new LinkedHashMap<>(); + + /** + * 注册所有默认的权限处理器 + */ + public void registerDefaultScopeHandler() { + scopeHandlerMap.put(CommonScope.OPENID, new OpenIdScopeHandler()); + } + + /** + * 注册一个权限处理器 + */ + public void registerScopeHandler(SaOAuth2ScopeAbstractHandler handler) { + scopeHandlerMap.put(handler.getHandlerScope(), handler); + // TODO 优化日志输出 + SaManager.getLog().info("新增权限处理器:" + handler.getHandlerScope()); +// SaTokenEventCenter.doRegisterAnnotationHandler(handler); + } + + /** + * 移除一个权限处理器 + */ + public void removeScopeHandler(String scope) { + scopeHandlerMap.remove(scope); + } + + /** + * 根据 scope 信息对一个 AccessTokenModel 进行加工处理 + */ + public SaScopeWorkFunction workAccessTokenByScope = (at) -> { + System.out.println("增强:" + at.accessToken); + System.out.println("权限:" + at.scopes); + // 遍历所有的权限处理器,如果此 AccessToken 具有这些权限,则开始加工 + if(at.scopes != null && !at.scopes.isEmpty()) { + for (Map.Entry entry: scopeHandlerMap.entrySet()) { + if(at.scopes.contains(entry.getKey())) { + entry.getValue().work(at); + } + } + } + + }; + + +} 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 dda7398e..90af6f8c 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 @@ -47,16 +47,6 @@ public class SaOAuth2Template { return SaOAuth2Manager.getDataLoader().getClientModel(clientId); } - /** - * 根据ClientId 和 LoginId 获取openid - * @param clientId 应用id - * @param loginId 账号id - * @return 此账号在此Client下的openid - */ - public String getOpenid(String clientId, Object loginId) { - return SaOAuth2Manager.getDataLoader().getOpenid(clientId, loginId); - } - // ------------------- 资源校验API /** * 根据id获取Client信息, 如果Client为空,则抛出异常