From c4b6a6381e6652314be486ba82a6d805e4ce5899 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Tue, 20 Aug 2024 12:58:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20OAuth2-=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=9D=83=E9=99=90=E5=A4=84=E7=90=86=E5=99=A8=20?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pj/oauth2/SaOAuth2ServerController.java | 14 +- .../com/pj/oauth2/UserinfoScopeHandler.java | 41 ++++ sa-token-doc/_sidebar.md | 6 +- .../oauth2/oauth2-custom-scope-handler.md | 178 ++++++++++++++++++ .../scope/handler/OidcScopeHandler.java | 2 +- .../scope/handler/OpenIdScopeHandler.java | 2 +- ...ava => SaOAuth2ScopeHandlerInterface.java} | 2 +- .../scope/handler/UserIdScopeHandler.java | 2 +- .../oauth2/strategy/SaOAuth2Strategy.java | 10 +- .../spring/oauth2/SaOAuth2BeanInject.java | 64 ++++++- 10 files changed, 300 insertions(+), 21 deletions(-) create mode 100644 sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/UserinfoScopeHandler.java create mode 100644 sa-token-doc/oauth2/oauth2-custom-scope-handler.md rename sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/{SaOAuth2ScopeAbstractHandler.java => SaOAuth2ScopeHandlerInterface.java} (96%) diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java index 594b8126..58d75726 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java @@ -8,6 +8,7 @@ import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @@ -59,11 +60,10 @@ public class SaOAuth2ServerController { // ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------ - // 获取Userinfo信息:昵称、头像、性别等等 + // 获取 userinfo 信息:昵称、头像、性别等等 @RequestMapping("/oauth2/userinfo") - public SaResult userinfo() { - // 获取 Access-Token 对应的账号id - String accessToken = SaHolder.getRequest().getParamNotNull("access_token"); + public SaResult userinfo(@RequestParam("access_token") String accessToken) { + // 获取 Access-Token 对应的账号id Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken); System.out.println("-------- 此Access-Token对应的账号id: " + loginId); @@ -71,9 +71,9 @@ public class SaOAuth2ServerController { SaOAuth2Util.checkScope(accessToken, "userinfo"); // 模拟账号信息 (真实环境需要查询数据库获取信息) - Map map = new LinkedHashMap(); - map.put("userId", "10008"); - map.put("nickname", "shengzhang_"); + Map map = new LinkedHashMap<>(); + // map.put("userId", loginId); 一般原则下,oauth2-server 不能把 userId 返回给 oauth2-client + map.put("nickname", "林小林"); map.put("avatar", "http://xxx.com/1.jpg"); map.put("age", "18"); map.put("sex", "男"); diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/UserinfoScopeHandler.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/UserinfoScopeHandler.java new file mode 100644 index 00000000..afca1436 --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/UserinfoScopeHandler.java @@ -0,0 +1,41 @@ +package com.pj.oauth2; + +import cn.dev33.satoken.oauth2.data.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; +import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author click33 + * @since 2024/8/20 + */ +@Component +public class UserinfoScopeHandler implements SaOAuth2ScopeHandlerInterface { + + @Override + public String getHandlerScope() { + return "userinfo"; + } + + @Override + public void workAccessToken(AccessTokenModel at) { + System.out.println("--------- userinfo 权限,加工 AccessTokenModel --------- "); + // 模拟账号信息 (真实环境需要查询数据库获取信息) + Map map = new LinkedHashMap(); + map.put("userId", "10008"); + map.put("nickname", "shengzhang_"); + map.put("avatar", "http://xxx.com/1.jpg"); + map.put("age", "18"); + map.put("sex", "男"); + map.put("address", "山东省 青岛市 城阳区"); + at.extraData.put("userinfo", map); + } + + @Override + public void workClientToken(ClientTokenModel ct) { + } + +} \ No newline at end of file diff --git a/sa-token-doc/_sidebar.md b/sa-token-doc/_sidebar.md index 432eb727..f742c13f 100644 --- a/sa-token-doc/_sidebar.md +++ b/sa-token-doc/_sidebar.md @@ -58,10 +58,10 @@ - [配置 client 域名校验 ](/oauth2/oauth2-check-domain) - [定制化登录页面与授权页面](/oauth2/oauth2-custom-login) - [自定义 API 路由 ](/oauth2/oauth2-custom-api) - - [前后端分离模式整合方案](/oauth2/4) - - [平台中心模式开发](/oauth2/5) - - [自定义 Scope 权限以处理器](/oauth2/6) + - [自定义 Scope 权限以处理器](/oauth2/oauth2-custom-scope-handler) - [为 Scope 划分等级](/oauth2/7) + + - [平台中心模式开发](/oauth2/5) - [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking) - [OAuth2 代码 API 参考](/oauth2/oauth2-dev) - [常见问题说明](/oauth2/8) diff --git a/sa-token-doc/oauth2/oauth2-custom-scope-handler.md b/sa-token-doc/oauth2/oauth2-custom-scope-handler.md new file mode 100644 index 00000000..2a391c80 --- /dev/null +++ b/sa-token-doc/oauth2/oauth2-custom-scope-handler.md @@ -0,0 +1,178 @@ +# OAuth2-自定义权限处理器 + +--- + +### 1、需求场景 +一般情况下,对于第三方 oauth2-client 来讲,仅仅拿到用户的 access_token 是不够的,还需要拿到更多的信息,比如用户昵称、头像等资料。 + +sa-token-oauth2 提供两种模式,让 access_token 可以得到更多信息。 + +- 自定义接口模式:在 oauth2-server 端开放一个资料查询接口,在 oauth2-client 得到 `access_token` 后,再次调用这个接口来获取 `userinfo` 信息。 +- 自定义权限处理器模式:自定义一个 `ScopeHandler`,直接在返回 `access_token` 时追加字段,将 `userinfo` 信息和 `access_token` 一并返回到 oauth2-client。 + + +### 2、自定义接口模式 + +#### 1、新建查询接口 + +在 oauth2-server 新建接口,查询指定 `access_token` 代表的 `userId` 其 `userinfo`: + +``` java +// 获取 userinfo 信息:昵称、头像、性别等等 +@RequestMapping("/oauth2/userinfo") +public SaResult userinfo(@RequestParam("access_token") String accessToken) { + // 获取 Access-Token 对应的账号id + Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken); + System.out.println("-------- 此Access-Token对应的账号id: " + loginId); + + // 校验 Access-Token 是否具有权限: userinfo + SaOAuth2Util.checkScope(accessToken, "userinfo"); + + // 模拟账号信息 (真实环境需要查询数据库获取信息) + Map map = new LinkedHashMap<>(); + // map.put("userId", loginId); 一般原则下,oauth2-server 不能把 userId 返回给 oauth2-client + map.put("nickname", "林小林"); + map.put("avatar", "http://xxx.com/1.jpg"); + map.put("age", "18"); + map.put("sex", "男"); + map.put("address", "山东省 青岛市 城阳区"); + return SaResult.ok().setMap(map); +} +``` + + +#### 2、申请 code 时指定权限 +oauth2-client 申请 `code` 时,一定需要加上 `userinfo` 权限 + +``` url +http://sa-oauth-server.com:8000/oauth2/authorize + ?response_type=code + &client_id=1001 + &redirect_uri=http://sa-oauth-client.com:8002/ + &scope=userinfo +``` + + +#### 3、code 换 access_token +访问上述链接后,得到 `code` 授权码,然后我们拿着 `code` 换 `access_token` + +``` url +http://sa-oauth-server.com:8000/oauth2/token + ?grant_type=authorization_code + &client_id=1001 + &client_secret=aaaa-bbbb-cccc-dddd-eeee + &code=${code} +``` + +#### 4、access_token 取 userinfo +使用返回的 `access_token` 再次访问接口 `/oauth2/userinfo` + +``` url +http://sa-oauth-server.com:8000/oauth2/userinfo?access_token=${access_token} +``` + +返回以下结果: +``` js +{ + "code": 200, + "msg": "ok", + "data": null, + "nickname": "林小林", + "avatar": "http://xxx.com/1.jpg", + "age": "18", + "sex": "男", + "address": "山东省 青岛市 城阳区" +} +``` + +拿到 userinfo。 + + + +### 3、自定义权限处理器模式 + +#### 1、新建权限处理器 +在 oauth2-server 新建 `UserinfoScopeHandler.java` 实现 `SaOAuth2ScopeHandlerInterface` 接口: + +``` java +@Component +public class UserinfoScopeHandler implements SaOAuth2ScopeHandlerInterface { + + @Override + public String getHandlerScope() { + return "userinfo"; + } + + @Override + public void workAccessToken(AccessTokenModel at) { + System.out.println("--------- userinfo 权限,加工 AccessTokenModel --------- "); + // 模拟账号信息 (真实环境需要查询数据库获取信息) + Map map = new LinkedHashMap(); + map.put("userId", "10008"); + map.put("nickname", "shengzhang_"); + map.put("avatar", "http://xxx.com/1.jpg"); + map.put("age", "18"); + map.put("sex", "男"); + map.put("address", "山东省 青岛市 城阳区"); + at.extraData.putAll(map); + } + + @Override + public void workClientToken(ClientTokenModel ct) { + } + +} +``` + +如上所述,所有写入到 `extraData` 中的数据,都将追加返回到 oauth2-client 端。 + + +#### 2、申请 code 时指定权限 +oauth2-client 申请 `code` 时,一定需要加上 `userinfo` 权限 +``` url +http://sa-oauth-server.com:8000/oauth2/authorize + ?response_type=code + &client_id=1001 + &redirect_uri=http://sa-oauth-client.com:8002/ + &scope=userinfo +``` + + +#### 3、code 换 access_token +3、访问上述链接后,得到 `code` 授权码,然后我们拿着 `code` 换 `access_token` +``` url +http://sa-oauth-server.com:8000/oauth2/token + ?grant_type=authorization_code + &client_id=1001 + &client_secret=aaaa-bbbb-cccc-dddd-eeee + &code=${code} +``` + +返回结果如下 +``` js +{ + "code": 200, + "msg": "ok", + "data": null, + "token_type": "bearer", + "access_token": "LQ24xI0hX25vIzvciHPA0PNsnGCweSFM1Bzl8783li07VAXpw8sEfn9xsta2", + "refresh_token": "rKB8mby1Mw8yZXHbWzliHx6lmatcLcULLw5C5cUMBhMMRx72DFg5u0owZgrA", + "expires_in": 7199, + "refresh_expires_in": 2591999, + "client_id": "1001", + "scope": "openid,userid,userinfo", + "userinfo": { + "userId": "10008", + "nickname": "shengzhang_", + "avatar": "http://xxx.com/1.jpg", + "age": "18", + "sex": "男", + "address": "山东省 青岛市 城阳区" + } +} +``` + +拿到 userinfo。 + +#### 总结 +相比于自定义接口模式,自定义权限处理器模式可以少一次网络请求,提前拿到 `userinfo` 信息。 \ No newline at end of file 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 71961833..7adb42d6 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 @@ -25,7 +25,7 @@ import cn.dev33.satoken.oauth2.scope.CommonScope; * @author click33 * @since 1.39.0 */ -public class OidcScopeHandler implements SaOAuth2ScopeAbstractHandler { +public class OidcScopeHandler implements SaOAuth2ScopeHandlerInterface { public String getHandlerScope() { return CommonScope.OIDC; 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 index 0bc100e7..cfc07a2b 100644 --- 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 @@ -27,7 +27,7 @@ import cn.dev33.satoken.oauth2.scope.CommonScope; * @author click33 * @since 1.39.0 */ -public class OpenIdScopeHandler implements SaOAuth2ScopeAbstractHandler { +public class OpenIdScopeHandler implements SaOAuth2ScopeHandlerInterface { public String getHandlerScope() { return CommonScope.OPENID; 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/SaOAuth2ScopeHandlerInterface.java similarity index 96% rename from sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeAbstractHandler.java rename to sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/SaOAuth2ScopeHandlerInterface.java index 32aa8110..d06ed40b 100644 --- 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/SaOAuth2ScopeHandlerInterface.java @@ -24,7 +24,7 @@ import cn.dev33.satoken.oauth2.data.model.ClientTokenModel; * @author click33 * @since 1.39.0 */ -public interface SaOAuth2ScopeAbstractHandler { +public interface SaOAuth2ScopeHandlerInterface { /** * 获取所要处理的权限 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/UserIdScopeHandler.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/UserIdScopeHandler.java index b062a3df..18fad7d2 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/UserIdScopeHandler.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/scope/handler/UserIdScopeHandler.java @@ -26,7 +26,7 @@ import cn.dev33.satoken.oauth2.scope.CommonScope; * @author click33 * @since 1.39.0 */ -public class UserIdScopeHandler implements SaOAuth2ScopeAbstractHandler { +public class UserIdScopeHandler implements SaOAuth2ScopeHandlerInterface { public String getHandlerScope() { return CommonScope.USERID; 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 97fbab77..05aa59f5 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 @@ -20,7 +20,7 @@ import cn.dev33.satoken.oauth2.function.strategy.*; import cn.dev33.satoken.oauth2.scope.CommonScope; import cn.dev33.satoken.oauth2.scope.handler.OidcScopeHandler; import cn.dev33.satoken.oauth2.scope.handler.OpenIdScopeHandler; -import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeAbstractHandler; +import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface; import cn.dev33.satoken.oauth2.scope.handler.UserIdScopeHandler; import cn.dev33.satoken.util.SaFoxUtil; @@ -49,7 +49,7 @@ public final class SaOAuth2Strategy { /** * 权限处理器集合 */ - public Map scopeHandlerMap = new LinkedHashMap<>(); + public Map scopeHandlerMap = new LinkedHashMap<>(); /** * 注册所有默认的权限处理器 @@ -63,7 +63,7 @@ public final class SaOAuth2Strategy { /** * 注册一个权限处理器 */ - public void registerScopeHandler(SaOAuth2ScopeAbstractHandler handler) { + public void registerScopeHandler(SaOAuth2ScopeHandlerInterface handler) { scopeHandlerMap.put(handler.getHandlerScope(), handler); // TODO 优化日志输出 SaManager.getLog().info("新增权限处理器:" + handler.getHandlerScope()); @@ -86,7 +86,7 @@ public final class SaOAuth2Strategy { public SaOAuth2ScopeWorkAccessTokenFunction workAccessTokenByScope = (at) -> { if(at.scopes != null && !at.scopes.isEmpty()) { for (String scope : at.scopes) { - SaOAuth2ScopeAbstractHandler handler = scopeHandlerMap.get(scope); + SaOAuth2ScopeHandlerInterface handler = scopeHandlerMap.get(scope); if(handler != null) { handler.workAccessToken(at); } @@ -100,7 +100,7 @@ public final class SaOAuth2Strategy { public SaOAuth2ScopeWorkClientTokenFunction workClientTokenByScope = (ct) -> { if(ct.scopes != null && !ct.scopes.isEmpty()) { for (String scope : ct.scopes) { - SaOAuth2ScopeAbstractHandler handler = scopeHandlerMap.get(scope); + SaOAuth2ScopeHandlerInterface handler = scopeHandlerMap.get(scope); if(handler != null) { handler.workClientToken(ct); } diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java index 0f4e1767..3ff3dda9 100644 --- a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java +++ b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java @@ -17,12 +17,20 @@ package cn.dev33.satoken.spring.oauth2; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.config.SaOAuth2Config; +import cn.dev33.satoken.oauth2.dao.SaOAuth2Dao; +import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter; +import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate; import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader; +import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver; import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor; +import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface; +import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy; import cn.dev33.satoken.oauth2.template.SaOAuth2Template; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import java.util.List; + // 小提示:如果你在 idea 中运行源码时出现异常:java: 程序包cn.dev33.satoken.oauth2不存在。 // 在项目根目录进入 cmd,执行 mvn package 即可解决 @@ -53,7 +61,7 @@ public class SaOAuth2BeanInject { * @param saOAuth2Template 模板代码类 */ @Autowired(required = false) - public void setSaOAuth2Interface(SaOAuth2Template saOAuth2Template) { + public void setSaOAuth2Template(SaOAuth2Template saOAuth2Template) { SaOAuth2ServerProcessor.instance.oauth2Template = saOAuth2Template; } @@ -63,8 +71,60 @@ public class SaOAuth2BeanInject { * @param dataLoader / */ @Autowired(required = false) - public void setSaOAuth2Interface(SaOAuth2DataLoader dataLoader) { + public void setSaOAuth2DataLoader(SaOAuth2DataLoader dataLoader) { SaOAuth2Manager.setDataLoader(dataLoader); } + /** + * 注入 OAuth2 数据解析器 Bean + * + * @param dataResolver / + */ + @Autowired(required = false) + public void setSaOAuth2DataResolver(SaOAuth2DataResolver dataResolver) { + SaOAuth2Manager.setDataResolver(dataResolver); + } + + /** + * 注入 OAuth2 数据格式转换器 Bean + * + * @param dataConverter / + */ + @Autowired(required = false) + public void setSaOAuth2DataConverter(SaOAuth2DataConverter dataConverter) { + SaOAuth2Manager.setDataConverter(dataConverter); + } + + /** + * 注入 OAuth2 数据构建器 Bean + * + * @param dataGenerate / + */ + @Autowired(required = false) + public void setSaOAuth2DataGenerate(SaOAuth2DataGenerate dataGenerate) { + SaOAuth2Manager.setDataGenerate(dataGenerate); + } + + /** + * 注入 OAuth2 数据持久 Bean + * + * @param dao / + */ + @Autowired(required = false) + public void setSaOAuth2Dao(SaOAuth2Dao dao) { + SaOAuth2Manager.setDao(dao); + } + + /** + * 注入自定义 scope 处理器 + * + * @param handlerList 自定义 scope 处理器集合 + */ + @Autowired(required = false) + public void setSaOAuth2ScopeHandler(List handlerList) { + for (SaOAuth2ScopeHandlerInterface handler : handlerList) { + SaOAuth2Strategy.instance.registerScopeHandler(handler); + } + } + }