diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/SaSsoServerApplication.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/SaSsoServerApplication.java index b898ed83..1d2c46d2 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/SaSsoServerApplication.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/SaSsoServerApplication.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,11 @@ public class SaSsoServerApplication { public static void main(String[] args) { SpringApplication.run(SaSsoServerApplication.class, args); - System.out.println("\n------ Sa-Token-SSO 统一认证中心启动成功 "); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getServerConfig()); + System.out.println(); } } \ No newline at end of file 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 7302afd7..6e2e64ee 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 @@ -25,7 +25,7 @@ public class SsoServerController { * http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址 * http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd * http://{host}:{port}/sso/checkTicket -- Ticket校验接口(isHttp=true时打开),接受参数:ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选] - * http://{host}:{port}/sso/signout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、secretkey=接口调用秘钥 + * http://{host}:{port}/sso/signout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、sign=参数签名 */ @RequestMapping("/sso/*") public Object ssoRequest() { diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/pom.xml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/pom.xml index 513629c2..e2599ed8 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/pom.xml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso2-client/pom.xml @@ -60,7 +60,14 @@ sa-token-alone-redis ${sa-token.version} - + + + + com.dtflys.forest + forest-spring-boot-starter + 1.5.26 + + 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 11383970..4eeed193 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,8 +1,11 @@ package com.pj.sso; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; +import com.dtflys.forest.Forest; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -35,6 +38,18 @@ public class SsoClientController { return SaSsoClientProcessor.instance.dister(); } + // 配置SSO相关参数 + @Autowired + private void configSso(SaSsoClientConfig ssoClient) { + // 配置Http请求处理器 + ssoClient.sendHttp = url -> { + System.out.println("------ 发起请求:" + url); + String resStr = Forest.get(url).executeAsString(); + System.out.println("------ 请求结果:" + resStr); + return resStr; + }; + } + // 全局异常拦截 @ExceptionHandler public SaResult handlerException(Exception e) { 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 f971cae7..8ce56dd6 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 @@ -4,14 +4,15 @@ server: # sa-token配置 sa-token: - # 每次登录时,产生不同的token - is-share: false # SSO-相关配置 sso-client: - # SSO-Server端 统一认证地址 + # SSO-Server 端主机地址 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 + sign: + # API 接口调用秘钥 + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) alone-redis: @@ -35,8 +36,7 @@ sa-token: max-idle: 10 # 连接池中的最小空闲连接 min-idle: 0 - - - - - \ No newline at end of file + +forest: + # 关闭 forest 请求日志打印 + log-enabled: false diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/.gitignore b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/.gitignore new file mode 100644 index 00000000..99a6e767 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/.gitignore @@ -0,0 +1,12 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.idea/ + +.factorypath \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/pom.xml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/pom.xml new file mode 100644 index 00000000..cd61b769 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/pom.xml @@ -0,0 +1,68 @@ + + 4.0.0 + cn.dev33 + sa-token-demo-sso3-client-test2 + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.14 + + + + + + 1.37.0 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + cn.dev33 + sa-token-spring-boot-starter + ${sa-token.version} + + + + + cn.dev33 + sa-token-sso + ${sa-token.version} + + + + + cn.dev33 + sa-token-redis-jackson + ${sa-token.version} + + + + + org.apache.commons + commons-pool2 + + + + + com.dtflys.forest + forest-spring-boot-starter + 1.5.26 + + + + + + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/java/com/pj/SaSso3ClientTest2Application.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/java/com/pj/SaSso3ClientTest2Application.java new file mode 100644 index 00000000..1374e3b7 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/java/com/pj/SaSso3ClientTest2Application.java @@ -0,0 +1,23 @@ +package com.pj; + +import cn.dev33.satoken.sso.SaSsoManager; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SaSso3ClientTest2Application { + + public static void main(String[] args) { + SpringApplication.run(SaSso3ClientTest2Application.class, args); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getClientConfig()); + System.out.println("测试访问应用端一: http://sa-sso-client1.com:9032"); + System.out.println("测试访问应用端二: http://sa-sso-client2.com:9032"); + System.out.println("测试访问应用端三: http://sa-sso-client3.com:9032"); + 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-test2/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/java/com/pj/sso/SsoClientController.java new file mode 100644 index 00000000..2492688a --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/java/com/pj/sso/SsoClientController.java @@ -0,0 +1,78 @@ +package com.pj.sso; + +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; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * Sa-Token-SSO Client端 Controller + * @author click33 + */ +@RestController +public class SsoClientController { + + // SSO-Client端:首页 + @RequestMapping("/") + public String index() { + String str = "

Sa-Token SSO-Client 应用端

" + + "

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

" + + "

登录" + + " 注销

"; + return str; + } + + /* + * SSO-Client端:处理所有SSO相关请求 + * http://{host}:{port}/sso/login -- Client端登录地址,接受参数:back=登录后的跳转地址 + * http://{host}:{port}/sso/logout -- Client端单点注销地址(isSlo=true时打开),接受参数:back=注销后的跳转地址 + * http://{host}:{port}/sso/logoutCall -- Client端单点注销回调地址(isSlo=true时打开),此接口为框架回调,开发者无需关心 + */ + @RequestMapping("/sso/*") + public Object ssoRequest() { + return SaSsoClientProcessor.instance.dister(); + } + + // 配置SSO相关参数 + @Autowired + private void configSso(SaSsoClientConfig ssoClient) { + // 配置Http请求处理器 + ssoClient.sendHttp = url -> { + System.out.println("------ 发起请求:" + url); + String resStr = Forest.get(url).executeAsString(); + System.out.println("------ 请求结果:" + resStr); + return resStr; + }; + } + + // 查询我的账号信息 + @RequestMapping("/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; + } + + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + +} diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/resources/application.yml new file mode 100644 index 00000000..ebec4dde --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso3-client-test2/src/main/resources/application.yml @@ -0,0 +1,49 @@ +# 端口 +server: + port: 9032 + +# sa-token配置 +sa-token: + # sso-client 相关配置 + sso-client: + # client 标识 + client: sso-client3-test2 + # sso-server 端主机地址 + server-url: http://sa-sso-server.com:9000 + # 使用 Http 请求校验ticket (模式三) + is-http: true + + sign: + # API 接口调用秘钥 + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + +spring: + # 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis) + redis: + # Redis数据库索引 + database: 4 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + +forest: + # 关闭 forest 请求日志打印 + log-enabled: false + + + \ No newline at end of file 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 f1bce7e4..ef48e853 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 @@ -6,6 +6,8 @@ server: sa-token: # sso-client 相关配置 sso-client: + # client 标识 + client: sso-client3 # sso-server 端主机地址 server-url: http://sa-sso-server.com:9000 # 使用 Http 请求校验ticket (模式三) @@ -19,7 +21,7 @@ spring: # 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis) redis: # Redis数据库索引 - database: 2 + database: 3 # Redis服务器地址 host: 127.0.0.1 # Redis服务器连接端口 diff --git a/sa-token-doc/sso/sso-apidoc.md b/sa-token-doc/sso/sso-apidoc.md index 8cb487b2..14d29257 100644 --- a/sa-token-doc/sso/sso-apidoc.md +++ b/sa-token-doc/sso/sso-apidoc.md @@ -22,6 +22,7 @@ http://{host}:{port}/sso/auth | :-------- | :-------- | :-------- | | redirect | 是 | 登录成功后的重定向地址,一般填写 location.href(从哪来回哪去) | | mode | 否 | 授权模式,取值 [simple, ticket],simple=登录后直接重定向,ticket=带着ticket参数重定向,默认值为ticket | +| client | 否 | 客户端标识,可不填,代表是一个匿名应用,若填写了,则校验 ticket 时也必须时这个 client 才可以校验成功 | 访问接口后有两种情况: - 情况一:当前会话在 SSO 认证中心未登录,会进入登录页开始登录。 @@ -40,7 +41,7 @@ http://{host}:{port}/sso/doLogin | name | 是 | 用户名 | | pwd | 是 | 密码 | -此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `sso.setDoLoginHandle` 函数中,此函数的返回值即是此接口的响应值。 +此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `ssoServer.doLoginHandle` 函数中,此函数的返回值即是此接口的响应值。 另外需要注意:此接口并非只能携带 name、pwd 参数,因为你可以在方法里通过 `SaHolder.getRequest().getParam("xxx")` 来获取前端提交的其它参数。 @@ -58,6 +59,7 @@ http://{host}:{port}/sso/checkTicket | :-------- | :-------- | :-------- | | ticket | 是 | 在步骤 1 中授权重定向时的 ticket 参数 | | ssoLogoutCall | 否 | 单点注销时的回调通知地址,只在SSO模式三单点注销时需要携带此参数| +| client | 否 | 客户端标识,可不填,代表是一个匿名应用,若填写了,则必须填写的和 `/sso/auth` 登录时填写的一致才可以校验成功 | 返回值场景: - 校验成功时: @@ -106,6 +108,7 @@ http://{host}:{port}/sso/signout?back=xxx | timestamp | 是 | 当前时间戳,13位 | | nonce | 是 | 随机字符串 | | sign | 是 | 签名,生成算法:`md5( loginId={账号id}&nonce={随机字符串}×tamp={13位时间戳}&key={secretkey秘钥} )` | +| client | 否 | 客户端标识,可不填,一般在帮助 “sso-server 端不同client不同秘钥” 的场景下找到对应秘钥时,才填写 | 例如: ``` url @@ -198,7 +201,8 @@ http://{host}:{port}/sso/logoutCall | loginId | 是 | 要注销的账号 id | | timestamp | 是 | 当前时间戳,13位 | | nonce | 是 | 随机字符串 | -| sign | 是 | 签名,生成算法:`md5( loginId={账号id}&nonce={随机字符串}×tamp={13位时间戳}&key={secretkey秘钥} )` | +| client | 否 | 客户端标识,如果你在登录时向 sso-server 端传递了 client 值,那么在此处 sso-server 也会给你回传过来,否则此参数无值 | +| sign | 是 | 签名,生成算法:`md5( loginId={账号id}&nonce={随机字符串}×tamp={13位时间戳}&key={secretkey秘钥} )` 如果 client 参数有值,则client也要参与签名,放在 loginId 参数签名(字典顺序)| 返回数据: diff --git a/sa-token-doc/sso/sso-server.md b/sa-token-doc/sso/sso-server.md index b87084ab..9193a8ab 100644 --- a/sa-token-doc/sso/sso-server.md +++ b/sa-token-doc/sso/sso-server.md @@ -90,21 +90,21 @@ implementation 'com.dtflys.forest:forest-spring-boot-starter:1.5.26' @RestController public class SsoServerController { - /* + /** * SSO-Server端:处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口) */ @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 = () -> { String msg = "当前会话在SSO-Server端尚未登录,请先访问" + " doLogin登录 " + "进行登录之后,刷新页面开始授权"; @@ -112,7 +112,7 @@ public class SsoServerController { }; // 配置:登录处理函数 - sso.doLoginHandle = (name, pwd) -> { + ssoServer.doLoginHandle = (name, pwd) -> { // 此处仅做模拟登录,真实环境应该查询数据进行登录 if("sa".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); @@ -122,11 +122,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; @@ -138,8 +139,8 @@ public class SsoServerController { ``` 注意: -- 在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数,可通过`SaHolder.getRequest().getParam("xxx")`来获取 -- 在 `setSendHttp` 函数中,使用 `try-catch` 是为了提高整个注销流程的容错性,避免在一些极端情况下注销失败(例如:某个 Client 端上线之后又下线,导致 http 请求无法调用成功,从而阻断了整个注销流程) +- 在`doLoginHandle`函数里如果要获取name, pwd以外的参数,可通过`SaHolder.getRequest().getParam("xxx")`来获取 +- 在 `sendHttp` 函数中,使用 `try-catch` 是为了提高整个注销流程的容错性,避免在一些极端情况下注销失败(例如:某个 Client 端上线之后又下线,导致 http 请求无法调用成功,从而阻断了整个注销流程) 全局异常处理: ``` java @@ -173,7 +174,7 @@ sa-token: # domain: stp.com # ------- SSO-模式二相关配置 - sso: + sso-server: # Ticket有效期 (单位: 秒),默认五分钟 ticket-timeout: 300 # 所有允许的授权回调地址 @@ -215,13 +216,13 @@ server.port=9000 # ------- SSO-模式二相关配置 # Ticket有效期 (单位: 秒),默认五分钟 -sa-token.sso.ticket-timeout=300 +sa-token.sso-server.ticket-timeout=300 # 所有允许的授权回调地址 -sa-token.sso.allow-url=* +sa-token.sso-server.allow-url=* # ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开) # 是否打开模式三 -sa-token.sso.is-http=true +sa-token.sso-server.is-http=true # API 接口调用秘钥 sa-token.sign.secret-key=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor @@ -243,7 +244,7 @@ forest.log-enabled: false -注意点:`sa-token.sso.allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项) +注意点:`sa-token.sso-server.allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项) ### 4、创建启动类 @@ -252,7 +253,11 @@ forest.log-enabled: false public class SaSsoServerApplication { public static void main(String[] args) { SpringApplication.run(SaSsoServerApplication.class, args); - System.out.println("\n------ Sa-Token-SSO 认证中心启动成功"); + + System.out.println(); + System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------"); + System.out.println("配置信息:" + SaSsoManager.getServerConfig()); + System.out.println(); } } ``` @@ -261,7 +266,7 @@ public class SaSsoServerApplication { ![sso-server-start](https://oss.dev33.cn/sa-token/doc/sso/sso-server-start.png 's-w-sh') -访问统一授权地址(仅测试SSO Server部署是否成功访问localhost,测试SSO模式一到模式三建议按照对应文档的域名进行配置并访问): +访问统一授权地址(仅测试 SSO-Server 是否部署成功,暂时还不需要点击登录): - [http://localhost:9000/sso/auth](http://localhost:9000/sso/auth) ![sso-server-init-login.png](https://oss.dev33.cn/sa-token/doc/sso/sso-server-init-login.png 's-w-sh') diff --git a/sa-token-doc/sso/sso-type1.md b/sa-token-doc/sso/sso-type1.md index f5e59fa7..2bac82ca 100644 --- a/sa-token-doc/sso/sso-type1.md +++ b/sa-token-doc/sso/sso-type1.md @@ -135,8 +135,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() + "

" + "

登录 " + @@ -166,11 +166,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: @@ -192,10 +190,8 @@ server.port=9001 ######### Sa-Token 配置 ######### -# SSO-Server端-单点登录授权地址 -sa-token.sso.auth-url=http://sso.stp.com:9000/sso/auth -# SSO-Server端-单点注销地址 -sa-token.sso.slo-url=http://sso.stp.com:9000/sso/signout +# SSO-Server端主机地址 +sa-token.sso-client.server-url=http://sso.stp.com:9000 # 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) # Redis数据库索引 @@ -222,7 +218,15 @@ sa-token.alone-redis.timeout=10s 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(); } } ``` diff --git a/sa-token-doc/sso/sso-type2.md b/sa-token-doc/sso/sso-type2.md index 4ebe1e87..32d115ea 100644 --- a/sa-token-doc/sso/sso-type2.md +++ b/sa-token-doc/sso/sso-type2.md @@ -64,7 +64,7 @@ sa-token.cookie.domain=stp.com ``` -此为模式一专属配置,现在我们将其注释掉 +此为模式一专属配置,现在我们将其注释掉**(一定要注释掉!)** #### 3.2、创建 SSO-Client 端项目 @@ -151,7 +151,7 @@ public class SsoClientController { */ @RequestMapping("/sso/*") public Object ssoRequest() { - return SaSsoProcessor.instance.clientDister(); + return SaSsoClientProcessor.instance.dister(); } } @@ -170,9 +170,9 @@ server: # sa-token配置 sa-token: # SSO-相关配置 - sso: - # SSO-Server端 统一认证地址 - auth-url: http://sa-sso-server.com:9000/sso/auth + sso-client: + # SSO-Server 端主机地址 + server-url: http://sa-sso-server.com:9000 # 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) alone-redis: @@ -194,7 +194,7 @@ server.port=9001 ######### Sa-Token 配置 ######### # SSO-Server端 统一认证地址 -sa-token.sso.auth-url=http://sa-sso-server.com:9000/sso/auth +sa-token.sso-client.server-url=http://sa-sso-server.com:9000 # 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) # Redis数据库索引 @@ -211,7 +211,7 @@ sa-token.alone-redis.timeout=10s -注意点:`sa-token.alone-redis` 的配置需要和SSO-Server端连接同一个Redis(database也要一样) +注意点:`sa-token.alone-redis` 的配置需要和SSO-Server端连接同一个Redis**(database 值也要一样!database 值也要一样!database 值也要一样!重说三!)** #### 3.5、写启动类 ``` java @@ -219,7 +219,15 @@ sa-token.alone-redis.timeout=10s 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(); } } ``` @@ -230,6 +238,8 @@ public class SaSso2ClientApplication { (1) 依次启动 `SSO-Server` 与 `SSO-Client`,然后从浏览器访问:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/) + + ![sso-client-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-client-index.png 's-w-sh') (2) 首次打开,提示当前未登录,我们点击 **`登录`** 按钮,页面会被重定向到登录中心 @@ -282,10 +292,121 @@ public class SaSso2ClientApplication { 默认测试密码:`sa / 123456`,其余流程保持不变 --> + + +### 5、无刷单点注销 + +有了单点登录,就必然伴随着单点注销(一处注销,全端下线) + +如果你的所有 client 都是基于 SSO 模式二来对接的,那么单点注销其实很简单: + +``` java +// 在 `sa-token.is-share=true` 的情况下,调用此代码即可单点注销: +StpUtil.logout(); + +// 在 `sa-token.is-share=false` 的情况下,调用此代码即可单点注销: +StpUtil.logout(StpUtil.getLoginId()); +``` + +你可能会比较疑惑,这不就是个普通的会话注销API吗,为什么会有单点注销的效果? + +因为模式二需要各个 sso-client 和 sso-server 连接同一个 redis,即使登录再多的 client,本质上对应的仍是同一个会话,因此可以做到任意一处调用注销,全端一起下线的效果。 + +而如果你的各个 client 架构各不相同,有的是模式二对接,有的是模式三对接,则需要麻烦一点才能做到单点注销。 + +这里的“麻烦”指两处:1、框架内部逻辑麻烦;2、开发者集成麻烦。 + +框架内部的麻烦 sa-token-sso 已经封装完毕,无需过多关注,而开发者的麻烦步骤也不是很多: +#### 5.1、增加 pom.xml 配置 -### 5、跨 Redis 的单点登录 + + +``` xml + + + com.dtflys.forest + forest-spring-boot-starter + 1.5.26 + +``` + +``` gradle +// Http请求工具 +implementation 'com.dtflys.forest:forest-spring-boot-starter:1.5.26' +``` + + +Forest 是一个轻量级 http 请求工具,详情参考:[Forest](https://forest.dtflyx.com/) + +因为我们已经在控制台手动打印 url 请求日志了,所以此处 `forest.log-enabled=false` 关闭 Forest 框架自身的日志打印,这不是必须的,你可以将其打开。 + + +#### 5.2、SSO-Client 端新增配置:API调用秘钥 + +在 `application.yml` 增加: + + + +``` yaml +sa-token: + sign: + # API 接口调用秘钥 + secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + +forest: + # 关闭 forest 请求日志打印 + log-enabled: false +``` + +``` properties +# 接口调用秘钥 +sa-token.sign.secret-key=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor +``` + + +注意 secretkey 秘钥需要与SSO认证中心的一致 + +#### 5.3、SSO-Client 配置 http 请求处理器 +``` java +// 配置SSO相关参数 +@Autowired +private void configSso(SaSsoClientConfig ssoClient) { + // 配置Http请求处理器 + ssoClient.sendHttp = url -> { + System.out.println("------ 发起请求:" + url); + String resStr = Forest.get(url).executeAsString(); + System.out.println("------ 请求结果:" + resStr); + return resStr; + }; +} +``` + +#### 5.3、启动测试 +重启项目,依次登录三个 client: +- [http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/) +- [http://sa-sso-client2.com:9001/](http://sa-sso-client2.com:9001/) +- [http://sa-sso-client3.com:9001/](http://sa-sso-client3.com:9001/) + +![sso-type3-client-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type3-client-index.png 's-w-sh') + +在任意一个 client 里,点击 **`[注销]`** 按钮,即可单点注销成功(打开另外两个client,刷新一下页面,登录态丢失)。 + + + +![sso-type3-slo-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type3-slo-index.png 's-w-sh') + +PS:这里我们为了方便演示,使用的是超链接跳页面的形式,正式项目中使用 Ajax 调用接口即可做到无刷单点登录退出。 + +例如,我们使用 [Apifox 接口测试工具](https://www.apifox.cn/) 可以做到同样的效果: + +![sso-slo-apifox.png](https://oss.dev33.cn/sa-token/doc/sso/sso-slo-apifox.png 's-w-sh') + +测试完毕! + + +### 6、跨 Redis 的单点登录 以上流程解决了跨域模式下的单点登录,但是后端仍然采用了共享Redis来同步会话,如果我们的架构设计中Client端与Server端无法共享Redis,又该怎么完成单点登录? 这就要采用模式三了,且往下看:[SSO模式三:Http请求获取会话](/sso/sso-type3) diff --git a/sa-token-doc/sso/sso-type3.md b/sa-token-doc/sso/sso-type3.md index c339424f..4d9d0a5b 100644 --- a/sa-token-doc/sso/sso-type3.md +++ b/sa-token-doc/sso/sso-type3.md @@ -10,7 +10,7 @@ 1. Client 端无法直连 Redis 校验 ticket,取出账号id。 2. Client 端无法与 Server 端共用一套会话,需要自行维护子会话。 -3. 由于不是一套会话,所以无法“一次注销,全端下线”,需要额外编写代码完成单点注销。 +3. 由于不是一套会话,所以无法“一次注销,全端下线”,需要额外编写代码完成单点注销(其实此处的“额外编写代码”已在SSO模式二“无刷单点注销”部分介绍完毕)。 所以模式三的主要目标:也就是在 模式二的基础上 解决上述 三个难题 @@ -20,76 +20,24 @@ ### 2、在Client 端更改 Ticket 校验方式 -#### 2.1、增加 pom.xml 配置 - - - -``` xml - - - com.dtflys.forest - forest-spring-boot-starter - 1.5.26 - -``` - -``` gradle -// Http请求工具 -implementation 'com.dtflys.forest:forest-spring-boot-starter:1.5.26' -``` - - - -> Forest 是一个轻量级 http 请求工具,详情参考:[Forest](https://forest.dtflyx.com/) - -#### 2.2、配置 http 请求处理器 -在SSO-Client端的 `SsoClientController` 中,新增以下配置 -``` java -// 配置SSO相关参数 -@Autowired -private void configSso(SaSsoConfig sso) { - // ... 其他代码 - - // 配置 Http 请求处理器 - sso.sendHttp = url -> { - System.out.println("------ 发起请求:" + url); - return Forest.get(url).executeAsString(); - }; -} -``` - -#### 2.3、application.yml 新增配置 +在 application.yml 新增配置: ``` yaml sa-token: - sso: + sso-client: # 打开模式三(使用Http请求校验ticket) is-http: true - # SSO-Server端 ticket校验地址 - check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket - -forest: - # 关闭 forest 请求日志打印 - log-enabled: false ``` ``` properties # 打开模式三(使用Http请求校验ticket) -sa-token.sso.is-http=true -# SSO-Server端 ticket校验地址 -sa-token.sso.check-ticket-url=http://sa-sso-server.com:9000/sso/checkTicket - -# 关闭 forest 请求日志打印 -forest.log-enabled: false +sa-token.sso-client.is-http=true ``` -因为我们已经在控制台手动打印 url 请求日志了,所以此处 `forest.log-enabled=false` 关闭 Forest 框架自身的日志打印,这不是必须的,你可以将其打开。 - -#### 2.4、启动项目测试 重启项目,访问测试: - [http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/) - [http://sa-sso-client2.com:9001/](http://sa-sso-client2.com:9001/) @@ -134,23 +82,7 @@ public SaResult getData(String apiType, String loginId) { #### 3.2、在 Client 端调用此接口查询数据 -首先在 application.yml 中配置接口地址: - - -``` yaml -sa-token: - sso: - # sso-server 端拉取数据地址 - get-data-url: http://sa-sso-server.com:9000/sso/getData -``` - -``` properties -# sso-server 端拉取数据地址 -sa-token.sso.get-data-url=http://sa-sso-server.com:9000/sso/getData -``` - - -然后在 `SsoClientController` 中新增接口 +在 `SsoClientController` 中新增接口 ``` java // 查询我的账号信息 @RequestMapping("/sso/myInfo") @@ -242,28 +174,19 @@ public Object getFansList(Long loginId) { } ``` -**注意:使用此方案时,需要在 client 端配置 `sa-token.sso.server-url` 地址,例如:** -``` yaml -sa-token: - sso: - # sso-server 端主机地址 - server-url: http://sa-sso-server.com:9000 -``` - #### 4.3、访问测试 访问测试:[http://sa-sso-client1.com:9001/sso/myFansList](http://sa-sso-client1.com:9001/sso/myFansList) +### 5、单点注销 -### 5、无刷单点注销 +有关 SSO 单点注销的步骤,在上一章节的“无刷单点注销”部分已讲解完毕(模式二和模式三通用),所以此处就不再赘述了。 -有了单点登录就必然要有单点注销,网上给出的大多数解决方案是将注销请求重定向至SSO-Server中心,逐个通知Client端下线 - -在某些场景下,页面的跳转可能造成不太好的用户体验,Sa-Token-SSO 允许你以 `REST API` 的形式构建接口,做到页面无刷新单点注销。 +此处简单介绍一下 SSO 模式三的单点注销链路过程: 1. Client 端在校验 ticket 时,将注销回调地址发送到 Server 端。 -2. Server 端将此 Client 的注销回调地址存储到 Set 集合。 +2. Server 端将此 Client 的注销回调回调信息存储到 List 集合。 3. Client 端向 Server 端发送单点注销请求。 4. Server 端遍历Set集合,逐个通知 Client 端下线。 5. Server 端注销下线。 @@ -273,57 +196,7 @@ sa-token: -这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文档增加以下配置即可: - -#### 5.1、SSO-Client 端新增配置 - -在 `application.yml` 增加配置:`API调用秘钥` 和 `单点注销接口URL`。 - - - -``` yaml -sa-token: - sso: - # 单点注销地址 - slo-url: http://sa-sso-server.com:9000/sso/signout - sign: - # API 接口调用秘钥 - secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor -``` - -``` properties -# 单点注销地址 -sa-token.sso.slo-url=http://sa-sso-server.com:9000/sso/signout -# 接口调用秘钥 -sa-token.sign.secret-key=kQwIOrYvnXmSDkwEiFngrKidMcdrgKor -``` - - - -注意 secretkey 秘钥需要与SSO认证中心的一致 - - -#### 5.2、启动测试 -重启项目,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/), -我们主要的测试点在于 `单点注销`,正常登录即可。 - -![sso-type3-client-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type3-client-index.png 's-w-sh') - -点击 **`[注销]`** 按钮,即可单点注销成功。 - - - -![sso-type3-slo-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type3-slo-index.png 's-w-sh') - -PS:这里我们为了方便演示,使用的是超链接跳页面的形式,正式项目中使用 Ajax 调用接口即可做到无刷单点登录退出。 - -例如,我们使用 [Apifox 接口测试工具](https://www.apifox.cn/) 可以做到同样的效果: - -![sso-slo-apifox.png](https://oss.dev33.cn/sa-token/doc/sso/sso-slo-apifox.png 's-w-sh') - -测试完毕! - - +这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文档步骤集成即可。 ### 6、后记 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 index d5b65596..5401d2f8 100644 --- 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 @@ -148,14 +148,10 @@ public class SaSsoClientProcessor { // 获取对象 SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); - // ---------- SSO-Client端:单点注销 [模式二] + // 无论登录时选择的是模式二还是模式三 + // 在注销时都应该按照模式三的方法,通过 http 请求调用 sso-server 的单点注销接口来做到全端下线 if(cfg.getIsSlo() && ! cfg.getIsHttp()) { - return ssoLogoutType2(); - } - - // ---------- SSO-Client端:单点注销 [模式三] - if(cfg.getIsSlo() && cfg.getIsHttp()) { - return ssoLogoutType3(); + return ssoLogoutByMode3(); } // 默认返回 @@ -166,7 +162,7 @@ public class SaSsoClientProcessor { * SSO-Client端:单点注销 [模式二] * @return 处理结果 */ - public Object ssoLogoutType2() { + public Object ssoLogoutByMode2() { // 获取对象 SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); @@ -185,7 +181,7 @@ public class SaSsoClientProcessor { * SSO-Client端:单点注销 [模式三] * @return 处理结果 */ - public Object ssoLogoutType3() { + public Object ssoLogoutByMode3() { // 获取对象 SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); 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 index 2f12fa92..22fea638 100644 --- 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 @@ -223,7 +223,7 @@ public class SaSsoServerProcessor { // step.1 校验签名 if(ssoServerTemplate.getServerConfig().getIsCheckSign()) { - ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.loginId); + ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.client, paramName.loginId); } else { SaSsoManager.printNoCheckSignWarningByRuntime(); }