From 08659f1fa8cbff24fff94290511b177ec5800cdb Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Tue, 30 Apr 2024 08:34:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E4=BD=93=E9=87=8D=E6=9E=84=20sa-token?= =?UTF-8?q?-sso=20=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=B0=86=20server=20?= =?UTF-8?q?=E7=AB=AF=E5=92=8C=20client=20=E7=AB=AF=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/pj/h5/H5Controller.java | 4 +- .../src/main/java/com/pj/sso/SsoConfig.java | 10 +- .../java/com/pj/sso/SsoServerController.java | 4 +- .../src/main/resources/app.yml | 5 +- .../java/com/pj/sso/SsoClientController.java | 4 +- .../src/main/resources/app.yml | 2 +- .../src/main/java/com/pj/h5/H5Controller.java | 6 +- .../java/com/pj/sso/SsoClientController.java | 4 +- .../src/main/resources/app.yml | 2 +- .../java/com/pj/sso/SaSsoAutoConfigure.java | 20 +- .../java/com/pj/sso/SsoClientController.java | 25 +- .../src/main/java/com/pj/sso/SsoConfig.java | 6 +- .../src/main/resources/app.yml | 2 +- .../src/main/java/com/pj/h5/H5Controller.java | 4 +- .../java/com/pj/sso/SsoServerController.java | 21 +- .../src/main/resources/application.yml | 2 +- .../java/com/pj/SaSso1ClientApplication.java | 11 +- .../java/com/pj/sso/SsoClientController.java | 11 +- .../src/main/resources/application.yml | 8 +- .../java/com/pj/SaSso2ClientApplication.java | 11 +- .../src/main/java/com/pj/h5/H5Controller.java | 11 +- .../java/com/pj/sso/SsoClientController.java | 9 +- .../src/main/resources/application.yml | 7 +- .../java/com/pj/SaSso3ClientApplication.java | 11 +- .../java/com/pj/sso/SsoClientController.java | 16 +- .../src/main/resources/application.yml | 15 +- .../cn/dev33/satoken/config/SaSsoConfig.java | 585 ---------------- .../cn/dev33/satoken/sso/SaSsoManager.java | 66 +- .../cn/dev33/satoken/sso/SaSsoProcessor.java | 511 -------------- .../cn/dev33/satoken/sso/SaSsoTemplate.java | 647 ------------------ .../satoken/sso/config/SaSsoClientConfig.java | 390 +++++++++++ .../satoken/sso/config/SaSsoServerConfig.java | 223 ++++++ .../sso/function/DoLoginHandleFunction.java | 32 + .../sso/function/NotLoginViewFunction.java | 32 + .../sso/function/SendHttpFunction.java | 32 + .../function/TicketResultHandleFunction.java | 32 + .../satoken/sso/model/SaSsoClientModel.java | 114 +++ .../sso/processor/SaSsoClientProcessor.java | 307 +++++++++ .../sso/processor/SaSsoProcessorHelper.java | 58 ++ .../sso/processor/SaSsoServerProcessor.java | 248 +++++++ .../sso/template/SaSsoClientTemplate.java | 206 ++++++ .../sso/template/SaSsoServerTemplate.java | 414 +++++++++++ .../satoken/sso/template/SaSsoTemplate.java | 85 +++ .../satoken/sso/{ => template}/SaSsoUtil.java | 106 +-- .../satoken/sso/{ => util}/SaSsoConsts.java | 6 +- .../satoken/solon/sso/SaSsoAutoConfigure.java | 4 +- .../satoken/spring/sso/SaSsoBeanInject.java | 54 +- .../satoken/spring/sso/SaSsoBeanRegister.java | 30 +- .../configure/inject/MySaSsoTemplate.java | 2 +- 49 files changed, 2441 insertions(+), 1974 deletions(-) delete mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java delete mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java delete mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientConfig.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoServerConfig.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/DoLoginHandleFunction.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/NotLoginViewFunction.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/SendHttpFunction.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/TicketResultHandleFunction.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientModel.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoProcessorHelper.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoServerProcessor.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientTemplate.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java create mode 100644 sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoTemplate.java rename sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/{ => template}/SaSsoUtil.java (65%) rename sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/{ => util}/SaSsoConsts.java (86%) diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java index 566af4f3..d6b2649d 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java @@ -1,8 +1,8 @@ package com.pj.h5; -import cn.dev33.satoken.sso.SaSsoConsts; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.sso.template.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java index 1dacf1f1..a2618dcc 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java @@ -1,8 +1,8 @@ package com.pj.sso; -import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDaoOfRedis; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import com.dtflys.forest.Forest; @@ -27,15 +27,15 @@ public class SsoConfig { // 配置SSO相关参数 @Bean - public void configSso(SaSsoConfig sso) { //SaSsoConfig 已自动构建 + public void configSso(SaSsoServerConfig ssoServer) { //SaSsoConfig 已自动构建 // 配置:未登录时返回的View - sso.notLoginView = () -> { + ssoServer.notLoginView = () -> { return new ModelAndView("sa-login.html"); }; // 配置:登录处理函数 - sso.doLoginHandle = (name, pwd) -> { + ssoServer.doLoginHandle = (name, pwd) -> { // 此处仅做模拟登录,真实环境应该查询数据进行登录 if("sa".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); @@ -45,7 +45,7 @@ public class SsoConfig { }; // 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉) - sso.sendHttp = url -> { + ssoServer.sendHttp = url -> { try { // 发起 http 请求 System.out.println("------ 发起请求:" + url); diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java index 36318606..babbdd87 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java @@ -1,7 +1,7 @@ package com.pj.sso; -import cn.dev33.satoken.sso.SaSsoProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Mapping; @@ -22,6 +22,6 @@ public class SsoServerController { */ @Mapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.serverDister(); + return SaSsoServerProcessor.instance.dister(); } } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml index b8728cb5..2fde61a4 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml @@ -10,7 +10,7 @@ sa-token: # domain: stp.com # ------- SSO-模式二相关配置 - sso: + sso-server: # Ticket有效期 (单位: 秒),默认五分钟 ticket-timeout: 300 # 所有允许的授权回调地址 @@ -22,7 +22,8 @@ sa-token: # 是否打开模式三 isHttp: true # 接口调用秘钥(用于SSO模式三的单点注销功能) - secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + sign: + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明) sa-token: #名字可以随意取 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java index f6251b81..5acea49a 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -21,8 +21,8 @@ public class SsoClientController implements Render { @Produces(MimeType.TEXT_HTML_VALUE) @Mapping("/") public String index() { - String authUrl = SaSsoManager.getConfig().splicingAuthUrl(); - String solUrl = SaSsoManager.getConfig().splicingSloUrl(); + String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); + String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); String str = "

Sa-Token SSO-Client 应用端

" + "

当前会话是否登录:" + StpUtil.isLogin() + "

" + "

登录 " + diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml index 41924a90..04730349 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml @@ -5,7 +5,7 @@ server: # Sa-Token 配置 sa-token: # SSO-相关配置 - sso: + sso-client: # SSO-Server端-单点登录授权地址 auth-url: http://sso.stp.com:9000/sso/auth # SSO-Server端-单点注销地址 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java index 1fbd1d87..c169b68a 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java @@ -1,7 +1,7 @@ package com.pj.h5; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; @@ -34,7 +34,7 @@ public class H5Controller implements Render { // 根据ticket进行登录 @Mapping("/sso/doLoginByTicket") public SaResult doLoginByTicket(String ticket) { - Object loginId = SaSsoProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket"); + Object loginId = SaSsoClientProcessor.instance.checkTicketByMode2Or3(ticket, "/sso/doLoginByTicket"); if(loginId != null) { StpUtil.login(loginId); return SaResult.data(StpUtil.getTokenValue()); diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java index edd3f8fe..76cbec98 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -1,7 +1,7 @@ package com.pj.sso; -import cn.dev33.satoken.sso.SaSsoProcessor; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; @@ -37,7 +37,7 @@ public class SsoClientController implements Render { */ @Mapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.clientDister(); + return SaSsoClientProcessor.instance.dister(); } // 全局异常拦截并转换 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml index b2c6ff54..2a4e01ab 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml @@ -5,7 +5,7 @@ server: # sa-token配置 sa-token: # SSO-相关配置 - sso: + sso-client: # SSO-Server端 统一认证地址 auth-url: http://sa-sso-server.com:9000/sso/auth # auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SaSsoAutoConfigure.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SaSsoAutoConfigure.java index 5b7a9cd4..2d625b4e 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SaSsoAutoConfigure.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SaSsoAutoConfigure.java @@ -1,10 +1,9 @@ package com.pj.sso; -import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.sso.SaSsoManager; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoTemplate; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Condition; import org.noear.solon.annotation.Configuration; @@ -25,7 +24,7 @@ public class SaSsoAutoConfigure { * 获取 SSO 配置Bean * */ @Bean - public SaSsoConfig getConfig(@Inject(value = "${sa-token.sso}",required = false) SaSsoConfig ssoConfig) { + public SaSsoClientConfig getConfig(@Inject(value = "${sa-token.sso-client}",required = false) SaSsoClientConfig ssoConfig) { return ssoConfig; } @@ -35,18 +34,17 @@ public class SaSsoAutoConfigure { * @param saSsoConfig 配置对象 */ @Bean - public void setSaSsoConfig(@Inject(required = false) SaSsoConfig saSsoConfig) { - SaSsoManager.setConfig(saSsoConfig); + public void setSaSsoConfig(@Inject(required = false) SaSsoClientConfig saSsoConfig) { + SaSsoManager.setClientConfig(saSsoConfig); } /** * 注入 Sa-Token-SSO 单点登录模块 Bean * - * @param ssoTemplate saSsoTemplate对象 + * @param ssoClientTemplate ssoClientTemplate对象 */ @Bean - public void setSaSsoTemplate(@Inject(required = false) SaSsoTemplate ssoTemplate) { - SaSsoUtil.ssoTemplate = ssoTemplate; - SaSsoProcessor.instance.ssoTemplate = ssoTemplate; + public void setSaSsoClientTemplate(@Inject(required = false) SaSsoClientTemplate ssoClientTemplate) { + SaSsoClientProcessor.instance.ssoClientTemplate = ssoClientTemplate; } } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java index 7567de9f..8f3afeef 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -1,7 +1,7 @@ package com.pj.sso; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; @@ -11,6 +11,9 @@ import org.noear.solon.boot.web.MimeType; import org.noear.solon.core.handle.Context; import org.noear.solon.core.handle.Render; +import java.util.HashMap; +import java.util.Map; + /** * Sa-Token-SSO Client端 Controller * @author click33 @@ -37,15 +40,21 @@ public class SsoClientController implements Render { */ @Mapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.clientDister(); + return SaSsoClientProcessor.instance.dister(); } // 查询我的账号信息 - @Mapping("/sso/myinfo") - public Object myinfo() { - Object userinfo = SaSsoUtil.getUserinfo(StpUtil.getLoginId()); - System.out.println("--------info:" + userinfo); - return userinfo; + @Mapping("/sso/myInfo") + public Object myInfo() { + // 组织请求参数 + Map map = new HashMap<>(); + map.put("apiType", "userinfo"); + map.put("loginId", StpUtil.getLoginId()); + + // 发起请求 + Object resData = SaSsoUtil.getData(map); + System.out.println("sso-server 返回的信息:" + resData); + return resData; } // 全局异常拦截并转换 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java index 4669b38b..bb8a0a85 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java @@ -1,6 +1,6 @@ package com.pj.sso; -import cn.dev33.satoken.config.SaSsoConfig; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; import com.dtflys.forest.Forest; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; @@ -12,9 +12,9 @@ import org.noear.solon.annotation.Configuration; public class SsoConfig { // 配置SSO相关参数 @Bean - private void configSso(SaSsoConfig sso) { + private void configSso(SaSsoClientConfig ssoClient) { // 配置Http请求处理器 - sso.sendHttp = url -> { + ssoClient.sendHttp = url -> { System.out.println("------ 发起请求:" + url); return Forest.get(url).executeAsString(); }; diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml index 294a2252..dff9616b 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml @@ -5,7 +5,7 @@ server: # sa-token配置 sa-token: # SSO-相关配置 - sso: + sso-client: # SSO-Server端 统一认证地址 auth-url: http://sa-sso-server.com:9000/sso/auth # 使用Http请求校验ticket diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/H5Controller.java index 1ebe3f9a..22602555 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/H5Controller.java @@ -4,8 +4,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.dev33.satoken.sso.SaSsoConsts; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.sso.template.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java index ca8973c4..7302afd7 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/sso/SsoServerController.java @@ -1,9 +1,9 @@ package com.pj.sso; -import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.sign.SaSignUtil; -import cn.dev33.satoken.sso.SaSsoProcessor; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import com.dtflys.forest.Forest; @@ -20,7 +20,7 @@ import org.springframework.web.servlet.ModelAndView; @RestController public class SsoServerController { - /* + /** * SSO-Server端:处理所有SSO相关请求 * http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址 * http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd @@ -29,20 +29,20 @@ public class SsoServerController { */ @RequestMapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.serverDister(); + return SaSsoServerProcessor.instance.dister(); } // 配置SSO相关参数 @Autowired - private void configSso(SaSsoConfig sso) { + private void configSso(SaSsoServerConfig ssoServer) { // 配置:未登录时返回的View - sso.notLoginView = () -> { + ssoServer.notLoginView = () -> { return new ModelAndView("sa-login.html"); }; // 配置:登录处理函数 - sso.doLoginHandle = (name, pwd) -> { + ssoServer.doLoginHandle = (name, pwd) -> { // 此处仅做模拟登录,真实环境应该查询数据进行登录 if("sa".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); @@ -52,11 +52,12 @@ public class SsoServerController { }; // 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉) - sso.sendHttp = url -> { + ssoServer.sendHttp = url -> { try { - // 发起 http 请求 System.out.println("------ 发起请求:" + url); - return Forest.get(url).executeAsString(); + String resStr = Forest.get(url).executeAsString(); + System.out.println("------ 请求结果:" + resStr); + return resStr; } catch (Exception e) { e.printStackTrace(); return null; diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml index c7937759..04395bf6 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/resources/application.yml @@ -10,7 +10,7 @@ sa-token: # domain: stp.com # ------- SSO-模式二相关配置 - sso: + sso-server: # Ticket有效期 (单位: 秒),默认五分钟 ticket-timeout: 300 # 所有允许的授权回调地址 diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/SaSso1ClientApplication.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/SaSso1ClientApplication.java index b5347d94..71d98206 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/SaSso1ClientApplication.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/SaSso1ClientApplication.java @@ -1,5 +1,6 @@ package com.pj; +import cn.dev33.satoken.sso.SaSsoManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -13,7 +14,15 @@ public class SaSso1ClientApplication { public static void main(String[] args) { SpringApplication.run(SaSso1ClientApplication.class, args); - System.out.println("\nSa-Token SSO模式一 Client端启动成功"); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 模式一 Client 端启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getClientConfig()); + System.out.println("测试访问应用端一: http://s1.stp.com:9001"); + System.out.println("测试访问应用端二: http://s2.stp.com:9001"); + System.out.println("测试访问应用端三: http://s3.stp.com:9001"); + System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456"); + System.out.println(); } } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/sso/SsoClientController.java index 0174ca9a..aefb851e 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/java/com/pj/sso/SsoClientController.java @@ -1,12 +1,11 @@ package com.pj.sso; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - import cn.dev33.satoken.sso.SaSsoManager; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; /** * Sa-Token-SSO Client端 Controller @@ -18,8 +17,8 @@ public class SsoClientController { // SSO-Client端:首页 @RequestMapping("/") public String index() { - String authUrl = SaSsoManager.getConfig().splicingAuthUrl(); - String solUrl = SaSsoManager.getConfig().splicingSloUrl(); + String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); + String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); String str = "

Sa-Token SSO-Client 应用端

" + "

当前会话是否登录:" + StpUtil.isLogin() + "

" + "

登录 " + diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml index 4c56b23a..b8745220 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml @@ -5,11 +5,9 @@ server: # Sa-Token 配置 sa-token: # SSO-相关配置 - sso: - # SSO-Server端-单点登录授权地址 - auth-url: http://sso.stp.com:9000/sso/auth - # SSO-Server端-单点注销地址 - slo-url: http://sso.stp.com:9000/sso/signout + sso-client: + # SSO-Server端主机地址 + server-url: http://sso.stp.com:9000 # 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) alone-redis: diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/SaSso2ClientApplication.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/SaSso2ClientApplication.java index 78b1e868..d19c7528 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/SaSso2ClientApplication.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/SaSso2ClientApplication.java @@ -1,5 +1,6 @@ package com.pj; +import cn.dev33.satoken.sso.SaSsoManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -8,7 +9,15 @@ public class SaSso2ClientApplication { public static void main(String[] args) { SpringApplication.run(SaSso2ClientApplication.class, args); - System.out.println("\nSa-Token SSO模式二 Client端启动成功"); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getClientConfig()); + System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001"); + System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001"); + System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001"); + System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456"); + System.out.println(); } } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java index acc1958d..64f72e13 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java @@ -1,14 +1,13 @@ package com.pj.h5; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoUtil; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoUtil; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.util.SaResult; - /** * 前后台分离架构下集成SSO所需的代码 (SSO-Client端) *

(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)

@@ -34,7 +33,7 @@ public class H5Controller { // 根据ticket进行登录 @RequestMapping("/sso/doLoginByTicket") public SaResult doLoginByTicket(String ticket) { - Object loginId = SaSsoProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket"); + Object loginId = SaSsoClientProcessor.instance.checkTicketByMode2Or3(ticket, "/sso/doLoginByTicket"); if(loginId != null) { StpUtil.login(loginId); return SaResult.data(StpUtil.getTokenValue()); diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/sso/SsoClientController.java index 05571649..11383970 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/java/com/pj/sso/SsoClientController.java @@ -1,13 +1,12 @@ package com.pj.sso; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.util.SaResult; - /** * Sa-Token-SSO Client端 Controller * @author click33 @@ -33,7 +32,7 @@ public class SsoClientController { */ @RequestMapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.clientDister(); + return SaSsoClientProcessor.instance.dister(); } // 全局异常拦截 diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/resources/application.yml index af6f09fe..bf59d24b 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/src/main/resources/application.yml @@ -5,12 +5,11 @@ server: # sa-token配置 sa-token: # SSO-相关配置 - sso: + sso-client: # SSO-Server端 统一认证地址 - auth-url: http://sa-sso-server.com:9000/sso/auth + server-url: http://sa-sso-server.com:9000 + # 前后端分离时用这个 # auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html - # 是否打开单点注销接口 - is-slo: true # 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) alone-redis: diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/SaSso3ClientApplication.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/SaSso3ClientApplication.java index e0e18259..cdcd78ce 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/SaSso3ClientApplication.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/SaSso3ClientApplication.java @@ -1,5 +1,6 @@ package com.pj; +import cn.dev33.satoken.sso.SaSsoManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -8,7 +9,15 @@ public class SaSso3ClientApplication { public static void main(String[] args) { SpringApplication.run(SaSso3ClientApplication.class, args); - System.out.println("\nSa-Token SSO模式三 Client端启动成功"); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getClientConfig()); + System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001"); + System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001"); + System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001"); + System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456"); + System.out.println(); } } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java index a873a998..2492688a 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/java/com/pj/sso/SsoClientController.java @@ -1,8 +1,8 @@ package com.pj.sso; -import cn.dev33.satoken.config.SaSsoConfig; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import com.dtflys.forest.Forest; @@ -39,16 +39,18 @@ public class SsoClientController { */ @RequestMapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.clientDister(); + return SaSsoClientProcessor.instance.dister(); } // 配置SSO相关参数 @Autowired - private void configSso(SaSsoConfig sso) { + private void configSso(SaSsoClientConfig ssoClient) { // 配置Http请求处理器 - sso.sendHttp = url -> { + ssoClient.sendHttp = url -> { System.out.println("------ 发起请求:" + url); - return Forest.get(url).executeAsString(); + String resStr = Forest.get(url).executeAsString(); + System.out.println("------ 请求结果:" + resStr); + return resStr; }; } diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml index 931f2519..4d669453 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client/src/main/resources/application.yml @@ -4,18 +4,13 @@ server: # sa-token配置 sa-token: - # SSO-相关配置 - sso: - # SSO-Server端 统一认证地址 - auth-url: http://sa-sso-server.com:9000/sso/auth + # sso-client 相关配置 + sso-client: + # sso-server 端主机地址 + server-url: http://sa-sso-server.com:9000 # 使用 Http 请求校验ticket (模式三) is-http: true - # SSO-Server端 ticket校验地址 - check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket - # 单点注销地址 - slo-url: http://sa-sso-server.com:9000/sso/signout - # 查询数据地址 - get-data-url: http://sa-sso-server.com:9000/sso/getData + sign: # API 接口调用秘钥 secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java deleted file mode 100644 index 81ff5a68..00000000 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java +++ /dev/null @@ -1,585 +0,0 @@ -/* - * 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.config; - - -import java.io.Serializable; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -import cn.dev33.satoken.sso.error.SaSsoErrorCode; -import cn.dev33.satoken.sso.exception.SaSsoException; -import cn.dev33.satoken.util.SaFoxUtil; -import cn.dev33.satoken.util.SaResult; - -/** - * Sa-Token SSO 单点登录模块 配置类 Model - * - * @author click33 - * @since 1.30.0 - */ -public class SaSsoConfig implements Serializable { - - private static final long serialVersionUID = -6541180061782004705L; - - - // ----------------- Server端相关配置 - - /** - * 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) - */ - public String mode = ""; - - /** - * Ticket有效期 (单位: 秒) - */ - public long ticketTimeout = 60 * 5; - - /** - * 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) - */ - public String allowUrl = "*"; - - /** - * 是否打开单点注销功能 - */ - public Boolean isSlo = true; - - /** - * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) - */ - public Boolean isHttp = false; - - - // ----------------- Client端相关配置 - -// /** -// * 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) -// */ -// public String mode = ""; // 同Server端,不再重复声明 - - /** - * 当前 Client 名称标识,用于和 ticket 码的互相锁定 - */ - public String client; - - /** - * 配置 Server 端单点登录授权地址 - */ - public String authUrl = "/sso/auth"; - - // /** - // * 是否打开单点注销功能 - // */ - // public Boolean isSlo = true; // 同Server端,不再重复声明 - - // /** - // * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) - // */ - // public Boolean isHttp = false; // 同Server端,不再重复声明 - - // /** - // * 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验) - // */ - // public String secretkey; // 同Server端,不再重复声明 - - /** - * 配置 Server 端的 ticket 校验地址 - */ - public String checkTicketUrl = "/sso/checkTicket"; - - /** - * 配置 Server 端查询数据 getData 地址 - */ - public String getDataUrl = "/sso/getData"; - - /** - * 配置 Server 端查询 userinfo 地址 - */ - public String userinfoUrl = "/sso/userinfo"; - - /** - * 配置 Server 端单点注销地址 - */ - public String sloUrl = "/sso/signout"; - - /** - * 配置当前 Client 端的登录地址(为空时自动获取) - */ - public String currSsoLogin; - - /** - * 配置当前 Client 端的单点注销回调URL (为空时自动获取) - */ - public String currSsoLogoutCall; - - /** - * 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 - */ - public String serverUrl; - - /** - * 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) - */ - public Boolean isCheckSign = true; - - - /** - * 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) - * - * @return / - */ - public String getMode() { - return this.mode; - } - - /** - * 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) - * - * @param mode / - */ - public void setMode(String mode) { - this.mode = mode; - } - - /** - * @return Ticket有效期 (单位: 秒) - */ - public long getTicketTimeout() { - return ticketTimeout; - } - - /** - * @param ticketTimeout Ticket有效期 (单位: 秒) - * @return 对象自身 - */ - public SaSsoConfig setTicketTimeout(long ticketTimeout) { - this.ticketTimeout = ticketTimeout; - return this; - } - - /** - * @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) - */ - public String getAllowUrl() { - return allowUrl; - } - - /** - * @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) - * @return 对象自身 - */ - public SaSsoConfig setAllowUrl(String allowUrl) { - this.allowUrl = allowUrl; - return this; - } - - /** - * @return 是否打开单点注销功能 - */ - public Boolean getIsSlo() { - return isSlo; - } - - /** - * @param isSlo 是否打开单点注销功能 - * @return 对象自身 - */ - public SaSsoConfig setIsSlo(Boolean isSlo) { - this.isSlo = isSlo; - return this; - } - - /** - * @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) - */ - public Boolean getIsHttp() { - return isHttp; - } - - /** - * @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) - * @return 对象自身 - */ - public SaSsoConfig setIsHttp(Boolean isHttp) { - this.isHttp = isHttp; - return this; - } - - /** - * @return 当前 Client 名称标识,用于和 ticket 码的互相锁定 - */ - public String getClient() { - return client; - } - - /** - * @param client 当前 Client 名称标识,用于和 ticket 码的互相锁定 - */ - public SaSsoConfig setClient(String client) { - this.client = client; - return this; - } - - /** - * @return 配置的 Server 端单点登录授权地址 - */ - public String getAuthUrl() { - return authUrl; - } - - /** - * @param authUrl 配置 Server 端单点登录授权地址 - * @return 对象自身 - */ - public SaSsoConfig setAuthUrl(String authUrl) { - this.authUrl = authUrl; - return this; - } - - /** - * @return 配置的 Server 端的 ticket 校验地址 - */ - public String getCheckTicketUrl() { - return checkTicketUrl; - } - - /** - * @param checkTicketUrl 配置 Server 端的 ticket 校验地址 - * @return 对象自身 - */ - public SaSsoConfig setCheckTicketUrl(String checkTicketUrl) { - this.checkTicketUrl = checkTicketUrl; - return this; - } - - /** - * @return Server 端查询数据 getData 地址 - */ - public String getGetDataUrl() { - return getDataUrl; - } - - /** - * @param getDataUrl 配置 Server 端查询数据 getData 地址 - * @return 对象自身 - */ - public SaSsoConfig setGetDataUrl(String getDataUrl) { - this.getDataUrl = getDataUrl; - return this; - } - - /** - * @return 配置的 Server 端查询 userinfo 地址 - */ - public String getUserinfoUrl() { - return userinfoUrl; - } - - /** - * @param userinfoUrl 配置 Server 端查询 userinfo 地址 - * @return 对象自身 - */ - public SaSsoConfig setUserinfoUrl(String userinfoUrl) { - this.userinfoUrl = userinfoUrl; - return this; - } - - /** - * @return 配置 Server 端单点注销地址 - */ - public String getSloUrl() { - return sloUrl; - } - - /** - * @param sloUrl 配置 Server 端单点注销地址 - * @return 对象自身 - */ - public SaSsoConfig setSloUrl(String sloUrl) { - this.sloUrl = sloUrl; - return this; - } - - /** - * @return 配置当前 Client 端的登录地址(为空时自动获取) - */ - public String getCurrSsoLogin() { - return currSsoLogin; - } - - /** - * @param currSsoLogin 配置当前 Client 端的登录地址(为空时自动获取) - * @return 对象自身 - */ - public SaSsoConfig setCurrSsoLogin(String currSsoLogin) { - this.currSsoLogin = currSsoLogin; - return this; - } - - /** - * @return 配置当前 Client 端的单点注销回调URL (为空时自动获取) - */ - public String getCurrSsoLogoutCall() { - return currSsoLogoutCall; - } - - /** - * @param currSsoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取) - * @return 对象自身 - */ - public SaSsoConfig setCurrSsoLogoutCall(String currSsoLogoutCall) { - this.currSsoLogoutCall = currSsoLogoutCall; - return this; - } - - /** - * @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 - */ - public String getServerUrl() { - return serverUrl; - } - - /** - * @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 - * @return 对象自身 - */ - public SaSsoConfig setServerUrl(String serverUrl) { - this.serverUrl = serverUrl; - return this; - } - - /** - * 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) - * - * @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) - */ - public Boolean getIsCheckSign() { - return this.isCheckSign; - } - - /** - * 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) - * - * @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) - */ - public SaSsoConfig setIsCheckSign(Boolean isCheckSign) { - this.isCheckSign = isCheckSign; - return this; - } - - @Override - public String toString() { - return "SaSsoConfig [" - + "mode=" + mode - + ", ticketTimeout=" + ticketTimeout - + ", allowUrl=" + allowUrl - + ", isSlo=" + isSlo - + ", isHttp=" + isHttp - + ", client=" + client - + ", authUrl=" + authUrl - + ", checkTicketUrl=" + checkTicketUrl - + ", getDataUrl=" + getDataUrl - + ", userinfoUrl=" + userinfoUrl - + ", sloUrl=" + sloUrl - + ", currSsoLogin=" + currSsoLogin - + ", currSsoLogoutCall=" + currSsoLogoutCall - + ", serverUrl=" + serverUrl - + ", isCheckSign=" + isCheckSign - + "]"; - } - - - // 额外添加的一些函数 - - /** - * @return 获取拼接url:Server 端单点登录授权地址 - */ - public String splicingAuthUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl()); - } - - /** - * @return 获取拼接url:Server 端的 ticket 校验地址 - */ - public String splicingCheckTicketUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl()); - } - - /** - * @return 获取拼接url:Server 端查询数据 getData 地址 - */ - public String splicingGetDataUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl()); - } - - /** - * @return 获取拼接url:Server 端查询 userinfo 地址 - */ - public String splicingUserinfoUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getUserinfoUrl()); - } - - /** - * @return 获取拼接url:Server 端单点注销地址 - */ - public String splicingSloUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl()); - } - - /** - * 以数组形式写入允许的授权回调地址 - * @param url 所有集合 - * @return 对象自身 - */ - public SaSsoConfig setAllow(String ...url) { - this.allowUrl = SaFoxUtil.arrayJoin(url); - return this; - } - - - // -------------------- SaSsoHandle 所有回调函数 -------------------- - - - /** - * SSO-Server端:未登录时返回的View - */ - public Supplier notLoginView = () -> { - return "当前会话在SSO-Server认证中心尚未登录(当前未配置登录视图)"; - }; - - /** - * SSO-Server端:登录函数 - */ - public BiFunction doLoginHandle = (name, pwd) -> { - return SaResult.error(); - }; - - /** - * SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用) - *

参数:loginId, back - *

返回值:返回给前端的值 - */ - public BiFunction ticketResultHandle = null; - - /** - * SSO-Client端:发送Http请求的处理函数 - */ - public Function sendHttp = url -> { - throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010); - }; - - - // -------------------- 废弃方法 -------------------- - - /** - *

属性为 public,请直接访问

- * @param notLoginView SSO-Server端:未登录时返回的View - * @return 对象自身 - */ - @Deprecated - public SaSsoConfig setNotLoginView(Supplier notLoginView) { - this.notLoginView = notLoginView; - return this; - } - - /** - *

属性为 public,请直接访问

- * @return 函数 SSO-Server端:未登录时返回的View - */ - @Deprecated - public Supplier getNotLoginView() { - return notLoginView; - } - - /** - *

属性为 public,请直接访问

- * @param doLoginHandle SSO-Server端:登录函数 - * @return 对象自身 - */ - @Deprecated - public SaSsoConfig setDoLoginHandle(BiFunction doLoginHandle) { - this.doLoginHandle = doLoginHandle; - return this; - } - - /** - *

属性为 public,请直接访问

- * @return 函数 SSO-Server端:登录函数 - */ - @Deprecated - public BiFunction getDoLoginHandle() { - return doLoginHandle; - } - - /** - *

属性为 public,请直接访问

- * @param ticketResultHandle SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用) - * @return 对象自身 - */ - @Deprecated - public SaSsoConfig setTicketResultHandle(BiFunction ticketResultHandle) { - this.ticketResultHandle = ticketResultHandle; - return this; - } - - /** - *

属性为 public,请直接访问

- * @return 函数 SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用) - */ - @Deprecated - public BiFunction getTicketResultHandle() { - return ticketResultHandle; - } - - /** - *

属性为 public,请直接访问

- * @param sendHttp SSO-Client端:发送Http请求的处理函数 - * @return 对象自身 - */ - @Deprecated - public SaSsoConfig setSendHttp(Function sendHttp) { - this.sendHttp = sendHttp; - return this; - } - - /** - *

属性为 public,请直接访问

- * @return 函数 SSO-Client端:发送Http请求的处理函数 - */ - @Deprecated - public Function getSendHttp() { - return sendHttp; - } - - /** - * @return 配置当前 Client 端的单点注销回调URL (为空时自动获取) - */ - public String getSsoLogoutCall() { - return currSsoLogoutCall; - } - - /** - * @param ssoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取) - * @return 对象自身 - */ - public SaSsoConfig setSsoLogoutCall(String ssoLogoutCall) { - this.currSsoLogoutCall = ssoLogoutCall; - return this; - } - -} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoManager.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoManager.java index afd696f1..564d0530 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoManager.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoManager.java @@ -15,7 +15,8 @@ */ package cn.dev33.satoken.sso; -import cn.dev33.satoken.config.SaSsoConfig; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; /** * Sa-Token-SSO 模块 总控类 @@ -26,29 +27,62 @@ import cn.dev33.satoken.config.SaSsoConfig; public class SaSsoManager { /** - * Sso 配置 Bean + * Sso Server 端 配置 Bean */ - private volatile static SaSsoConfig config; - public static SaSsoConfig getConfig() { - if (config == null) { + private volatile static SaSsoServerConfig serverConfig; + public static SaSsoServerConfig getServerConfig() { + if (serverConfig == null) { synchronized (SaSsoManager.class) { - if (config == null) { - setConfig(new SaSsoConfig()); + if (serverConfig == null) { + setServerConfig(new SaSsoServerConfig()); } } } - return config; + return serverConfig; } - public static void setConfig(SaSsoConfig config) { - SaSsoManager.config = config; + public static void setServerConfig(SaSsoServerConfig serverConfig) { + SaSsoManager.serverConfig = serverConfig; // 如果配置了 is-check-sign=false,则打印一条警告日志 - if ( ! config.getIsCheckSign()) { - System.err.println("-----------------------------------------------------------------------------"); - System.err.println("警告信息:"); - System.err.println("当前配置项 sa-token.sso.is-check-sign=false 代表跳过 SSO 参数签名校验"); - System.err.println("此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); - System.err.println("-----------------------------------------------------------------------------"); + if ( ! serverConfig.getIsCheckSign()) { + printNoCheckSignWarningByStartup(); } } + /** + * Sso Client 端 配置 Bean + */ + private volatile static SaSsoClientConfig clientConfig; + public static SaSsoClientConfig getClientConfig() { + if (clientConfig == null) { + synchronized (SaSsoManager.class) { + if (clientConfig == null) { + setClientConfig(new SaSsoClientConfig()); + } + } + } + return clientConfig; + } + public static void setClientConfig(SaSsoClientConfig clientConfig) { + SaSsoManager.clientConfig = clientConfig; + // 如果配置了 is-check-sign=false,则打印一条警告日志 + if ( ! clientConfig.getIsCheckSign()) { + printNoCheckSignWarningByStartup(); + } + } + + // 在启动时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息 + public static void printNoCheckSignWarningByStartup() { + System.err.println("-----------------------------------------------------------------------------"); + System.err.println("警告信息:"); + System.err.println("当前配置项 sa-token.sso.is-check-sign=false 代表跳过 SSO 参数签名校验"); + System.err.println("此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); + System.err.println("-----------------------------------------------------------------------------"); + } + + // 在运行时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息 + public static void printNoCheckSignWarningByRuntime() { + System.err.println("警告信息:当前配置项 sa-token.sso.is-check-sign=false 已跳过参数签名校验," + + "此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); + } + } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java deleted file mode 100644 index 77e47d0d..00000000 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoProcessor.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * 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.sso; - -import cn.dev33.satoken.config.SaSsoConfig; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaRequest; -import cn.dev33.satoken.context.model.SaResponse; -import cn.dev33.satoken.sso.error.SaSsoErrorCode; -import cn.dev33.satoken.sso.exception.SaSsoException; -import cn.dev33.satoken.sso.name.ApiName; -import cn.dev33.satoken.sso.name.ParamName; -import cn.dev33.satoken.stp.StpLogic; -import cn.dev33.satoken.util.SaFoxUtil; -import cn.dev33.satoken.util.SaResult; - -/** - * SSO 请求处理器 - * - * @author click33 - * @since 1.32.0 - */ -public class SaSsoProcessor { - - /** - * 底层 SaSsoTemplate 对象 - */ - public SaSsoTemplate ssoTemplate = SaSsoUtil.ssoTemplate; - - - // ----------- SSO-Server 端路由分发 ----------- - - /** - * 分发 Server 端所有请求 - * @return 处理结果 - */ - public Object serverDister() { - - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - ApiName apiName = ssoTemplate.apiName; - - // ------------------ 路由分发 ------------------ - - // ---------- SSO-Server端:授权地址 - if(req.isPath(apiName.ssoAuth)) { - return ssoAuth(); - } - - // ---------- SSO-Server端:RestAPI 登录接口 - if(req.isPath(apiName.ssoDoLogin)) { - return ssoDoLogin(); - } - - // ---------- SSO-Server端:校验ticket 获取账号id - if(req.isPath(apiName.ssoCheckTicket) && cfg.getIsHttp()) { - return ssoCheckTicket(); - } - - // ---------- SSO-Server端:单点注销 - if(req.isPath(apiName.ssoSignout)) { - return ssoSignout(); - } - - // 默认返回 - return SaSsoConsts.NOT_HANDLE; - } - - /** - * SSO-Server端:授权地址 - * @return 处理结果 - */ - public Object ssoAuth() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - StpLogic stpLogic = ssoTemplate.getStpLogic(); - ParamName paramName = ssoTemplate.paramName; - - // ---------- 此处有两种情况分开处理: - // ---- 情况1:在SSO认证中心尚未登录,需要先去登录 - if( ! stpLogic.isLogin()) { - return cfg.notLoginView.get(); - } - // ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式: - String mode = req.getParam(paramName.mode, ""); - - // 方式1:直接重定向回Client端 (mode=simple) - if(mode.equals(SaSsoConsts.MODE_SIMPLE)) { - String redirect = req.getParam(paramName.redirect); - ssoTemplate.checkRedirectUrl(redirect); - return res.redirect(redirect); - } else { - // 方式2:带着ticket参数重定向回Client端 (mode=ticket) - String redirectUrl = ssoTemplate.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(paramName.client), req.getParam(paramName.redirect)); - return res.redirect(redirectUrl); - } - } - - /** - * SSO-Server端:RestAPI 登录接口 - * @return 处理结果 - */ - public Object ssoDoLogin() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - ParamName paramName = ssoTemplate.paramName; - - // 处理 - return cfg.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd)); - } - - /** - * SSO-Server端:校验ticket 获取账号id [模式三] - * @return 处理结果 - */ - public Object ssoCheckTicket() { - ParamName paramName = ssoTemplate.paramName; - - // 1、获取参数 - SaRequest req = SaHolder.getRequest(); - String client = req.getParam(paramName.client); - String ticket = req.getParamNotNull(paramName.ticket); - String sloCallback = req.getParam(paramName.ssoLogoutCall); - - // 2、校验签名 - if(ssoTemplate.getSsoConfig().getIsCheckSign()) { - ssoTemplate.getSignTemplate().checkRequest(req, - paramName.client, paramName.ticket, paramName.ssoLogoutCall); - } else { - ssoTemplate.printNoCheckSignWarning(); - } - - // 3、校验ticket,获取 loginId - Object loginId = ssoTemplate.checkTicket(ticket, client); - if(SaFoxUtil.isEmpty(loginId)) { - return SaResult.error("无效ticket:" + ticket); - } - - // 4、注册此客户端的单点注销回调URL - ssoTemplate.registerSloCallbackUrl(loginId, sloCallback); - - // 5、给 client 端响应结果 - return SaResult.data(loginId); - } - - /** - * SSO-Server端:单点注销 - * @return 处理结果 - */ - public Object ssoSignout() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - ParamName paramName = ssoTemplate.paramName; - - // SSO-Server端:单点注销 [用户访问式] (不带loginId参数) - if(cfg.getIsSlo() && ! req.hasParam(paramName.loginId)) { - return ssoSignoutByUserVisit(); - } - - // SSO-Server端:单点注销 [Client调用式] (带loginId参数 & isHttp=true) - if(cfg.getIsHttp() && cfg.getIsSlo() && req.hasParam(paramName.loginId)) { - return ssoSignoutByClientHttp(); - } - - // 默认返回 - return SaSsoConsts.NOT_HANDLE; - } - - /** - * SSO-Server端:单点注销 [用户访问式] - * @return 处理结果 - */ - public Object ssoSignoutByUserVisit() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - Object loginId = ssoTemplate.getStpLogic().getLoginIdDefaultNull(); - - // 单点注销 - if(SaFoxUtil.isNotEmpty(loginId)) { - ssoTemplate.ssoLogout(loginId); - } - - // 完成 - return ssoLogoutBack(req, res); - } - - /** - * SSO-Server端:单点注销 [Client调用式] - * @return 处理结果 - */ - public Object ssoSignoutByClientHttp() { - ParamName paramName = ssoTemplate.paramName; - - // 获取参数 - SaRequest req = SaHolder.getRequest(); - String loginId = req.getParam(paramName.loginId); - - // step.1 校验签名 - if(ssoTemplate.getSsoConfig().getIsCheckSign()) { - ssoTemplate.getSignTemplate().checkRequest(req, paramName.loginId); - } else { - ssoTemplate.printNoCheckSignWarning(); - } - - // step.2 单点注销 - ssoTemplate.ssoLogout(loginId); - - // 响应 - return SaResult.ok(); - } - - - // ----------- SSO-Client 端路由分发 ----------- - - /** - * 分发 Client 端所有请求 - * @return 处理结果 - */ - public Object clientDister() { - ApiName apiName = ssoTemplate.apiName; - - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - - // ------------------ 路由分发 ------------------ - - // ---------- SSO-Client端:登录地址 - if(req.isPath(apiName.ssoLogin)) { - return ssoLogin(); - } - - // ---------- SSO-Client端:单点注销 - if(req.isPath(apiName.ssoLogout)) { - return ssoLogout(); - } - - // ---------- SSO-Client端:单点注销的回调 [模式三] - if(req.isPath(apiName.ssoLogoutCall) && cfg.getIsSlo() && cfg.getIsHttp()) { - return ssoLogoutCall(); - } - - // 默认返回 - return SaSsoConsts.NOT_HANDLE; - } - - /** - * SSO-Client端:登录地址 - * @return 处理结果 - */ - public Object ssoLogin() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - SaSsoConfig cfg = SaSsoManager.getConfig(); - StpLogic stpLogic = ssoTemplate.getStpLogic(); - ApiName apiName = ssoTemplate.apiName; - ParamName paramName = ssoTemplate.paramName; - - // 获取参数 - String back = req.getParam(paramName.back, "/"); - String ticket = req.getParam(paramName.ticket); - - // 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回 - if(stpLogic.isLogin()) { - return res.redirect(back); - } - /* - * 此时有两种情况: - * 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心 - * 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录 - */ - if(ticket == null) { - // 获取当前项目的 sso 登录地址 - // 全局配置了就是用全局的,否则使用当前请求的地址 - String currSsoLoginUrl; - if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogin())) { - currSsoLoginUrl = cfg.getCurrSsoLogin(); - } else { - currSsoLoginUrl = SaHolder.getRequest().getUrl(); - } - // 构建url - String serverAuthUrl = ssoTemplate.buildServerAuthUrl(currSsoLoginUrl, back); - return res.redirect(serverAuthUrl); - } else { - // ------- 1、校验ticket,获取 loginId - Object loginId = checkTicket(ticket, apiName.ssoLogin); - - // Be: 如果开发者自定义了处理逻辑 - if(cfg.ticketResultHandle != null) { - return cfg.ticketResultHandle.apply(loginId, back); - } - - // ------- 2、如果 loginId 无值,说明 ticket 无效 - if(SaFoxUtil.isEmpty(loginId)) { - throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoErrorCode.CODE_30004); - } else { - // 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址 - stpLogic.login(loginId); - return res.redirect(back); - } - } - } - - /** - * SSO-Client端:单点注销 - * @return 处理结果 - */ - public Object ssoLogout() { - // 获取对象 - SaSsoConfig cfg = SaSsoManager.getConfig(); - - // ---------- SSO-Client端:单点注销 [模式二] - if(cfg.getIsSlo() && ! cfg.getIsHttp()) { - return ssoLogoutType2(); - } - - // ---------- SSO-Client端:单点注销 [模式三] - if(cfg.getIsSlo() && cfg.getIsHttp()) { - return ssoLogoutType3(); - } - - // 默认返回 - return SaSsoConsts.NOT_HANDLE; - } - - /** - * SSO-Client端:单点注销 [模式二] - * @return 处理结果 - */ - public Object ssoLogoutType2() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - StpLogic stpLogic = ssoTemplate.getStpLogic(); - - // 开始处理 - if(stpLogic.isLogin()) { - stpLogic.logout(stpLogic.getLoginId()); - } - - // 返回 - return ssoLogoutBack(req, res); - } - - /** - * SSO-Client端:单点注销 [模式三] - * @return 处理结果 - */ - public Object ssoLogoutType3() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - StpLogic stpLogic = ssoTemplate.getStpLogic(); - - // 如果未登录,则无需注销 - if( ! stpLogic.isLogin()) { - return ssoLogoutBack(req, res); - } - - // 调用 sso-server 认证中心单点注销API - String url = ssoTemplate.buildSloUrl(stpLogic.getLoginId()); - SaResult result = ssoTemplate.request(url); - - // 校验响应状态码 - if(SaResult.CODE_SUCCESS == result.getCode()) { - // 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀 - if(stpLogic.isLogin()) { - stpLogic.logout(); - } - return ssoLogoutBack(req, res); - } else { - // 将 sso-server 回应的消息作为异常抛出 - throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30006); - } - } - - /** - * SSO-Client端:单点注销的回调 [模式三] - * @return 处理结果 - */ - public Object ssoLogoutCall() { - ParamName paramName = ssoTemplate.paramName; - - // 获取对象 - SaRequest req = SaHolder.getRequest(); - StpLogic stpLogic = ssoTemplate.getStpLogic(); - - // 获取参数 - String loginId = req.getParamNotNull(paramName.loginId); - - // 校验参数签名 - if(ssoTemplate.getSsoConfig().getIsCheckSign()) { - ssoTemplate.getSignTemplate().checkRequest(req, paramName.loginId); - } else { - ssoTemplate.printNoCheckSignWarning(); - } - - // 注销当前应用端会话 - stpLogic.logout(loginId); - - // 响应 - return SaResult.ok("单点注销回调成功"); - } - - - // ----------- 工具方法 - - /** - * 封装:单点注销成功后返回结果 - * @param req SaRequest对象 - * @param res SaResponse对象 - * @return 返回结果 - */ - public Object ssoLogoutBack(SaRequest req, SaResponse res) { - ParamName paramName = ssoTemplate.paramName; - - /* - * 三种情况: - * 1. 有back参数,值为SELF -> 回退一级并刷新 - * 2. 有back参数,值为url -> 跳转到此url地址 - * 3. 无back参数 -> 返回json数据 - */ - String back = req.getParam(paramName.back); - if(SaFoxUtil.isNotEmpty(back)) { - if(back.equals(SaSsoConsts.SELF)) { - return ""; - } - return res.redirect(back); - } else { - return SaResult.ok("单点注销成功"); - } - } - - /** - * 封装:校验ticket,取出loginId - * @param ticket ticket码 - * @param currUri 当前路由的uri,用于计算单点注销回调地址 - * @return loginId - */ - public Object checkTicket(String ticket, String currUri) { - SaSsoConfig cfg = SaSsoManager.getConfig(); - ApiName apiName = ssoTemplate.apiName; - - // --------- 两种模式 - if(cfg.getIsHttp()) { - // q1、使用模式三:使用 http 请求从认证中心校验ticket - - // 计算当前 sso-client 的单点注销回调地址 - String ssoLogoutCall = null; - if(cfg.getIsSlo()) { - // 如果配置了回调地址,就使用配置的值: - if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) { - ssoLogoutCall = cfg.getCurrSsoLogoutCall(); - } - // 如果提供了当前 uri,则根据此值来计算: - else if(SaFoxUtil.isNotEmpty(currUri)) { - ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall); - } - // 否则视为不注册单点注销回调地址 - else { - } - } - - // 构建请求URL - String checkUrl = ssoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall); - - // 发起请求 - SaResult result = ssoTemplate.request(checkUrl); - - // 校验 - if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) { - return result.getData(); - } else { - // 将 sso-server 回应的消息作为异常抛出 - throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005); - } - } else { - // q2、使用模式二:直连Redis校验ticket - return ssoTemplate.checkTicket(ticket); - } - } - - - // ----------- 全局默认实例 ----------- - - /** - * 全局默认实例 - */ - public static SaSsoProcessor instance = new SaSsoProcessor(); - -} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java deleted file mode 100644 index 495986ea..00000000 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoTemplate.java +++ /dev/null @@ -1,647 +0,0 @@ -/* - * 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.sso; - -import cn.dev33.satoken.SaManager; -import cn.dev33.satoken.config.SaSsoConfig; -import cn.dev33.satoken.session.SaSession; -import cn.dev33.satoken.sign.SaSignTemplate; -import cn.dev33.satoken.sso.error.SaSsoErrorCode; -import cn.dev33.satoken.sso.exception.SaSsoException; -import cn.dev33.satoken.sso.name.ApiName; -import cn.dev33.satoken.sso.name.ParamName; -import cn.dev33.satoken.stp.StpLogic; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.strategy.SaStrategy; -import cn.dev33.satoken.util.SaFoxUtil; -import cn.dev33.satoken.util.SaResult; - -import java.util.*; - -/** - * Sa-Token-SSO 单点登录模块 - * - * @author click33 - * @since 1.30.0 - */ -public class SaSsoTemplate { - - // ---------------------- 全局配置 ---------------------- - - /** - * 所有 API 名称 - */ - public ApiName apiName = new ApiName(); - - /** - * 所有参数名称 - */ - public ParamName paramName = new ParamName(); - - /** - * @param paramName 替换 paramName 对象 - * @return 对象自身 - */ - public SaSsoTemplate setParamName(ParamName paramName) { - this.paramName = paramName; - return this; - } - - /** - * @param apiName 替换 apiName 对象 - * @return 对象自身 - */ - public SaSsoTemplate setApiName(ApiName apiName) { - this.apiName = apiName; - return this; - } - - /** - * 获取底层使用的会话对象 - * @return / - */ - public StpLogic getStpLogic() { - return StpUtil.stpLogic; - } - - /** - * 获取底层使用的配置对象 - * @return / - */ - public SaSsoConfig getSsoConfig() { - return SaSsoManager.getConfig(); - } - - /** - * 获取底层使用的 API 签名对象 - * @return / - */ - public SaSignTemplate getSignTemplate() { - return SaManager.getSaSignTemplate(); - } - - - // ---------------------- Ticket 操作 ---------------------- - - /** - * 保存 Ticket 关联的 loginId - * @param ticket ticket码 - * @param loginId 账号id - */ - public void saveTicket(String ticket, Object loginId) { - // 保存 ticket -> loginId 的关系 - long ticketTimeout = SaSsoManager.getConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout); - } - - /** - * 保存 Ticket 索引 (id 反查 ticket) - * @param ticket ticket码 - * @param loginId 账号id - */ - public void saveTicketIndex(String ticket, Object loginId) { - long ticketTimeout = SaSsoManager.getConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketIndexKey(loginId), String.valueOf(ticket), ticketTimeout); - } - - /** - * 保存 Ticket 关联的 client - * @param ticket ticket码 - * @param client 客户端标识 - */ - public void saveTicketToClient(String ticket, String client) { - if(SaFoxUtil.isEmpty(client)) { - return; - } - long ticketTimeout = SaSsoManager.getConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout); - } - - /** - * 删除 Ticket - * @param ticket Ticket码 - */ - public void deleteTicket(String ticket) { - if(ticket == null) { - return; - } - SaManager.getSaTokenDao().delete(splicingTicketSaveKey(ticket)); - } - - /** - * 删除 Ticket索引 - * @param loginId 账号id - */ - public void deleteTicketIndex(Object loginId) { - if(loginId == null) { - return; - } - SaManager.getSaTokenDao().delete(splicingTicketIndexKey(loginId)); - } - - /** - * 删除 Ticket 关联的 client - * @param ticket Ticket码 - */ - public void deleteTicketToClient(String ticket) { - if(ticket == null) { - return; - } - SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); - } - - /** - * 查询 ticket 指向的 loginId,如果 ticket 码无效则返回 null - * @param ticket Ticket码 - * @return 账号id - */ - public Object getLoginId(String ticket) { - if(SaFoxUtil.isEmpty(ticket)) { - return null; - } - return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); - } - - /** - * 查询 ticket 指向的 loginId,并转换为指定类型 - * @param 要转换的类型 - * @param ticket Ticket码 - * @param cs 要转换的类型 - * @return 账号id - */ - public T getLoginId(String ticket, Class cs) { - return SaFoxUtil.getValueByType(getLoginId(ticket), cs); - } - - /** - * 查询 指定 loginId 其所属的 ticket 值 - * @param loginId 账号id - * @return Ticket值 - */ - public String getTicketValue(Object loginId) { - if(loginId == null) { - return null; - } - return SaManager.getSaTokenDao().get(splicingTicketIndexKey(loginId)); - } - - /** - * 查询 ticket 关联的 client,如果 ticket 码无效则返回 null - * @param ticket Ticket码 - * @return 账号id - */ - public String getTicketToClient(String ticket) { - if(SaFoxUtil.isEmpty(ticket)) { - return null; - } - return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket)); - } - - // - - /** - * 根据 账号id 创建一个 Ticket码 - * @param loginId 账号id - * @param client 客户端标识 - * @return Ticket码 - */ - public String createTicket(Object loginId, String client) { - // 创建 Ticket - String ticket = randomTicket(loginId); - - // 保存 Ticket - saveTicket(ticket, loginId); - saveTicketIndex(ticket, loginId); - saveTicketToClient(ticket, client); - - // 返回 Ticket - return ticket; - } - - /** - * 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除 - * @param ticket Ticket码 - * @return 账号id - */ - public Object checkTicket(String ticket) { - return checkTicket(ticket, getSsoConfig().getClient()); - } - - /** - * 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除 - * @param ticket Ticket码 - * @param client client 标识 - * @return 账号id - */ - public Object checkTicket(String ticket, String client) { - // 读取 loginId - String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); - - if(loginId != null) { - - // 解析出这个 ticket 关联的 Client - String ticketClient = getTicketToClient(ticket); - - // 如果指定了 client 标识,则校验一下 client 标识是否一致 - if(SaFoxUtil.isNotEmpty(client) && SaFoxUtil.notEquals(client, ticketClient)) { - throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket) - .setCode(SaSsoErrorCode.CODE_30011); - } - - // 删除 ticket 信息,使其只有一次性有效 - deleteTicket(ticket); - deleteTicketIndex(loginId); - deleteTicketToClient(ticket); - } - - // - return loginId; - } - - /** - * 随机一个 Ticket码 - * @param loginId 账号id - * @return Ticket码 - */ - public String randomTicket(Object loginId) { - return SaFoxUtil.getRandomString(64); - } - - /** - * 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) - * @return see note - */ - public String getAllowUrl() { - // 默认从配置文件中返回 - return SaSsoManager.getConfig().getAllowUrl(); - } - - /** - * 校验重定向url合法性 - * @param url 下放ticket的url地址 - */ - public void checkRedirectUrl(String url) { - - // 1、是否是一个有效的url - if( ! SaFoxUtil.isUrl(url) ) { - throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001); - } - - // 2、截取掉?后面的部分 - int qIndex = url.indexOf("?"); - if(qIndex != -1) { - url = url.substring(0, qIndex); - } - - // 3、是否在[允许地址列表]之中 - List authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(",")); - if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) { - throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002); - } - - // 校验通过 √ - } - - - // ------------------- SSO 模式三相关 ------------------- - - /** - * 为指定账号id注册单点注销回调URL - * @param loginId 账号id - * @param sloCallbackUrl 单点注销时的回调URL - */ - public void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) { - if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) { - return; - } - SaSession session = getStpLogic().getSessionByLoginId(loginId); - Set urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new); - urlSet.add(sloCallbackUrl); - session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet); - } - - /** - * 指定账号单点注销 - * @param loginId 指定账号 - */ - public void ssoLogout(Object loginId) { - - // 如果这个账号尚未登录,则无操作 - SaSession session = getStpLogic().getSessionByLoginId(loginId, false); - if(session == null) { - return; - } - - // step.1 遍历通知 Client 端注销会话 - SaSsoConfig cfg = SaSsoManager.getConfig(); - Set urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new); - for (String url : urlSet) { - url = joinLoginIdAndSign(url, loginId); - cfg.sendHttp.apply(url); - } - - // step.2 Server端注销 - getStpLogic().logout(loginId); - } - - /** - * 根据配置的 getData 地址,查询数据 - * @param paramMap 查询参数 - * @return 查询结果 - */ - public Object getData(Map paramMap) { - String getDataUrl = SaSsoManager.getConfig().splicingGetDataUrl(); - return getData(getDataUrl, paramMap); - } - - /** - * 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址) - * @param path 自定义 path - * @param paramMap 查询参数 - * @return 查询结果 - */ - public Object getData(String path, Map paramMap) { - String url = buildCustomPathUrl(path, paramMap); - return SaSsoManager.getConfig().sendHttp.apply(url); - } - - - // ---------------------- 构建URL ---------------------- - - /** - * 构建URL:Server端 单点登录地址 - * @param clientLoginUrl Client端登录地址 - * @param back 回调路径 - * @return [SSO-Server端-认证地址 ] - */ - public String buildServerAuthUrl(String clientLoginUrl, String back) { - - // 服务端认证地址 - String serverUrl = SaSsoManager.getConfig().splicingAuthUrl(); - - // 拼接客户端标识 - String client = SaSsoManager.getConfig().getClient(); - if(SaFoxUtil.isNotEmpty(client)) { - serverUrl = SaFoxUtil.joinParam(serverUrl, paramName.client, client); - } - - - // 对back地址编码 - back = (back == null ? "" : back); - back = SaFoxUtil.encodeUrl(back); - - // 开始拼接 sso 统一认证地址,形如:serverAuthUrl = http://xxx.com?redirectUrl=xxx.com?back=xxx.com - - /* - * 部分 Servlet 版本 request.getRequestURL() 返回的 url 带有 query 参数,形如:http://domain.com?id=1, - * 如果不加判断会造成最终生成的 serverAuthUrl 带有双 back 参数 ,这个 if 判断正是为了解决此问题 - */ - if( ! clientLoginUrl.contains(paramName.back + "=" + back) ) { - clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back); - } - - // 返回 - return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl); - } - - /** - * 构建URL:Server端向Client下放ticket的地址 - * @param loginId 账号id - * @param client 客户端标识 - * @param redirect Client端提供的重定向地址 - * @return see note - */ - public String buildRedirectUrl(Object loginId, String client, String redirect) { - - // 校验 重定向地址 是否合法 - checkRedirectUrl(redirect); - - // 删掉 旧Ticket - deleteTicket(getTicketValue(loginId)); - - // 创建 新Ticket - String ticket = createTicket(loginId, client); - - // 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket) - return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket); - } - - /** - * 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug - * @param url url - * @return 编码过后的url - */ - public String encodeBackParam(String url) { - - // 获取back参数所在位置 - int index = url.indexOf("?" + paramName.back + "="); - if(index == -1) { - index = url.indexOf("&" + paramName.back + "="); - if(index == -1) { - return url; - } - } - - // 开始编码 - int length = paramName.back.length() + 2; - String back = url.substring(index + length); - back = SaFoxUtil.encodeUrl(back); - - // 放回url中 - url = url.substring(0, index + length) + back; - return url; - } - - /** - * 构建URL:校验ticket的URL - *

在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id - * @param ticket ticket码 - * @param ssoLogoutCallUrl 单点注销时的回调URL - * @return 构建完毕的URL - */ - public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) { - - // s1:先收集应该增加的参数:client、ticket、ssoLogoutCall - Map paramMap = new TreeMap<>(); - - // 拼接 client 参数 - String client = getSsoConfig().getClient(); - if(SaFoxUtil.isNotEmpty(client)) { - paramMap.put(paramName.client, client); - } - - // 拼接 ticket 参数 - paramMap.put(paramName.ticket, ticket); - - // 拼接单点注销时的回调 URL - if(ssoLogoutCallUrl != null) { - paramMap.put(paramName.ssoLogoutCall, ssoLogoutCallUrl); - } - - // s2:构建 url 地址 - String url = SaSsoManager.getConfig().splicingCheckTicketUrl(); - String paramStr = getSignTemplate().addSignParamsAndJoin(paramMap); - String finalUrl = SaFoxUtil.joinParam(url, paramStr); - - // 返回 - return finalUrl; - } - - /** - * 构建URL:单点注销URL - * @param loginId 要注销的账号id - * @return 单点注销URL - */ - public String buildSloUrl(Object loginId) { - String url = SaSsoManager.getConfig().splicingSloUrl(); - return joinLoginIdAndSign(url, loginId); - } - - /** - * 构建URL:Server端 getData 地址,带签名等参数 - * @param paramMap 查询参数 - * @return / - */ - public String buildGetDataUrl(Map paramMap) { - String getDataUrl = SaSsoManager.getConfig().getGetDataUrl(); - return buildCustomPathUrl(getDataUrl, paramMap); - } - - /** - * 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址) - * @param paramMap 请求参数 - * @return / - */ - public String buildCustomPathUrl(String path, Map paramMap) { - // 如果path不是以 http 开头,那么就拼接上 serverUrl - String url = path; - if( ! url.startsWith("http") ) { - String serverUrl = SaSsoManager.getConfig().getServerUrl(); - SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012); - url = SaFoxUtil.spliceTwoUrl(serverUrl, path); - } - - // 添加签名等参数,并序列化 - return joinParamMapAndSign(url, paramMap); - } - - - // ------------------- 发起请求 ------------------- - - /** - * 发出请求,并返回 SaResult 结果 - * @param url 请求地址 - * @return 返回的结果 - */ - public SaResult request(String url) { - String body = SaSsoManager.getConfig().sendHttp.apply(url); - Map map = SaManager.getSaJsonTemplate().parseJsonToMap(body); - return new SaResult(map); - } - - /** - * 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面 - * @param url 请求地址 - * @param paramMap 请求原始参数列表 - * @return 加工后的url - */ - public String joinParamMapAndSign(String url, Map paramMap) { - // 在参数列表中追加:时间戳、随机字符串、参数签名 - SaManager.getSaSignTemplate().addSignParams(paramMap); - - // 将参数列表序列化为kv字符串 - String signParams = SaManager.getSaSignTemplate().joinParams(paramMap); - - // 将kv字符串拼接到url后面 - return SaFoxUtil.joinParam(url, signParams); - } - - /** - * 给 url 拼接 loginId 参数,并拼接 sign 等参数 - * @param url 链接 - * @param loginId 账号id - * @return 加工后的url - */ - public String joinLoginIdAndSign(String url, Object loginId) { - Map paramMap = new LinkedHashMap<>(); - paramMap.put(paramName.loginId, loginId); - return joinParamMapAndSign(url, paramMap); - } - - - // ------------------- 返回相应key ------------------- - - /** - * 拼接key:Ticket 查 账号Id - * @param ticket ticket值 - * @return key - */ - public String splicingTicketSaveKey(String ticket) { - return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket; - } - - /** - * 拼接key:Ticket 查 所属的 client - * @param ticket ticket值 - * @return key - */ - public String splicingTicketToClientSaveKey(String ticket) { - return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket; - } - - /** - * 拼接key:账号Id 反查 Ticket - * @param id 账号id - * @return key - */ - public String splicingTicketIndexKey(Object id) { - return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id; - } - - - // 在 sa-token.sso.is-check-sign=false 时,输出警告信息 - public void printNoCheckSignWarning() { - System.err.println("警告信息:当前配置项 sa-token.sso.is-check-sign=false 已跳过参数签名校验," + - "此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); - } - - - // -------- 以下方法已废弃,仅为兼容旧版本而保留 -------- - - /** - * 构建URL:Server端 账号资料查询地址 - * @param loginId 账号id - * @return Server端 账号资料查询地址 - */ - @Deprecated - public String buildUserinfoUrl(Object loginId) { - String userinfoUrl = SaSsoManager.getConfig().splicingUserinfoUrl(); - return joinLoginIdAndSign(userinfoUrl, loginId); - } - - /** - * 获取:账号资料 - * @param loginId 账号id - * @return 账号资料 - */ - @Deprecated - public Object getUserinfo(Object loginId) { - String url = buildUserinfoUrl(loginId); - return request(url); - } - - -} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientConfig.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientConfig.java new file mode 100644 index 00000000..89d2c12d --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientConfig.java @@ -0,0 +1,390 @@ +/* + * 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.sso.config; + + +import cn.dev33.satoken.sso.error.SaSsoErrorCode; +import cn.dev33.satoken.sso.exception.SaSsoException; +import cn.dev33.satoken.sso.function.SendHttpFunction; +import cn.dev33.satoken.sso.function.TicketResultHandleFunction; +import cn.dev33.satoken.util.SaFoxUtil; + +import java.io.Serializable; + +/** + * Sa-Token SSO 单点登录模块 配置类 (Client端) + * + * @author click33 + * @since 1.30.0 + */ +public class SaSsoClientConfig implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; + + /** + * 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + */ + public String mode = ""; + + /** + * 当前 Client 名称标识,用于和 ticket 码的互相锁定 + */ + public String client; + + /** + * 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 + */ + public String serverUrl; + + /** + * 单独配置 Server 端单点登录授权地址 + */ + public String authUrl = "/sso/auth"; + + /** + * 单独配置 Server 端的 ticket 校验地址 + */ + public String checkTicketUrl = "/sso/checkTicket"; + + /** + * 单独配置 Server 端查询数据 getData 地址 + */ + public String getDataUrl = "/sso/getData"; + + /** + * 单独配置 Server 端查询 userinfo 地址 + */ + public String userinfoUrl = "/sso/userinfo"; + + /** + * 单独配置 Server 端单点注销地址 + */ + public String sloUrl = "/sso/signout"; + + /** + * 配置当前 Client 端的登录地址(为空时自动获取) + */ + public String currSsoLogin; + + /** + * 配置当前 Client 端的单点注销回调URL (为空时自动获取) + */ + public String currSsoLogoutCall; + + /** + * 是否打开单点注销功能 + */ + public Boolean isSlo = true; + + /** + * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + */ + public Boolean isHttp = false; + + /** + * 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public Boolean isCheckSign = true; + + + // get set + + /** + * 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + * + * @return / + */ + public String getMode() { + return this.mode; + } + + /** + * 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + * + * @param mode / + */ + public void setMode(String mode) { + this.mode = mode; + } + + /** + * @return 是否打开单点注销功能 + */ + public Boolean getIsSlo() { + return isSlo; + } + + /** + * @param isSlo 是否打开单点注销功能 + * @return 对象自身 + */ + public SaSsoClientConfig setIsSlo(Boolean isSlo) { + this.isSlo = isSlo; + return this; + } + + /** + * @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + */ + public Boolean getIsHttp() { + return isHttp; + } + + /** + * @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + * @return 对象自身 + */ + public SaSsoClientConfig setIsHttp(Boolean isHttp) { + this.isHttp = isHttp; + return this; + } + + /** + * @return 当前 Client 名称标识,用于和 ticket 码的互相锁定 + */ + public String getClient() { + return client; + } + + /** + * @param client 当前 Client 名称标识,用于和 ticket 码的互相锁定 + */ + public SaSsoClientConfig setClient(String client) { + this.client = client; + return this; + } + + /** + * @return 配置的 Server 端单点登录授权地址 + */ + public String getAuthUrl() { + return authUrl; + } + + /** + * @param authUrl 配置 Server 端单点登录授权地址 + * @return 对象自身 + */ + public SaSsoClientConfig setAuthUrl(String authUrl) { + this.authUrl = authUrl; + return this; + } + + /** + * @return 配置的 Server 端的 ticket 校验地址 + */ + public String getCheckTicketUrl() { + return checkTicketUrl; + } + + /** + * @param checkTicketUrl 配置 Server 端的 ticket 校验地址 + * @return 对象自身 + */ + public SaSsoClientConfig setCheckTicketUrl(String checkTicketUrl) { + this.checkTicketUrl = checkTicketUrl; + return this; + } + + /** + * @return Server 端查询数据 getData 地址 + */ + public String getGetDataUrl() { + return getDataUrl; + } + + /** + * @param getDataUrl 配置 Server 端查询数据 getData 地址 + * @return 对象自身 + */ + public SaSsoClientConfig setGetDataUrl(String getDataUrl) { + this.getDataUrl = getDataUrl; + return this; + } + + /** + * @return 配置的 Server 端查询 userinfo 地址 + */ + public String getUserinfoUrl() { + return userinfoUrl; + } + + /** + * @param userinfoUrl 配置 Server 端查询 userinfo 地址 + * @return 对象自身 + */ + public SaSsoClientConfig setUserinfoUrl(String userinfoUrl) { + this.userinfoUrl = userinfoUrl; + return this; + } + + /** + * @return 配置 Server 端单点注销地址 + */ + public String getSloUrl() { + return sloUrl; + } + + /** + * @param sloUrl 配置 Server 端单点注销地址 + * @return 对象自身 + */ + public SaSsoClientConfig setSloUrl(String sloUrl) { + this.sloUrl = sloUrl; + return this; + } + + /** + * @return 配置当前 Client 端的登录地址(为空时自动获取) + */ + public String getCurrSsoLogin() { + return currSsoLogin; + } + + /** + * @param currSsoLogin 配置当前 Client 端的登录地址(为空时自动获取) + * @return 对象自身 + */ + public SaSsoClientConfig setCurrSsoLogin(String currSsoLogin) { + this.currSsoLogin = currSsoLogin; + return this; + } + + /** + * @return 配置当前 Client 端的单点注销回调URL (为空时自动获取) + */ + public String getCurrSsoLogoutCall() { + return currSsoLogoutCall; + } + + /** + * @param currSsoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取) + * @return 对象自身 + */ + public SaSsoClientConfig setCurrSsoLogoutCall(String currSsoLogoutCall) { + this.currSsoLogoutCall = currSsoLogoutCall; + return this; + } + + /** + * @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 + */ + public String getServerUrl() { + return serverUrl; + } + + /** + * @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 + * @return 对象自身 + */ + public SaSsoClientConfig setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + return this; + } + + /** + * 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * + * @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public Boolean getIsCheckSign() { + return this.isCheckSign; + } + + /** + * 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * + * @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public SaSsoClientConfig setIsCheckSign(Boolean isCheckSign) { + this.isCheckSign = isCheckSign; + return this; + } + + @Override + public String toString() { + return "SaSsoConfig [" + + "mode=" + mode + + ", client=" + client + + ", serverUrl=" + serverUrl + + ", authUrl=" + authUrl + + ", checkTicketUrl=" + checkTicketUrl + + ", getDataUrl=" + getDataUrl + + ", userinfoUrl=" + userinfoUrl + + ", sloUrl=" + sloUrl + + ", currSsoLogin=" + currSsoLogin + + ", currSsoLogoutCall=" + currSsoLogoutCall + + ", isSlo=" + isSlo + + ", isHttp=" + isHttp + + ", isCheckSign=" + isCheckSign + + "]"; + } + + // 额外添加的一些函数 + + /** + * @return 获取拼接url:Server 端单点登录授权地址 + */ + public String splicingAuthUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl()); + } + + /** + * @return 获取拼接url:Server 端的 ticket 校验地址 + */ + public String splicingCheckTicketUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl()); + } + + /** + * @return 获取拼接url:Server 端查询数据 getData 地址 + */ + public String splicingGetDataUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl()); + } + + /** + * @return 获取拼接url:Server 端查询 userinfo 地址 + */ + public String splicingUserinfoUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getUserinfoUrl()); + } + + /** + * @return 获取拼接url:Server 端单点注销地址 + */ + public String splicingSloUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl()); + } + + + // -------------------- 所有回调函数 -------------------- + + /** + * SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用) + *

参数:loginId, back + *

返回值:返回给前端的值 + */ + public TicketResultHandleFunction ticketResultHandle = null; + + /** + * SSO-Client端:发送Http请求的处理函数 + */ + public SendHttpFunction sendHttp = url -> { + throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010); + }; + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoServerConfig.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoServerConfig.java new file mode 100644 index 00000000..21a9d3d6 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoServerConfig.java @@ -0,0 +1,223 @@ +/* + * 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.sso.config; + + +import cn.dev33.satoken.sso.error.SaSsoErrorCode; +import cn.dev33.satoken.sso.exception.SaSsoException; +import cn.dev33.satoken.sso.function.DoLoginHandleFunction; +import cn.dev33.satoken.sso.function.NotLoginViewFunction; +import cn.dev33.satoken.sso.function.SendHttpFunction; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +import java.io.Serializable; + +/** + * Sa-Token SSO 单点登录模块 配置类 (Server端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoServerConfig implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; + + + // ----------------- Server端相关配置 + + /** + * 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + */ + public String mode = ""; + + /** + * Ticket有效期 (单位: 秒) + */ + public long ticketTimeout = 60 * 5; + + /** + * 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + */ + public String allowUrl = "*"; + + /** + * 是否打开单点注销功能 + */ + public Boolean isSlo = true; + + /** + * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + */ + public Boolean isHttp = false; + + /** + * 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public Boolean isCheckSign = true; + + + // get set + + /** + * 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + * + * @return / + */ + public String getMode() { + return this.mode; + } + + /** + * 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响) + * + * @param mode / + */ + public void setMode(String mode) { + this.mode = mode; + } + + /** + * @return Ticket有效期 (单位: 秒) + */ + public long getTicketTimeout() { + return ticketTimeout; + } + + /** + * @param ticketTimeout Ticket有效期 (单位: 秒) + * @return 对象自身 + */ + public SaSsoServerConfig setTicketTimeout(long ticketTimeout) { + this.ticketTimeout = ticketTimeout; + return this; + } + + /** + * @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + */ + public String getAllowUrl() { + return allowUrl; + } + + /** + * @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + * @return 对象自身 + */ + public SaSsoServerConfig setAllowUrl(String allowUrl) { + this.allowUrl = allowUrl; + return this; + } + + /** + * @return 是否打开单点注销功能 + */ + public Boolean getIsSlo() { + return isSlo; + } + + /** + * @param isSlo 是否打开单点注销功能 + * @return 对象自身 + */ + public SaSsoServerConfig setIsSlo(Boolean isSlo) { + this.isSlo = isSlo; + return this; + } + + /** + * @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + */ + public Boolean getIsHttp() { + return isHttp; + } + + /** + * @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo) + * @return 对象自身 + */ + public SaSsoServerConfig setIsHttp(Boolean isHttp) { + this.isHttp = isHttp; + return this; + } + + /** + * 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * + * @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public Boolean getIsCheckSign() { + return this.isCheckSign; + } + + /** + * 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * + * @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + */ + public SaSsoServerConfig setIsCheckSign(Boolean isCheckSign) { + this.isCheckSign = isCheckSign; + return this; + } + + /** + * 以数组形式写入允许的授权回调地址 + * @param url 所有集合 + * @return 对象自身 + */ + public SaSsoServerConfig setAllow(String ...url) { + this.allowUrl = SaFoxUtil.arrayJoin(url); + return this; + } + + @Override + public String toString() { + return "SaSsoServerConfig [" + + "mode=" + mode + + ", ticketTimeout=" + ticketTimeout + + ", allowUrl=" + allowUrl + + ", isSlo=" + isSlo + + ", isHttp=" + isHttp + + ", isCheckSign=" + isCheckSign + + "]"; + } + + + // -------------------- 所有回调函数 -------------------- + + + /** + * SSO-Server端:未登录时返回的View + */ + public NotLoginViewFunction notLoginView = () -> { + return "当前会话在SSO-Server认证中心尚未登录(当前未配置登录视图)"; + }; + + /** + * SSO-Server端:登录函数 + */ + public DoLoginHandleFunction doLoginHandle = (name, pwd) -> { + return SaResult.error(); + }; + + /** + * SSO-Server端:发送Http请求的处理函数 + */ + public SendHttpFunction sendHttp = url -> { + throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010); + }; + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/DoLoginHandleFunction.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/DoLoginHandleFunction.java new file mode 100644 index 00000000..a4d94f7a --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/DoLoginHandleFunction.java @@ -0,0 +1,32 @@ +/* + * 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.sso.function; + +import java.util.function.BiFunction; + +/** + * 函数式接口:SSO-Server端:未登录时返回的View + * + *

参数:账号、密码

+ *

返回:登录结果

+ * + * @author click33 + * @since 1.38.0 + */ +@FunctionalInterface +public interface DoLoginHandleFunction extends BiFunction { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/NotLoginViewFunction.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/NotLoginViewFunction.java new file mode 100644 index 00000000..644a0a94 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/NotLoginViewFunction.java @@ -0,0 +1,32 @@ +/* + * 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.sso.function; + +import java.util.function.Supplier; + +/** + * 函数式接口:SSO-Server端:未登录时返回的View + * + *

参数:无

+ *

返回:未登录时的 View 视图

+ * + * @author click33 + * @since 1.38.0 + */ +@FunctionalInterface +public interface NotLoginViewFunction extends Supplier { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/SendHttpFunction.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/SendHttpFunction.java new file mode 100644 index 00000000..874e8a7e --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/SendHttpFunction.java @@ -0,0 +1,32 @@ +/* + * 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.sso.function; + +import java.util.function.Function; + +/** + * 函数式接口:发送Http请求的处理函数 + * + *

参数:要请求的url

+ *

返回:请求结果

+ * + * @author click33 + * @since 1.38.0 + */ +@FunctionalInterface +public interface SendHttpFunction extends Function { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/TicketResultHandleFunction.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/TicketResultHandleFunction.java new file mode 100644 index 00000000..7fdc26e0 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/TicketResultHandleFunction.java @@ -0,0 +1,32 @@ +/* + * 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.sso.function; + +import java.util.function.BiFunction; + +/** + * 函数式接口:SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用) + * + *

参数:loginId, back

+ *

返回:返回给前端的值

+ * + * @author click33 + * @since 1.38.0 + */ +@FunctionalInterface +public interface TicketResultHandleFunction extends BiFunction { + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientModel.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientModel.java new file mode 100644 index 00000000..ae50feb1 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientModel.java @@ -0,0 +1,114 @@ +/* + * 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.sso.model; + + +/** + * Sa-Token SSO Model + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoClientModel { + + /** + * 客户端标识 + */ + public String client; + + /** + * 单点注销回调url + */ + public String ssoLogoutCall; + + /** + * 此 client 注册信息的时间,13位时间戳 + */ + public Long regTime; + + public SaSsoClientModel() { + } + + public SaSsoClientModel(String client, String ssoLogoutCall) { + this.client = client; + this.ssoLogoutCall = ssoLogoutCall; + this.regTime = System.currentTimeMillis(); + } + + /** + * 获取 客户端标识 + * + * @return client 客户端标识 + */ + public String getClient() { + return this.client; + } + + /** + * 设置 客户端标识 + * + * @param client 客户端标识 + */ + public void setClient(String client) { + this.client = client; + } + + /** + * 获取 单点注销回调url + * + * @return ssoLogoutCall 单点注销回调url + */ + public String getSsoLogoutCall() { + return this.ssoLogoutCall; + } + + /** + * 设置 单点注销回调url + * + * @param ssoLogoutCall 单点注销回调url + */ + public void setSsoLogoutCall(String ssoLogoutCall) { + this.ssoLogoutCall = ssoLogoutCall; + } + + /** + * 获取 此 client 注册信息的时间,13位时间戳 + * + * @return regTime 此 client 注册信息的时间,13位时间戳 + */ + public Long getRegTime() { + return this.regTime; + } + + /** + * 设置 此 client 注册信息的时间,13位时间戳 + * + * @param regTime 此 client 注册信息的时间,13位时间戳 + */ + public void setRegTime(Long regTime) { + this.regTime = regTime; + } + + @Override + public String toString() { + return "SaSsoClientModel{" + + "client='" + client + '\'' + + ", ssoLogoutCall='" + ssoLogoutCall + '\'' + + ", regTime='" + regTime + '\'' + + '}'; + } + +} \ No newline at end of file diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java new file mode 100644 index 00000000..eb04a659 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java @@ -0,0 +1,307 @@ +/* + * 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.sso.processor; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.error.SaSsoErrorCode; +import cn.dev33.satoken.sso.exception.SaSsoException; +import cn.dev33.satoken.sso.name.ApiName; +import cn.dev33.satoken.sso.name.ParamName; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * SSO 请求处理器 (Client端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoClientProcessor { + + /** + * 全局默认实例 + */ + public static SaSsoClientProcessor instance = new SaSsoClientProcessor(); + + /** + * 底层 SaSsoClientTemplate 对象 + */ + public SaSsoClientTemplate ssoClientTemplate = new SaSsoClientTemplate(); + + // ----------- SSO-Client 端路由分发 ----------- + + /** + * 分发 Client 端所有请求 + * @return 处理结果 + */ + public Object dister() { + ApiName apiName = ssoClientTemplate.apiName; + + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + + // ------------------ 路由分发 ------------------ + + // ---------- SSO-Client端:登录地址 + if(req.isPath(apiName.ssoLogin)) { + return ssoLogin(); + } + + // ---------- SSO-Client端:单点注销 + if(req.isPath(apiName.ssoLogout)) { + return ssoLogout(); + } + + // ---------- SSO-Client端:单点注销的回调 [模式三] + if(req.isPath(apiName.ssoLogoutCall) && cfg.getIsSlo() && cfg.getIsHttp()) { + return ssoLogoutCall(); + } + + // 默认返回 + return SaSsoConsts.NOT_HANDLE; + } + + /** + * SSO-Client端:登录地址 + * @return 处理结果 + */ + public Object ssoLogin() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + ApiName apiName = ssoClientTemplate.apiName; + ParamName paramName = ssoClientTemplate.paramName; + + // 获取参数 + String back = req.getParam(paramName.back, "/"); + String ticket = req.getParam(paramName.ticket); + + // 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回 + if(stpLogic.isLogin()) { + return res.redirect(back); + } + /* + * 此时有两种情况: + * 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心 + * 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录 + */ + if(ticket == null) { + // 获取当前项目的 sso 登录地址 + // 全局配置了就是用全局的,否则使用当前请求的地址 + String currSsoLoginUrl; + if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogin())) { + currSsoLoginUrl = cfg.getCurrSsoLogin(); + } else { + currSsoLoginUrl = SaHolder.getRequest().getUrl(); + } + // 构建url + String serverAuthUrl = ssoClientTemplate.buildServerAuthUrl(currSsoLoginUrl, back); + return res.redirect(serverAuthUrl); + } else { + // ------- 1、校验ticket,获取 loginId + Object loginId = checkTicketByMode2Or3(ticket, apiName.ssoLogin); + + // Be: 如果开发者自定义了处理逻辑 + if(cfg.ticketResultHandle != null) { + return cfg.ticketResultHandle.apply(loginId, back); + } + + // ------- 2、如果 loginId 无值,说明 ticket 无效 + if(SaFoxUtil.isEmpty(loginId)) { + throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoErrorCode.CODE_30004); + } else { + // 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址 + stpLogic.login(loginId); + return res.redirect(back); + } + } + } + + /** + * SSO-Client端:单点注销 + * @return 处理结果 + */ + public Object ssoLogout() { + // 获取对象 + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + + // ---------- SSO-Client端:单点注销 [模式二] + if(cfg.getIsSlo() && ! cfg.getIsHttp()) { + return ssoLogoutType2(); + } + + // ---------- SSO-Client端:单点注销 [模式三] + if(cfg.getIsSlo() && cfg.getIsHttp()) { + return ssoLogoutType3(); + } + + // 默认返回 + return SaSsoConsts.NOT_HANDLE; + } + + /** + * SSO-Client端:单点注销 [模式二] + * @return 处理结果 + */ + public Object ssoLogoutType2() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + + // 开始处理 + if(stpLogic.isLogin()) { + stpLogic.logout(stpLogic.getLoginId()); + } + + // 返回 + return ssoLogoutBack(req, res); + } + + /** + * SSO-Client端:单点注销 [模式三] + * @return 处理结果 + */ + public Object ssoLogoutType3() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + + // 如果未登录,则无需注销 + if( ! stpLogic.isLogin()) { + return ssoLogoutBack(req, res); + } + + // 调用 sso-server 认证中心单点注销API + String url = ssoClientTemplate.buildSloUrl(stpLogic.getLoginId()); + SaResult result = ssoClientTemplate.request(url); + + // 校验响应状态码 + if(SaResult.CODE_SUCCESS == result.getCode()) { + // 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀 + if(stpLogic.isLogin()) { + stpLogic.logout(); + } + return ssoLogoutBack(req, res); + } else { + // 将 sso-server 回应的消息作为异常抛出 + throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30006); + } + } + + /** + * SSO-Client端:单点注销的回调 [模式三] + * @return 处理结果 + */ + public Object ssoLogoutCall() { + + // 获取对象 + SaRequest req = SaHolder.getRequest(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + ParamName paramName = ssoClientTemplate.paramName; + SaSsoClientConfig ssoConfig = ssoClientTemplate.getClientConfig(); + + // 获取参数 + String loginId = req.getParamNotNull(paramName.loginId); + + // 校验参数签名 + if(ssoConfig.getIsCheckSign()) { + ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).checkRequest(req, paramName.loginId); + } else { + SaSsoManager.printNoCheckSignWarningByRuntime(); + } + + // 注销当前应用端会话 + stpLogic.logout(loginId); + + // 响应 + return SaResult.ok("单点注销回调成功"); + } + + // 工具方法 + + /** + * 封装:校验ticket,取出loginId + * @param ticket ticket码 + * @param currUri 当前路由的uri,用于计算单点注销回调地址 + * @return loginId + */ + public Object checkTicketByMode2Or3(String ticket, String currUri) { + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + ApiName apiName = ssoClientTemplate.apiName; + + // --------- 两种模式 + if(cfg.getIsHttp()) { + // q1、使用模式三:使用 http 请求从认证中心校验ticket + + // 计算当前 sso-client 的单点注销回调地址 + String ssoLogoutCall = null; + if(cfg.getIsSlo()) { + // 如果配置了回调地址,就使用配置的值: + if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) { + ssoLogoutCall = cfg.getCurrSsoLogoutCall(); + } + // 如果提供了当前 uri,则根据此值来计算: + else if(SaFoxUtil.isNotEmpty(currUri)) { + ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall); + } + // 否则视为不注册单点注销回调地址 + else { + } + } + + // 构建请求URL + String checkUrl = ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall); + + // 发起请求 + SaResult result = ssoClientTemplate.request(checkUrl); + + // 校验 + if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) { + return result.getData(); + } else { + // 将 sso-server 回应的消息作为异常抛出 + throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005); + } + } else { + // q2、使用模式二:直连Redis校验ticket + // return ssoClientTemplate.checkTicket(ticket); + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, cfg.getClient()); + } + } + + /** + * 封装:单点注销成功后返回结果 + * @param req SaRequest对象 + * @param res SaResponse对象 + * @return 返回结果 + */ + public Object ssoLogoutBack(SaRequest req, SaResponse res) { + return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoClientTemplate.paramName); + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoProcessorHelper.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoProcessorHelper.java new file mode 100644 index 00000000..0fafb624 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoProcessorHelper.java @@ -0,0 +1,58 @@ +/* + * 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.sso.processor; + +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.sso.name.ParamName; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * SSO 请求处理器,辅助方法 + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoProcessorHelper { + + /** + * 封装:单点注销成功后返回结果 + * @param req SaRequest对象 + * @param res SaResponse对象 + * @return 返回结果 + */ + public static Object ssoLogoutBack(SaRequest req, SaResponse res, ParamName paramName) { + + /* + * 三种情况: + * 1. 有back参数,值为SELF -> 回退一级并刷新 + * 2. 有back参数,值为url -> 跳转到此url地址 + * 3. 无back参数 -> 返回json数据 + */ + String back = req.getParam(paramName.back); + if(SaFoxUtil.isNotEmpty(back)) { + if(back.equals(SaSsoConsts.SELF)) { + return ""; + } + return res.redirect(back); + } else { + return SaResult.ok("单点注销成功"); + } + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoServerProcessor.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoServerProcessor.java new file mode 100644 index 00000000..2f12fa92 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoServerProcessor.java @@ -0,0 +1,248 @@ +/* + * 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.sso.processor; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.name.ApiName; +import cn.dev33.satoken.sso.name.ParamName; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * SSO 请求处理器 (Server端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoServerProcessor { + + /** + * 全局默认实例 + */ + public static SaSsoServerProcessor instance = new SaSsoServerProcessor(); + + /** + * 底层 SaSsoServerTemplate 对象 + */ + public SaSsoServerTemplate ssoServerTemplate = new SaSsoServerTemplate(); + + // ----------- SSO-Server 端路由分发 ----------- + + /** + * 分发 Server 端所有请求 + * @return 处理结果 + */ + public Object dister() { + + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); + ApiName apiName = ssoServerTemplate.apiName; + + // ------------------ 路由分发 ------------------ + + // ---------- SSO-Server端:授权地址 + if(req.isPath(apiName.ssoAuth)) { + return ssoAuth(); + } + + // ---------- SSO-Server端:RestAPI 登录接口 + if(req.isPath(apiName.ssoDoLogin)) { + return ssoDoLogin(); + } + + // ---------- SSO-Server端:校验ticket 获取账号id + if(req.isPath(apiName.ssoCheckTicket) && cfg.getIsHttp()) { + return ssoCheckTicket(); + } + + // ---------- SSO-Server端:单点注销 + if(req.isPath(apiName.ssoSignout)) { + return ssoSignout(); + } + + // 默认返回 + return SaSsoConsts.NOT_HANDLE; + } + + /** + * SSO-Server端:授权地址 + * @return 处理结果 + */ + public Object ssoAuth() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); + StpLogic stpLogic = ssoServerTemplate.getStpLogic(); + ParamName paramName = ssoServerTemplate.paramName; + + // ---------- 此处有两种情况分开处理: + // ---- 情况1:在SSO认证中心尚未登录,需要先去登录 + if( ! stpLogic.isLogin()) { + return cfg.notLoginView.get(); + } + // ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式: + String mode = req.getParam(paramName.mode, ""); + + // 方式1:直接重定向回Client端 (mode=simple) + if(mode.equals(SaSsoConsts.MODE_SIMPLE)) { + String redirect = req.getParam(paramName.redirect); + ssoServerTemplate.checkRedirectUrl(redirect); + return res.redirect(redirect); + } else { + // 方式2:带着ticket参数重定向回Client端 (mode=ticket) + String redirectUrl = ssoServerTemplate.buildRedirectUrl( + stpLogic.getLoginId(), req.getParam(paramName.client), req.getParam(paramName.redirect)); + return res.redirect(redirectUrl); + } + } + + /** + * SSO-Server端:RestAPI 登录接口 + * @return 处理结果 + */ + public Object ssoDoLogin() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); + ParamName paramName = ssoServerTemplate.paramName; + + // 处理 + return cfg.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd)); + } + + /** + * SSO-Server端:校验ticket 获取账号id [模式三] + * @return 处理结果 + */ + public Object ssoCheckTicket() { + ParamName paramName = ssoServerTemplate.paramName; + + // 1、获取参数 + SaRequest req = SaHolder.getRequest(); + String client = req.getParam(paramName.client); + String ticket = req.getParamNotNull(paramName.ticket); + String sloCallback = req.getParam(paramName.ssoLogoutCall); + + // 2、校验签名 + if(ssoServerTemplate.getServerConfig().getIsCheckSign()) { + ssoServerTemplate.getSignTemplate(client).checkRequest(req, + paramName.client, paramName.ticket, paramName.ssoLogoutCall); + } else { + SaSsoManager.printNoCheckSignWarningByRuntime(); + } + + // 3、校验ticket,获取 loginId + Object loginId = ssoServerTemplate.checkTicket(ticket, client); + if(SaFoxUtil.isEmpty(loginId)) { + return SaResult.error("无效ticket:" + ticket); + } + + // 4、注册此客户端的单点注销回调URL + ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback); + + // 5、给 client 端响应结果 + return SaResult.data(loginId); + } + + /** + * SSO-Server端:单点注销 + * @return 处理结果 + */ + public Object ssoSignout() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); + ParamName paramName = ssoServerTemplate.paramName; + + // SSO-Server端:单点注销 [用户访问式] (不带loginId参数) + if(cfg.getIsSlo() && ! req.hasParam(paramName.loginId)) { + return ssoSignoutByUserVisit(); + } + + // SSO-Server端:单点注销 [Client调用式] (带loginId参数 & isHttp=true) + if(cfg.getIsHttp() && cfg.getIsSlo() && req.hasParam(paramName.loginId)) { + return ssoSignoutByClientHttp(); + } + + // 默认返回 + return SaSsoConsts.NOT_HANDLE; + } + + /** + * SSO-Server端:单点注销 [用户访问式] + * @return 处理结果 + */ + public Object ssoSignoutByUserVisit() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + Object loginId = ssoServerTemplate.getStpLogic().getLoginIdDefaultNull(); + + // 单点注销 + if(SaFoxUtil.isNotEmpty(loginId)) { + ssoServerTemplate.ssoLogout(loginId); + } + + // 完成 + return ssoLogoutBack(req, res); + } + + /** + * SSO-Server端:单点注销 [Client调用式] + * @return 处理结果 + */ + public Object ssoSignoutByClientHttp() { + ParamName paramName = ssoServerTemplate.paramName; + + // 获取参数 + SaRequest req = SaHolder.getRequest(); + String loginId = req.getParam(paramName.loginId); + String client = req.getParam(paramName.client); + + // step.1 校验签名 + if(ssoServerTemplate.getServerConfig().getIsCheckSign()) { + ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.loginId); + } else { + SaSsoManager.printNoCheckSignWarningByRuntime(); + } + + // step.2 单点注销 + ssoServerTemplate.ssoLogout(loginId); + + // 响应 + return SaResult.ok(); + } + + /** + * 封装:单点注销成功后返回结果 + * @param req SaRequest对象 + * @param res SaResponse对象 + * @return 返回结果 + */ + public Object ssoLogoutBack(SaRequest req, SaResponse res) { + return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName); + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientTemplate.java new file mode 100644 index 00000000..7a2034e5 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientTemplate.java @@ -0,0 +1,206 @@ +/* + * 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.sso.template; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.error.SaSsoErrorCode; +import cn.dev33.satoken.sso.exception.SaSsoException; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Sa-Token SSO 模板方法类 (Client端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoClientTemplate extends SaSsoTemplate { + + /** + * 获取底层使用的SsoClient配置对象 + * @return / + */ + public SaSsoClientConfig getClientConfig() { + return SaSsoManager.getClientConfig(); + } + + // ------------------- SSO 模式三相关 ------------------- + + /** + * 根据配置的 getData 地址,查询数据 + * @param paramMap 查询参数 + * @return 查询结果 + */ + public Object getData(Map paramMap) { + String getDataUrl = getClientConfig().splicingGetDataUrl(); + return getData(getDataUrl, paramMap); + } + + /** + * 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址) + * @param path 自定义 path + * @param paramMap 查询参数 + * @return 查询结果 + */ + public Object getData(String path, Map paramMap) { + String url = buildCustomPathUrl(path, paramMap); + return getClientConfig().sendHttp.apply(url); + } + + // ---------------------- 构建URL ---------------------- + + /** + * 构建URL:Server端 单点登录地址 + * @param clientLoginUrl Client端登录地址 + * @param back 回调路径 + * @return [SSO-Server端-认证地址 ] + */ + public String buildServerAuthUrl(String clientLoginUrl, String back) { + SaSsoClientConfig ssoConfig = getClientConfig(); + + // 服务端认证地址 + String serverUrl = ssoConfig.splicingAuthUrl(); + + // 拼接客户端标识 + String client = ssoConfig.getClient(); + if(SaFoxUtil.isNotEmpty(client)) { + serverUrl = SaFoxUtil.joinParam(serverUrl, paramName.client, client); + } + + + // 对back地址编码 + back = (back == null ? "" : back); + back = SaFoxUtil.encodeUrl(back); + + // 开始拼接 sso 统一认证地址,形如:serverAuthUrl = http://xxx.com?redirectUrl=xxx.com?back=xxx.com + + /* + * 部分 Servlet 版本 request.getRequestURL() 返回的 url 带有 query 参数,形如:http://domain.com?id=1, + * 如果不加判断会造成最终生成的 serverAuthUrl 带有双 back 参数 ,这个 if 判断正是为了解决此问题 + */ + if( ! clientLoginUrl.contains(paramName.back + "=" + back) ) { + clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back); + } + + // 返回 + return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl); + } + + /** + * 构建URL:校验ticket的URL + *

在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id + * @param ticket ticket码 + * @param ssoLogoutCallUrl 单点注销时的回调URL + * @return 构建完毕的URL + */ + public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) { + + SaSsoClientConfig ssoConfig = getClientConfig(); + + // 1、url + String url = ssoConfig.splicingCheckTicketUrl(); + + // 2、参数:client、ticket、ssoLogoutCall + Map paramMap = new TreeMap<>(); + paramMap.put(paramName.ticket, ticket); + paramMap.put(paramName.client, ssoConfig.getClient()); + paramMap.put(paramName.ssoLogoutCall, ssoLogoutCallUrl); + + // 追加签名参数,并序列化为kv字符串 + String signParamStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(paramMap); + + // 3、拼接 + return SaFoxUtil.joinParam(url, signParamStr); + } + + /** + * 构建URL:单点注销URL + * @param loginId 要注销的账号id + * @return 单点注销URL + */ + public String buildSloUrl(Object loginId) { + // 获取所需对象 + SaSsoClientConfig ssoConfig = getClientConfig(); + String url = ssoConfig.splicingSloUrl(); + String currClient = ssoConfig.getClient(); + + // 组织请求参数 + Map paramMap = new TreeMap<>(); + paramMap.put(paramName.loginId, loginId); + paramMap.put(paramName.client, currClient); + + // 追加签名参数,并序列化为kv字符串 + String signParamsStr = getSignTemplate(currClient).addSignParamsAndJoin(paramMap); + + // 拼接到 url 上 + return SaFoxUtil.joinParam(url, signParamsStr); + } + + /** + * 构建URL:Server端 getData 地址,带签名等参数 + * @param paramMap 查询参数 + * @return / + */ + public String buildGetDataUrl(Map paramMap) { + String getDataUrl = getClientConfig().getGetDataUrl(); + return buildCustomPathUrl(getDataUrl, paramMap); + } + + /** + * 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址) + * @param paramMap 请求参数 + * @return / + */ + public String buildCustomPathUrl(String path, Map paramMap) { + SaSsoClientConfig ssoConfig = getClientConfig(); + + // 构建 url + // 如果 path 不是以 http 开头,那么就拼接上 serverUrl + String url = path; + if( ! url.startsWith("http") ) { + String serverUrl = ssoConfig.getServerUrl(); + SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012); + url = SaFoxUtil.spliceTwoUrl(serverUrl, path); + } + + // 构建参数字符串 + paramMap.put(paramName.client, ssoConfig.getClient()); + String signParamsStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(paramMap); + + // 拼接 + return SaFoxUtil.joinParam(url, signParamsStr); + } + + + // ------------------- 发起请求 ------------------- + + /** + * 发出请求,并返回 SaResult 结果 + * @param url 请求地址 + * @return 返回的结果 + */ + public SaResult request(String url) { + String body = getClientConfig().sendHttp.apply(url); + Map map = SaManager.getSaJsonTemplate().parseJsonToMap(body); + return new SaResult(map); + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java new file mode 100644 index 00000000..b6dcc876 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java @@ -0,0 +1,414 @@ +/* + * 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.sso.template; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.sso.util.SaSsoConsts; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.error.SaSsoErrorCode; +import cn.dev33.satoken.sso.exception.SaSsoException; +import cn.dev33.satoken.sso.model.SaSsoClientModel; +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.util.SaFoxUtil; + +import java.util.*; + +/** + * Sa-Token SSO 模板方法类 (Server端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoServerTemplate extends SaSsoTemplate { + + /** + * 获取底层使用的SsoServer配置对象 + * @return / + */ + public SaSsoServerConfig getServerConfig() { + return SaSsoManager.getServerConfig(); + } + + // ---------------------- Ticket 操作 ---------------------- + + /** + * 保存 Ticket 关联的 loginId + * @param ticket ticket码 + * @param loginId 账号id + */ + public void saveTicket(String ticket, Object loginId) { + // 保存 ticket -> loginId 的关系 + long ticketTimeout = getServerConfig().getTicketTimeout(); + SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout); + } + + /** + * 保存 Ticket 索引 (id 反查 ticket) + * @param ticket ticket码 + * @param loginId 账号id + */ + public void saveTicketIndex(String ticket, Object loginId) { + long ticketTimeout = getServerConfig().getTicketTimeout(); + SaManager.getSaTokenDao().set(splicingTicketIndexKey(loginId), String.valueOf(ticket), ticketTimeout); + } + + /** + * 保存 Ticket 关联的 client + * @param ticket ticket码 + * @param client 客户端标识 + */ + public void saveTicketToClient(String ticket, String client) { + if(SaFoxUtil.isEmpty(client)) { + return; + } + long ticketTimeout = getServerConfig().getTicketTimeout(); + SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout); + } + + /** + * 删除 Ticket + * @param ticket Ticket码 + */ + public void deleteTicket(String ticket) { + if(ticket == null) { + return; + } + SaManager.getSaTokenDao().delete(splicingTicketSaveKey(ticket)); + } + + /** + * 删除 Ticket索引 + * @param loginId 账号id + */ + public void deleteTicketIndex(Object loginId) { + if(loginId == null) { + return; + } + SaManager.getSaTokenDao().delete(splicingTicketIndexKey(loginId)); + } + + /** + * 删除 Ticket 关联的 client + * @param ticket Ticket码 + */ + public void deleteTicketToClient(String ticket) { + if(ticket == null) { + return; + } + SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); + } + + /** + * 查询 ticket 指向的 loginId,如果 ticket 码无效则返回 null + * @param ticket Ticket码 + * @return 账号id + */ + public Object getLoginId(String ticket) { + if(SaFoxUtil.isEmpty(ticket)) { + return null; + } + return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); + } + + /** + * 查询 ticket 指向的 loginId,并转换为指定类型 + * @param 要转换的类型 + * @param ticket Ticket码 + * @param cs 要转换的类型 + * @return 账号id + */ + public T getLoginId(String ticket, Class cs) { + return SaFoxUtil.getValueByType(getLoginId(ticket), cs); + } + + /** + * 查询 指定 loginId 其所属的 ticket 值 + * @param loginId 账号id + * @return Ticket值 + */ + public String getTicketValue(Object loginId) { + if(loginId == null) { + return null; + } + return SaManager.getSaTokenDao().get(splicingTicketIndexKey(loginId)); + } + + /** + * 查询 ticket 关联的 client,如果 ticket 码无效则返回 null + * @param ticket Ticket码 + * @return 账号id + */ + public String getTicketToClient(String ticket) { + if(SaFoxUtil.isEmpty(ticket)) { + return null; + } + return SaManager.getSaTokenDao().get(splicingTicketToClientSaveKey(ticket)); + } + + // + + /** + * 根据 账号id 创建一个 Ticket码 + * @param loginId 账号id + * @param client 客户端标识 + * @return Ticket码 + */ + public String createTicket(Object loginId, String client) { + // 创建 Ticket + String ticket = randomTicket(loginId); + + // 保存 Ticket + saveTicket(ticket, loginId); + saveTicketIndex(ticket, loginId); + saveTicketToClient(ticket, client); + + // 返回 Ticket + return ticket; + } + + /** + * 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除 + * @param ticket Ticket码 + * @return 账号id + */ + public Object checkTicket(String ticket) { + return checkTicket(ticket, null); + } + + /** + * 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除 + * @param ticket Ticket码 + * @param client client 标识 + * @return 账号id + */ + public Object checkTicket(String ticket, String client) { + // 读取 loginId + String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket)); + + if(loginId != null) { + + // 解析出这个 ticket 关联的 Client + String ticketClient = getTicketToClient(ticket); + + // 如果指定了 client 标识,则校验一下 client 标识是否一致 + if(SaFoxUtil.isNotEmpty(client) && SaFoxUtil.notEquals(client, ticketClient)) { + throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket) + .setCode(SaSsoErrorCode.CODE_30011); + } + + // 删除 ticket 信息,使其只有一次性有效 + deleteTicket(ticket); + deleteTicketIndex(loginId); + deleteTicketToClient(ticket); + } + + // + return loginId; + } + + /** + * 随机一个 Ticket码 + * @param loginId 账号id + * @return Ticket码 + */ + public String randomTicket(Object loginId) { + return SaFoxUtil.getRandomString(64); + } + + /** + * 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + * @return see note + */ + public String getAllowUrl() { + // 默认从配置文件中返回 + return getServerConfig().getAllowUrl(); + } + + /** + * 校验重定向url合法性 + * @param url 下放ticket的url地址 + */ + public void checkRedirectUrl(String url) { + + // 1、是否是一个有效的url + if( ! SaFoxUtil.isUrl(url) ) { + throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001); + } + + // 2、截取掉?后面的部分 + int qIndex = url.indexOf("?"); + if(qIndex != -1) { + url = url.substring(0, qIndex); + } + + // 3、是否在[允许地址列表]之中 + List authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(",")); + if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) { + throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002); + } + + // 校验通过 √ + } + + + + // ------------------- SSO 模式三相关 ------------------- + + /** + * 为指定账号id注册单点注销回调URL + * @param loginId 账号id + * @param client 指定客户端标识,可为null + * @param sloCallbackUrl 单点注销时的回调URL + */ + public void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) { + // 如果提供的参数是空值,则直接返回,不进行任何操作 + if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) { + return; + } + + SaSession session = getStpLogic().getSessionByLoginId(loginId); + + // 取 + List scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new); + + // 加 + scmList.add(new SaSsoClientModel(client, sloCallbackUrl)); + + // 存 + session.set(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, scmList); + } + + /** + * 指定账号单点注销 + * @param loginId 指定账号 + */ + public void ssoLogout(Object loginId) { + + // 如果这个账号尚未登录,则无操作 + SaSession session = getStpLogic().getSessionByLoginId(loginId, false); + if(session == null) { + return; + } + + SaSsoServerConfig ssoConfig = getServerConfig(); + + // step.1 遍历通知 Client 端注销会话 + List scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new); + scmList.forEach(scm -> { + // url + String sloCallUrl = scm.getSsoLogoutCall(); + + // 参数 + Map paramsMap = new TreeMap<>(); + paramsMap.put(paramName.client, scm.getClient()); + paramsMap.put(paramName.loginId, loginId); + String signParamsStr = getSignTemplate(scm.getClient()).addSignParamsAndJoin(paramsMap); + + // 拼接 + String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr); + + // 发起请求 + ssoConfig.sendHttp.apply(finalUrl); + }); + + // step.2 Server端注销 + getStpLogic().logout(loginId); + } + + + // ---------------------- 构建URL ---------------------- + + /** + * 构建URL:Server端向Client下放ticket的地址 + * @param loginId 账号id + * @param client 客户端标识 + * @param redirect Client端提供的重定向地址 + * @return see note + */ + public String buildRedirectUrl(Object loginId, String client, String redirect) { + + // 校验 重定向地址 是否合法 + checkRedirectUrl(redirect); + + // 删掉 旧Ticket + deleteTicket(getTicketValue(loginId)); + + // 创建 新Ticket + String ticket = createTicket(loginId, client); + + // 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket) + return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket); + } + + /** + * 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug + * @param url url + * @return 编码过后的url + */ + public String encodeBackParam(String url) { + + // 获取back参数所在位置 + int index = url.indexOf("?" + paramName.back + "="); + if(index == -1) { + index = url.indexOf("&" + paramName.back + "="); + if(index == -1) { + return url; + } + } + + // 开始编码 + int length = paramName.back.length() + 2; + String back = url.substring(index + length); + back = SaFoxUtil.encodeUrl(back); + + // 放回url中 + url = url.substring(0, index + length) + back; + return url; + } + + + // ------------------- 返回相应key ------------------- + + /** + * 拼接key:Ticket 查 账号Id + * @param ticket ticket值 + * @return key + */ + public String splicingTicketSaveKey(String ticket) { + return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket; + } + + /** + * 拼接key:Ticket 查 所属的 client + * @param ticket ticket值 + * @return key + */ + public String splicingTicketToClientSaveKey(String ticket) { + return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket; + } + + /** + * 拼接key:账号Id 反查 Ticket + * @param id 账号id + * @return key + */ + public String splicingTicketIndexKey(Object id) { + return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id; + } + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoTemplate.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoTemplate.java new file mode 100644 index 00000000..8a525e8d --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoTemplate.java @@ -0,0 +1,85 @@ +/* + * 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.sso.template; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.sign.SaSignTemplate; +import cn.dev33.satoken.sso.name.ApiName; +import cn.dev33.satoken.sso.name.ParamName; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +import java.util.Map; + +/** + * Sa-Token SSO 模板方法类 (公共端) + * + * @author click33 + * @since 1.30.0 + */ +public class SaSsoTemplate { + + // ---------------------- 全局配置 ---------------------- + + /** + * 所有 API 名称 + */ + public ApiName apiName = new ApiName(); + + /** + * 所有参数名称 + */ + public ParamName paramName = new ParamName(); + + /** + * @param paramName 替换 paramName 对象 + * @return 对象自身 + */ + public SaSsoTemplate setParamName(ParamName paramName) { + this.paramName = paramName; + return this; + } + + /** + * @param apiName 替换 apiName 对象 + * @return 对象自身 + */ + public SaSsoTemplate setApiName(ApiName apiName) { + this.apiName = apiName; + return this; + } + + /** + * 获取底层使用的会话对象 + * @return / + */ + public StpLogic getStpLogic() { + return StpUtil.stpLogic; + } + + /** + * 获取底层使用的 API 签名对象 + * @param client 指定客户端标识,填 null 代表获取默认的 + * @return / + */ + public SaSignTemplate getSignTemplate(String client) { + // 框架默认只返回全局 SaSignTemplate,client 参数留作开发者扩展 + return SaManager.getSaSignTemplate(); + } + + +} diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java similarity index 65% rename from sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java rename to sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java index 22685abc..4ff93fc4 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoUtil.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.dev33.satoken.sso; +package cn.dev33.satoken.sso.template; -import cn.dev33.satoken.util.SaResult; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; import java.util.Map; @@ -27,12 +28,6 @@ import java.util.Map; */ public class SaSsoUtil { - /** - * 底层 SaSsoTemplate 对象 - */ - public static SaSsoTemplate ssoTemplate = new SaSsoTemplate(); - - // ---------------------- Ticket 操作 ---------------------- /** @@ -42,7 +37,7 @@ public class SaSsoUtil { * @return Ticket码 */ public static String createTicket(Object loginId, String client) { - return ssoTemplate.createTicket(loginId, client); + return SaSsoServerProcessor.instance.ssoServerTemplate.createTicket(loginId, client); } /** @@ -50,7 +45,7 @@ public class SaSsoUtil { * @param ticket Ticket码 */ public static void deleteTicket(String ticket) { - ssoTemplate.deleteTicket(ticket); + SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicket(ticket); } /** @@ -58,7 +53,7 @@ public class SaSsoUtil { * @param loginId 账号id */ public static void deleteTicketIndex(Object loginId) { - ssoTemplate.deleteTicketIndex(loginId); + SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(loginId); } /** @@ -67,7 +62,7 @@ public class SaSsoUtil { * @return 账号id */ public static Object getLoginId(String ticket) { - return ssoTemplate.getLoginId(ticket); + return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket); } /** @@ -78,7 +73,7 @@ public class SaSsoUtil { * @return 账号id */ public static T getLoginId(String ticket, Class cs) { - return ssoTemplate.getLoginId(ticket, cs); + return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket, cs); } /** @@ -87,7 +82,7 @@ public class SaSsoUtil { * @return 账号id */ public static Object checkTicket(String ticket) { - return ssoTemplate.checkTicket(ticket); + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket); } /** @@ -97,7 +92,7 @@ public class SaSsoUtil { * @return 账号id */ public static Object checkTicket(String ticket, String client) { - return ssoTemplate.checkTicket(ticket, client); + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, client); } /** @@ -105,7 +100,7 @@ public class SaSsoUtil { * @return see note */ public static String getAllowUrl() { - return ssoTemplate.getAllowUrl(); + return SaSsoServerProcessor.instance.ssoServerTemplate.getAllowUrl(); } /** @@ -113,7 +108,7 @@ public class SaSsoUtil { * @param url 下放ticket的url地址 */ public static void checkRedirectUrl(String url) { - ssoTemplate.checkRedirectUrl(url); + SaSsoServerProcessor.instance.ssoServerTemplate.checkRedirectUrl(url); } @@ -126,16 +121,17 @@ public class SaSsoUtil { * @return 构建完毕的URL */ public static String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) { - return ssoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl); + return SaSsoClientProcessor.instance.ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl); } /** * 为指定账号id注册单点注销回调URL * @param loginId 账号id + * @param client 指定客户端标识,可为null * @param sloCallbackUrl 单点注销时的回调URL */ - public static void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) { - ssoTemplate.registerSloCallbackUrl(loginId, sloCallbackUrl); + public static void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) { + SaSsoServerProcessor.instance.ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallbackUrl); } /** @@ -144,7 +140,7 @@ public class SaSsoUtil { * @return 单点注销URL */ public static String buildSloUrl(Object loginId) { - return ssoTemplate.buildSloUrl(loginId); + return SaSsoClientProcessor.instance.ssoClientTemplate.buildSloUrl(loginId); } /** @@ -152,7 +148,7 @@ public class SaSsoUtil { * @param loginId 指定账号 */ public static void ssoLogout(Object loginId) { - ssoTemplate.ssoLogout(loginId); + SaSsoServerProcessor.instance.ssoServerTemplate.ssoLogout(loginId); } /** @@ -161,7 +157,7 @@ public class SaSsoUtil { * @return 查询结果 */ public static Object getData(Map paramMap) { - return ssoTemplate.getData(paramMap); + return SaSsoClientProcessor.instance.ssoClientTemplate.getData(paramMap); } /** @@ -171,7 +167,7 @@ public class SaSsoUtil { * @return 查询结果 */ public static Object getData(String path, Map paramMap) { - return ssoTemplate.getData(path, paramMap); + return SaSsoClientProcessor.instance.ssoClientTemplate.getData(path, paramMap); } @@ -184,7 +180,7 @@ public class SaSsoUtil { * @return [SSO-Server端-认证地址 ] */ public static String buildServerAuthUrl(String clientLoginUrl, String back) { - return ssoTemplate.buildServerAuthUrl(clientLoginUrl, back); + return SaSsoClientProcessor.instance.ssoClientTemplate.buildServerAuthUrl(clientLoginUrl, back); } /** @@ -195,7 +191,7 @@ public class SaSsoUtil { * @return see note */ public static String buildRedirectUrl(Object loginId, String client, String redirect) { - return ssoTemplate.buildRedirectUrl(loginId, client, redirect); + return SaSsoServerProcessor.instance.ssoServerTemplate.buildRedirectUrl(loginId, client, redirect); } /** @@ -204,7 +200,7 @@ public class SaSsoUtil { * @return / */ public static String buildGetDataUrl(Map paramMap) { - return ssoTemplate.buildGetDataUrl(paramMap); + return SaSsoClientProcessor.instance.ssoClientTemplate.buildGetDataUrl(paramMap); } /** @@ -213,62 +209,8 @@ public class SaSsoUtil { * @return / */ public static String buildCustomPathUrl(String path, Map paramMap) { - return ssoTemplate.buildCustomPathUrl(path, paramMap); + return SaSsoClientProcessor.instance.ssoClientTemplate.buildCustomPathUrl(path, paramMap); } - - // ------------------- 发起请求 ------------------- - - /** - * 发出请求,并返回 SaResult 结果 - * @param url 请求地址 - * @return 返回的结果 - */ - public static SaResult request(String url) { - return ssoTemplate.request(url); - } - - /** - * 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面 - * @param url 请求地址 - * @param paramMap 请求原始参数列表 - * @return 加工后的url - */ - public static String joinParamMapAndSign(String url, Map paramMap) { - return ssoTemplate.joinLoginIdAndSign(url, paramMap); - } - - /** - * 给 url 拼接 loginId 参数,并拼接 sign 等参数 - * @param url 链接 - * @param loginId 账号id - * @return 加工后的url - */ - public static String joinLoginIdAndSign(String url, Object loginId) { - return ssoTemplate.joinLoginIdAndSign(url, loginId); - } - - - // -------- 以下方法已废弃,仅为兼容旧版本而保留 -------- - - /** - * 构建URL:Server端 账号资料查询地址 - * @param loginId 账号id - * @return Server端 账号资料查询地址 - */ - @Deprecated - public static String buildUserinfoUrl(Object loginId) { - return ssoTemplate.buildUserinfoUrl(loginId); - } - - /** - * 获取:账号资料 - * @param loginId 账号id - * @return 账号资料 - */ - @Deprecated - public static Object getUserinfo(Object loginId) { - return ssoTemplate.getUserinfo(loginId); - } } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java similarity index 86% rename from sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java rename to sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java index d9c88797..6c2654fb 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.dev33.satoken.sso; +package cn.dev33.satoken.sso.util; /** * Sa-Token-SSO模块相关常量 @@ -24,8 +24,12 @@ package cn.dev33.satoken.sso; public class SaSsoConsts { /** Client端单点注销回调URL的Set集合,存储在Session中使用的key */ + @Deprecated public static final String SLO_CALLBACK_SET_KEY = "SLO_CALLBACK_SET_KEY_"; + /** Client 端 Model 信息的 List 集合,存储在 SaSession 中使用的key */ + public static final String SSO_CLIENT_MODEL_LIST_KEY_ = "SSO_CLIENT_MODEL_LIST_KEY_"; + /** 表示OK的返回结果 */ public static final String OK = "ok"; diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/sso/SaSsoAutoConfigure.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/sso/SaSsoAutoConfigure.java index afdb4119..c09d5b3b 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/sso/SaSsoAutoConfigure.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/sso/SaSsoAutoConfigure.java @@ -18,8 +18,8 @@ package cn.dev33.satoken.solon.sso; import cn.dev33.satoken.config.SaSsoConfig; import cn.dev33.satoken.sso.SaSsoManager; import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoTemplate; -import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.sso.template.SaSsoTemplate; +import cn.dev33.satoken.sso.template.SaSsoUtil; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Condition; import org.noear.solon.annotation.Configuration; diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanInject.java b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanInject.java index 90bc7981..9e8f07b9 100644 --- a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanInject.java +++ b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanInject.java @@ -15,17 +15,18 @@ */ package cn.dev33.satoken.spring.sso; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import cn.dev33.satoken.config.SaSsoConfig; -import cn.dev33.satoken.sso.SaSsoManager; -import cn.dev33.satoken.sso.SaSsoProcessor; -import cn.dev33.satoken.sso.SaSsoTemplate; -import cn.dev33.satoken.sso.SaSsoUtil; - /** - * 注入 Sa-Token-SSO 所需要的 Bean + * 注入 Sa-Token SSO 所需要的 Bean * * @author click33 * @since 1.34.0 @@ -34,24 +35,43 @@ import cn.dev33.satoken.sso.SaSsoUtil; public class SaSsoBeanInject { /** - * 注入 Sa-Token-SSO 配置类 + * 注入 Sa-Token SSO Server 端 配置类 * - * @param saSsoConfig 配置对象 + * @param serverConfig 配置对象 */ @Autowired(required = false) - public void setSaOAuth2Config(SaSsoConfig saSsoConfig) { - SaSsoManager.setConfig(saSsoConfig); + public void setSaSsoServerConfig(SaSsoServerConfig serverConfig) { + SaSsoManager.setServerConfig(serverConfig); } /** - * 注入 SSO 模板代码类 - * - * @param ssoTemplate SaSsoTemplate 对象 + * 注入 Sa-Token SSO Client 端 配置类 + * + * @param clientConfig 配置对象 */ @Autowired(required = false) - public void setSaSsoTemplate(SaSsoTemplate ssoTemplate) { - SaSsoUtil.ssoTemplate = ssoTemplate; - SaSsoProcessor.instance.ssoTemplate = ssoTemplate; + public void setSaSsoClientConfig(SaSsoClientConfig clientConfig) { + SaSsoManager.setClientConfig(clientConfig); + } + + /** + * 注入 SSO 模板代码类 (Server 端) + * + * @param ssoServerTemplate / + */ + @Autowired(required = false) + public void setSaSsoServerTemplate(SaSsoServerTemplate ssoServerTemplate) { + SaSsoServerProcessor.instance.ssoServerTemplate = ssoServerTemplate; + } + + /** + * 注入 SSO 模板代码类 (Client 端) + * + * @param ssoClientTemplate / + */ + @Autowired(required = false) + public void setSaSsoClientTemplate(SaSsoClientTemplate ssoClientTemplate) { + SaSsoClientProcessor.instance.ssoClientTemplate = ssoClientTemplate; } } diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanRegister.java b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanRegister.java index 04400323..f9a6245c 100644 --- a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanRegister.java +++ b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/sso/SaSsoBeanRegister.java @@ -15,15 +15,15 @@ */ package cn.dev33.satoken.spring.sso; +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.config.SaSsoServerConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; -import cn.dev33.satoken.config.SaSsoConfig; -import cn.dev33.satoken.sso.SaSsoManager; - /** - * 注册 Sa-Token-SSO 所需要的 Bean + * 注册 Sa-Token SSO 所需要的 Bean * * @author click33 * @since 1.34.0 @@ -32,13 +32,23 @@ import cn.dev33.satoken.sso.SaSsoManager; public class SaSsoBeanRegister { /** - * 获取 SSO 配置对象 - * @return 配置对象 + * 获取 SSO Server 端 配置对象 + * @return 配置对象 */ @Bean - @ConfigurationProperties(prefix = "sa-token.sso") - public SaSsoConfig getSaSsoConfig() { - return new SaSsoConfig(); + @ConfigurationProperties(prefix = "sa-token.sso-server") + public SaSsoServerConfig getSaSsoServerConfig() { + return new SaSsoServerConfig(); } - + + /** + * 获取 SSO Client 端 配置对象 + * @return 配置对象 + */ + @Bean + @ConfigurationProperties(prefix = "sa-token.sso-client") + public SaSsoClientConfig getSaSsoClientConfig() { + return new SaSsoClientConfig(); + } + } diff --git a/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/integrate/configure/inject/MySaSsoTemplate.java b/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/integrate/configure/inject/MySaSsoTemplate.java index fa82f7d8..caeb06e5 100644 --- a/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/integrate/configure/inject/MySaSsoTemplate.java +++ b/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/integrate/configure/inject/MySaSsoTemplate.java @@ -17,7 +17,7 @@ package cn.dev33.satoken.integrate.configure.inject; import org.springframework.stereotype.Component; -import cn.dev33.satoken.sso.SaSsoTemplate; +import cn.dev33.satoken.sso.template.SaSsoTemplate; /** * 自定义 Sa-SSO 模板方法