From 174a94db01c4d1cb84ffff4e0e507414f5f321a8 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Mon, 19 Aug 2024 23:29:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=95=B4=E9=80=82=E9=85=8D=E6=8B=86?= =?UTF-8?q?=E5=88=86=E5=BC=8F=E8=B7=AF=E7=94=B1=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/pj/SaOAuth2ServerApplication.java | 4 +- .../com/pj/oauth2/SaOAuth2DataLoaderImpl.java | 6 +- .../pj/oauth2/SaOAuth2ServerController.java | 16 +- .../src/main/resources/application.yml | 20 ++- sa-token-doc/_sidebar.md | 2 +- sa-token-doc/oauth2/oauth2-custom-api.md | 104 ++++++++++++ .../satoken/oauth2/config/SaOAuth2Config.java | 63 +++---- .../SaOAuth2DataConverterDefaultImpl.java | 4 +- .../SaOAuth2DataGenerateDefaultImpl.java | 12 +- .../data/model/loader/SaClientModel.java | 104 ++++++------ .../SaOAuth2DataResolverDefaultImpl.java | 2 +- .../oauth2/error/SaOAuth2ErrorCode.java | 3 + .../processor/SaOAuth2ServerProcessor.java | 155 ++++++++++++------ 13 files changed, 335 insertions(+), 160 deletions(-) create mode 100644 sa-token-doc/oauth2/oauth2-custom-api.md diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java index 4accb81c..4cf9200b 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java @@ -1,5 +1,6 @@ package com.pj; +import cn.dev33.satoken.oauth2.SaOAuth2Manager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -12,7 +13,8 @@ public class SaOAuth2ServerApplication { public static void main(String[] args) { SpringApplication.run(SaOAuth2ServerApplication.class, args); - System.out.println("\nSa-Token-OAuth Server端启动成功"); + System.out.println("\nSa-Token-OAuth Server端启动成功,配置如下:"); + System.out.println(SaOAuth2Manager.getConfig()); } } 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 72948364..a7d10925 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 @@ -22,7 +22,11 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader { .setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥 .addAllowUrls("*") // 所有允许授权的 url .addContractScopes("openid", "userid", "userinfo") // 所有签约的权限 - .setIsAutoMode(true); // 是否自动判断开放的授权模式 + .setEnableCode(true) // 是否开启授权码模式 + .setEnableImplicit(true) // 是否开启隐式模式 + .setEnablePassword(true) // 是否开启密码模式 + .setEnableClient(true) // 是否开启客户端模式 + ; } return null; } diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java index 44ddce9d..594b8126 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 @@ -7,7 +7,6 @@ import cn.dev33.satoken.oauth2.template.SaOAuth2Util; 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.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @@ -17,20 +16,20 @@ import java.util.LinkedHashMap; import java.util.Map; /** - * Sa-OAuth2 Server端 控制器 + * Sa-Token-OAuth2 Server端 Controller + * * @author click33 - * */ @RestController public class SaOAuth2ServerController { - // 处理所有OAuth相关请求 + // OAuth2-Server 端:处理所有OAuth相关请求 @RequestMapping("/oauth2/*") public Object request() { System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl()); return SaOAuth2ServerProcessor.instance.dister(); } - + // Sa-OAuth2 定制化配置 @Autowired public void configOAuth2Server(SaOAuth2Config cfg) { @@ -57,13 +56,6 @@ public class SaOAuth2ServerController { }; } - // 全局异常拦截 - @ExceptionHandler - public SaResult handlerException(Exception e) { - e.printStackTrace(); - return SaResult.error(e.getMessage()); - } - // ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------ diff --git a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/resources/application.yml index 0d263504..44f39cd0 100644 --- a/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/resources/application.yml @@ -3,15 +3,19 @@ server: # sa-token配置 sa-token: - # token名称 (同时也是cookie名称) - token-name: satoken-server + # token名称 (同时也是 Cookie 名称) + token-name: satoken-oauth2-server # OAuth2.0 配置 - oauth2: - is-code: true - is-implicit: true - is-password: true - is-client: true - + oauth2: + # 是否全局开启授权码模式 + enable-code: true + # 是否全局开启 Implicit 模式 + enable-implicit: true + # 是否全局开启密码模式 + enable-password: true + # 是否全局开启客户端模式 + enable-client: true + spring: # redis配置 redis: diff --git a/sa-token-doc/_sidebar.md b/sa-token-doc/_sidebar.md index e15f364c..432eb727 100644 --- a/sa-token-doc/_sidebar.md +++ b/sa-token-doc/_sidebar.md @@ -57,7 +57,7 @@ - [OAuth2-Server端开放 API 接口](/oauth2/oauth2-apidoc) - [配置 client 域名校验 ](/oauth2/oauth2-check-domain) - [定制化登录页面与授权页面](/oauth2/oauth2-custom-login) - - [拆分式路由](/oauth2/3) + - [自定义 API 路由 ](/oauth2/oauth2-custom-api) - [前后端分离模式整合方案](/oauth2/4) - [平台中心模式开发](/oauth2/5) - [自定义 Scope 权限以处理器](/oauth2/6) diff --git a/sa-token-doc/oauth2/oauth2-custom-api.md b/sa-token-doc/oauth2/oauth2-custom-api.md new file mode 100644 index 00000000..a398c37d --- /dev/null +++ b/sa-token-doc/oauth2/oauth2-custom-api.md @@ -0,0 +1,104 @@ +# OAuth2-自定义 API 路由 + +--- + +### 方式一:修改全局变量 + +在之前的章节中,我们演示了如何搭建一个 OAuth2 认证中心: +``` java +/** + * Sa-Token-OAuth2 Server端 Controller + */ +@RestController +public class SaOAuth2ServerController { + + // OAuth2-Server 端:处理所有 OAuth 相关请求 + @RequestMapping("/oauth2/*") + public Object request() { + return SaOAuth2ServerProcessor.instance.dister(); + } + + // ... 其它代码 + +} +``` + +这种写法集成简单但却不够灵活。例如获取 code 授权码地址只能是:`http://{host}:{port}/oauth2/authorize`,如果我们想要自定义其API地址,应该怎么做呢? + +打开 OAuth2 模块相关源码,有关 API 的设计都定义在: +[SaOAuth2Consts.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/consts/SaOAuth2Consts.java) +中,我们可以对其进行二次修改。 + +例如,我们可以在 Main 方法启动类或者 OAuth2 配置方法中修改变量值: +``` java +// 配置 OAuth2 相关参数 +@Autowired +private void configOAuth2Server(SaOAuth2Config cfg) { + // 自定义API地址 + SaOAuth2Consts.Api.authorize = "/oauth2/authorize2"; + // ... +} +``` + +启动项目,统一认证地址就被我们修改成了:`http://{host}:{port}/oauth2/authorize2` + + +### 方式二:拆分路由入口 +根据上述路由入口:`@RequestMapping("/oauth2/*")`,我们给它起一个合适的名字 —— 聚合式路由。 + +与之对应的,我们可以将其修改为拆分式路由: + +``` java +/** + * Sa-Token-OAuth2 Server端 Controller + */ +@RestController +public class SaOAuth2ServerController { + + // 模式一:Code授权码 || 模式二:隐藏式 + @RequestMapping("/oauth2/authorize") + public Object authorize() { + return SaOAuth2ServerProcessor.instance.authorize(); + } + + // 用户登录 + @RequestMapping("/oauth2/doLogin") + public Object doLogin() { + return SaOAuth2ServerProcessor.instance.doLogin(); + } + + // 用户确认授权 + @RequestMapping("/oauth2/doConfirm") + public Object doConfirm() { + return SaOAuth2ServerProcessor.instance.doConfirm(); + } + + // Code 换 Access-Token || 模式三:密码式 + @RequestMapping("/oauth2/token") + public Object token() { + return SaOAuth2ServerProcessor.instance.tokenOrPassword(); + } + + // Refresh-Token 刷新 Access-Token + @RequestMapping("/oauth2/refresh") + public Object refresh() { + return SaOAuth2ServerProcessor.instance.refresh(); + } + + // 回收 Access-Token + @RequestMapping("/oauth2/revoke") + public Object revoke() { + return SaOAuth2ServerProcessor.instance.revoke(); + } + + // 模式四:凭证式 + @RequestMapping("/oauth2/client_token") + public Object clientToken() { + return SaOAuth2ServerProcessor.instance.clientToken(); + } + +} +``` + +拆分式路由 与 聚合式路由 在功能上完全等价,且提供了更为细致的路由管控。 + 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 3114e650..41cab136 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 @@ -34,16 +34,16 @@ public class SaOAuth2Config implements Serializable { private static final long serialVersionUID = -6541180061782004705L; /** 是否打开模式:授权码(Authorization Code) */ - public Boolean isCode = true; + public Boolean enableCode = true; /** 是否打开模式:隐藏式(Implicit) */ - public Boolean isImplicit = true; + public Boolean enableImplicit = true; /** 是否打开模式:密码式(Password) */ - public Boolean isPassword = true; + public Boolean enablePassword = true; /** 是否打开模式:凭证式(Client Credentials) */ - public Boolean isClient = true; + public Boolean enableClient = true; /** 是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */ public Boolean isNewRefresh = false; @@ -69,59 +69,59 @@ public class SaOAuth2Config implements Serializable { /** - * @return isCode + * @return enableCode */ - public Boolean getIsCode() { - return isCode; + public Boolean getEnableCode() { + return enableCode; } /** - * @param isCode 要设置的 isCode + * @param enableCode 要设置的 enableCode */ - public void setIsCode(Boolean isCode) { - this.isCode = isCode; + public void setEnableCode(Boolean enableCode) { + this.enableCode = enableCode; } /** - * @return isImplicit + * @return enableImplicit */ - public Boolean getIsImplicit() { - return isImplicit; + public Boolean getEnableImplicit() { + return enableImplicit; } /** - * @param isImplicit 要设置的 isImplicit + * @param enableImplicit 要设置的 enableImplicit */ - public void setIsImplicit(Boolean isImplicit) { - this.isImplicit = isImplicit; + public void setEnableImplicit(Boolean enableImplicit) { + this.enableImplicit = enableImplicit; } /** - * @return isPassword + * @return enablePassword */ - public Boolean getIsPassword() { - return isPassword; + public Boolean getEnablePassword() { + return enablePassword; } /** - * @param isPassword 要设置的 isPassword + * @param enablePassword 要设置的 enablePassword */ - public void setIsPassword(Boolean isPassword) { - this.isPassword = isPassword; + public void setEnablePassword(Boolean enablePassword) { + this.enablePassword = enablePassword; } /** - * @return isClient + * @return enableClient */ - public Boolean getIsClient() { - return isClient; + public Boolean getEnableClient() { + return enableClient; } /** - * @param isClient 要设置的 isClient + * @param enableClient 要设置的 enableClient */ - public void setIsClient(Boolean isClient) { - this.isClient = isClient; + public void setEnableClient(Boolean enableClient) { + this.enableClient = enableClient; } /** @@ -254,8 +254,11 @@ public class SaOAuth2Config implements Serializable { @Override public String toString() { - return "SaOAuth2Config [isCode=" + isCode + ", isImplicit=" + isImplicit + ", isPassword=" + isPassword - + ", isClient=" + isClient + return "SaOAuth2Config [" + + "enableCode=" + enableCode + + ", enableImplicit=" + enableImplicit + + ", enablePassword=" + enablePassword + + ", enableClient=" + enableClient + ", isNewRefresh=" + isNewRefresh + ", codeTimeout=" + codeTimeout + ", accessTokenTimeout=" + accessTokenTimeout 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 37ec58b3..5212fc43 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 @@ -100,8 +100,8 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter { rt.expiresTime = System.currentTimeMillis() + (clientModel.getRefreshTokenTimeout() * 1000); rt.extraData = new LinkedHashMap<>(at.extraData); // 改变 at 属性 - at.refreshToken = rt.refreshToken; - at.refreshExpiresTime = rt.expiresTime; +// at.refreshToken = rt.refreshToken; +// at.refreshExpiresTime = rt.expiresTime; return rt; } 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 7c6f5ff6..1ff51e79 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 @@ -87,10 +87,10 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { // 3、生成token AccessTokenModel at = dataConverter.convertCodeToAccessToken(cm); + SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at); at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; - SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); // 4、保存token dao.saveAccessToken(at); @@ -166,14 +166,20 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate { // 2、生成 新Access-Token String newAtValue = SaOAuth2Strategy.instance.createAccessToken.execute(ra.clientId, ra.loginId, ra.scopes); AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scopes); - // TODO 此处的 openid 应该怎么加载? - // at.openid = SaOAuth2Manager.getDataLoader().getOpenid(ra.clientId, ra.loginId); + + // 3、根据权限构建额外参数 + at.extraData = new LinkedHashMap<>(); + SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at); + SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(ra.clientId); at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000); // 3、生成&保存 Refresh-Token if(isCreateRt) { RefreshTokenModel rt = SaOAuth2Manager.getDataConverter().convertAccessTokenToRefreshToken(at); + at.refreshToken = rt.refreshToken; + at.refreshExpiresTime = rt.expiresTime; + dao.saveRefreshToken(rt); dao.saveRefreshTokenIndex(rt); } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java index 2cc8dc5f..248c1aef 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/loader/SaClientModel.java @@ -54,23 +54,23 @@ public class SaClientModel implements Serializable { public List allowUrls; /** 此 Client 是否打开模式:授权码(Authorization Code) */ - public Boolean isCode = false; + public Boolean enableCode = false; /** 此 Client 是否打开模式:隐藏式(Implicit) */ - public Boolean isImplicit = false; + public Boolean enableImplicit = false; /** 此 Client 是否打开模式:密码式(Password) */ - public Boolean isPassword = false; + public Boolean enablePassword = false; /** 此 Client 是否打开模式:凭证式(Client Credentials) */ - public Boolean isClient = false; + public Boolean enableClient = false; - /** - * 是否自动判断此 Client 开放的授权模式 - *
此值为true时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠全局设置 - *
此值为false时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠局部配置+全局配置 - */ - public Boolean isAutoMode = true; +// /** +// * 是否自动判断此 Client 开放的授权模式 +// *
此值为true时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠全局设置 +// *
此值为false时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠局部配置+全局配置 +// */ +// public Boolean isAutoMode = true; /** 单独配置此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] */ public Boolean isNewRefresh; @@ -171,83 +171,83 @@ public class SaClientModel implements Serializable { /** * @return 此 Client 是否打开模式:授权码(Authorization Code) */ - public Boolean getIsCode() { - return isCode; + public Boolean getEnableCode() { + return enableCode; } /** - * @param isCode 此 Client 是否打开模式:授权码(Authorization Code) + * @param enableCode 此 Client 是否打开模式:授权码(Authorization Code) * @return 对象自身 */ - public SaClientModel setIsCode(Boolean isCode) { - this.isCode = isCode; + public SaClientModel setEnableCode(Boolean enableCode) { + this.enableCode = enableCode; return this; } /** * @return 此 Client 是否打开模式:隐藏式(Implicit) */ - public Boolean getIsImplicit() { - return isImplicit; + public Boolean getEnableImplicit() { + return enableImplicit; } /** - * @param isImplicit 此 Client 是否打开模式:隐藏式(Implicit) + * @param enableImplicit 此 Client 是否打开模式:隐藏式(Implicit) * @return 对象自身 */ - public SaClientModel setIsImplicit(Boolean isImplicit) { - this.isImplicit = isImplicit; + public SaClientModel setEnableImplicit(Boolean enableImplicit) { + this.enableImplicit = enableImplicit; return this; } /** * @return 此 Client 是否打开模式:密码式(Password) */ - public Boolean getIsPassword() { - return isPassword; + public Boolean getEnablePassword() { + return enablePassword; } /** - * @param isPassword 此 Client 是否打开模式:密码式(Password) + * @param enablePassword 此 Client 是否打开模式:密码式(Password) * @return 对象自身 */ - public SaClientModel setIsPassword(Boolean isPassword) { - this.isPassword = isPassword; + public SaClientModel setEnablePassword(Boolean enablePassword) { + this.enablePassword = enablePassword; return this; } /** * @return 此 Client 是否打开模式:凭证式(Client Credentials) */ - public Boolean getIsClient() { - return isClient; + public Boolean getEnableClient() { + return enableClient; } /** - * @param isClient 此 Client 是否打开模式:凭证式(Client Credentials) + * @param enableClient 此 Client 是否打开模式:凭证式(Client Credentials) * @return 对象自身 */ - public SaClientModel setIsClient(Boolean isClient) { - this.isClient = isClient; + public SaClientModel setEnableClient(Boolean enableClient) { + this.enableClient = enableClient; return this; } - - /** - * @return 是否自动判断此 Client 开放的授权模式 - */ - public Boolean getIsAutoMode() { - return isAutoMode; - } - - /** - * @param isAutoMode 是否自动判断此 Client 开放的授权模式 - * @return 对象自身 - */ - public SaClientModel setIsAutoMode(Boolean isAutoMode) { - this.isAutoMode = isAutoMode; - return this; - } - +// +// /** +// * @return 是否自动判断此 Client 开放的授权模式 +// */ +// public Boolean getIsAutoMode() { +// return isAutoMode; +// } +// +// /** +// * @param isAutoMode 是否自动判断此 Client 开放的授权模式 +// * @return 对象自身 +// */ +// public SaClientModel setIsAutoMode(Boolean isAutoMode) { +// this.isAutoMode = isAutoMode; +// return this; +// } +// /** * @return 此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] @@ -338,11 +338,11 @@ public class SaClientModel implements Serializable { ", clientSecret='" + clientSecret + '\'' + ", contractScopes=" + contractScopes + ", allowUrls=" + allowUrls + - ", isCode=" + isCode + - ", isImplicit=" + isImplicit + - ", isPassword=" + isPassword + - ", isClient=" + isClient + - ", isAutoMode=" + isAutoMode + + ", isCode=" + enableCode + + ", isImplicit=" + enableImplicit + + ", isPassword=" + enablePassword + + ", isClient=" + enableClient + +// ", isAutoMode=" + isAutoMode + ", isNewRefresh=" + isNewRefresh + ", accessTokenTimeout=" + accessTokenTimeout + ", refreshTokenTimeout=" + refreshTokenTimeout + diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java index 97375f88..339ac51a 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/resolver/SaOAuth2DataResolverDefaultImpl.java @@ -113,7 +113,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver { public Map buildClientTokenReturnValue(ClientTokenModel ct) { Map map = new LinkedHashMap<>(); map.put("client_token", ct.clientToken); - map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议 + // map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议 map.put("expires_in", ct.getExpiresIn()); map.put("client_id", ct.clientId); map.put("scope", SaOAuth2Manager.getDataConverter().convertScopeListToString(ct.scopes)); diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java index cd6a3fa4..fa15e0d4 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/error/SaOAuth2ErrorCode.java @@ -98,6 +98,9 @@ public interface SaOAuth2ErrorCode { /** 无效response_type */ int CODE_30125 = 30125; + /** 无效grant_type */ + int CODE_30126 = 30126; + /** 暂未开放授权码模式 */ int CODE_30131 = 30131; 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 39a2446c..55857340 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 @@ -71,28 +71,24 @@ public class SaOAuth2ServerProcessor { // ------------------ 路由分发 ------------------ - // 模式一:Code授权码 - if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.code)) { - SaClientModel cm = currClientModel(); - if(cfg.getIsCode() && (cm.isCode || cm.isAutoMode)) { - return authorize(); - } - throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30131); + // 模式一:Code授权码 || 模式二:隐藏式 + if(req.isPath(Api.authorize)) { + return authorize(); } - - // Code授权码 获取 Access-Token - if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.authorization_code)) { - return token(); + + // Code 换 Access-Token || 模式三:密码式 + if(req.isPath(Api.token)) { + return tokenOrPassword(); } // Refresh-Token 刷新 Access-Token - if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) { - return refreshToken(); + if(req.isPath(Api.refresh)) { + return refresh(); } // 回收 Access-Token if(req.isPath(Api.revoke)) { - return revokeToken(); + return revoke(); } // doLogin 登录接口 @@ -105,31 +101,9 @@ public class SaOAuth2ServerProcessor { return doConfirm(); } - // 模式二:隐藏式 - if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token)) { - SaClientModel cm = currClientModel(); - if(cfg.getIsImplicit() && (cm.isImplicit || cm.isAutoMode)) { - return authorize(); - } - throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30132); - } - - // 模式三:密码式 - if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.password)) { - SaClientModel cm = currClientModel(); - if(cfg.getIsPassword() && (cm.isPassword || cm.isAutoMode)) { - return password(); - } - throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30133); - } - // 模式四:凭证式 - if(req.isPath(Api.client_token) && req.isParam(Param.grant_type, GrantType.client_credentials)) { - SaClientModel cm = currClientModel(); - if(cfg.getIsClient() && (cm.isClient || cm.isAutoMode)) { - return clientToken(); - } - throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30134); + if(req.isPath(Api.client_token)) { + return clientToken(); } // 默认返回 @@ -141,42 +115,67 @@ public class SaOAuth2ServerProcessor { * @return 处理结果 */ public Object authorize() { + // 获取变量 SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate(); + String responseType = req.getParamNotNull(Param.response_type); - // 1、如果尚未登录, 则先去登录 + // 1、先判断是否开启了指定的授权模式 + // 模式一:Code授权码 + if(responseType.equals(ResponseType.code)) { + if(!cfg.enableCode) { + throwErrorSystemNotEnableModel(); + } + if(!currClientModel().enableCode) { + throwErrorClientNotEnableModel(); + } + } + // 模式二:隐藏式 + else if(responseType.equals(ResponseType.token)) { + if(!cfg.enableImplicit) { + throwErrorSystemNotEnableModel(); + } + if(!currClientModel().enableImplicit) { + throwErrorClientNotEnableModel(); + } + } + // 其它 + else { + throw new SaOAuth2Exception("无效 response_type: " + req.getParam(Param.response_type)).setCode(SaOAuth2ErrorCode.CODE_30125); + } + + // 2、如果尚未登录, 则先去登录 if( ! getStpLogic().isLogin()) { return cfg.notLoginView.get(); } - // 2、构建请求 Model + // 3、构建请求 Model RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, getStpLogic().getLoginId()); - // 3、校验:重定向域名是否合法 + // 4、校验:重定向域名是否合法 oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri); - // 4、校验:此次申请的Scope,该Client是否已经签约 + // 5、校验:此次申请的Scope,该Client是否已经签约 oauth2Template.checkContract(ra.clientId, ra.scopes); - // 5、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 + // 6、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 boolean isGrant = oauth2Template.isGrant(ra.loginId, ra.clientId, ra.scopes); if( ! isGrant) { return cfg.confirmView.apply(ra.clientId, ra.scopes); } - - // 6、判断授权类型 - // 如果是 授权码式,则:开始重定向授权,下放code + // 7、判断授权类型,重定向到不同地址 + // 如果是 授权码式,则:开始重定向授权,下放code if(ResponseType.code.equals(ra.responseType)) { CodeModel codeModel = dataGenerate.generateCode(ra); String redirectUri = dataGenerate.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state); return res.redirect(redirectUri); } - // 如果是 隐藏式,则:开始重定向授权,下放 token + // 如果是 隐藏式,则:开始重定向授权,下放 token if(ResponseType.token.equals(ra.responseType)) { AccessTokenModel at = dataGenerate.generateAccessToken(ra, false); String redirectUri = dataGenerate.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state); @@ -187,6 +186,34 @@ public class SaOAuth2ServerProcessor { throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125); } + /** + * Code 换 Access-Token || 模式三:密码式 + * @return 处理结果 + */ + public Object tokenOrPassword() { + + String grantType = SaHolder.getRequest().getParamNotNull(Param.grant_type); + + // Code 换 Access-Token + if(grantType.equals(GrantType.authorization_code)) { + return token(); + } + + // 模式三:密码式 + if(grantType.equals(GrantType.password)) { + SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); + if(!cfg.enablePassword) { + throwErrorSystemNotEnableModel(); + } + if(!currClientModel().enablePassword) { + throwErrorClientNotEnableModel(); + } + return password(); + } + + throw new SaOAuth2Exception("无效 grant_type:" + grantType); + } + /** * Code授权码 获取 Access-Token * @return 处理结果 @@ -216,9 +243,13 @@ public class SaOAuth2ServerProcessor { * Refresh-Token 刷新 Access-Token * @return 处理结果 */ - public Object refreshToken() { + public Object refresh() { // 获取变量 SaRequest req = SaHolder.getRequest(); + String grantType = req.getParamNotNull(Param.grant_type); + if(!grantType.equals(GrantType.refresh_token)) { + throw new SaOAuth2Exception("无效 grant_type:" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); + } // 获取参数 @@ -241,7 +272,7 @@ public class SaOAuth2ServerProcessor { * 回收 Access-Token * @return 处理结果 */ - public Object revokeToken() { + public Object revoke() { // 获取变量 SaRequest req = SaHolder.getRequest(); @@ -344,6 +375,18 @@ public class SaOAuth2ServerProcessor { public Object clientToken() { // 获取变量 SaRequest req = SaHolder.getRequest(); + SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); + + String grantType = req.getParamNotNull(Param.grant_type); + if(!grantType.equals(GrantType.client_credentials)) { + throw new SaOAuth2Exception("无效 grant_type:" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126); + } + if(!cfg.enableClient) { + throwErrorSystemNotEnableModel(); + } + if(!currClientModel().enableClient) { + throwErrorClientNotEnableModel(); + } // 获取参数 ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req); @@ -383,4 +426,18 @@ public class SaOAuth2ServerProcessor { return StpUtil.stpLogic; } + /** + * 系统未开放此授权模式时抛出异常 + */ + public void throwErrorSystemNotEnableModel() { + throw new SaOAuth2Exception("系统暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131); + } + + /** + * 应用未开放此授权模式时抛出异常 + */ + public void throwErrorClientNotEnableModel() { + throw new SaOAuth2Exception("应用暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131); + } + }