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 5acea49a..31d4d0a4 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 @@ -22,7 +22,7 @@ public class SsoClientController implements Render { @Mapping("/") public String index() { String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); - String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); + String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl(); 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/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 aefb851e..b5138e3f 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 @@ -18,7 +18,7 @@ public class SsoClientController { @RequestMapping("/") public String index() { String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); - String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); + String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl(); String str = "

Sa-Token SSO-Client 应用端

" + "

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

" + "

登录 " + 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 2fc5ecbc..537644ac 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 @@ -20,7 +20,7 @@ public class SsoClientController { // 首页 @RequestMapping("/") public String index() { - String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); + String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl(); String str = "

Sa-Token SSO-Client 应用端

" + "

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

" + "

登录 " + 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 564d0530..774e137b 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 @@ -70,18 +70,18 @@ public class SaSsoManager { } } - // 在启动时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息 + // 在启动时检测到 sa-token.sso-[server/client].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("当前配置项 sa-token.sso-[server/client].is-check-sign=false 代表跳过 SSO 参数签名校验"); System.err.println("此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); System.err.println("-----------------------------------------------------------------------------"); } - // 在运行时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息 + // 在运行时检测到 sa-token.sso-[server/client].is-check-sign=false 时,输出警告信息 public static void printNoCheckSignWarningByRuntime() { - System.err.println("警告信息:当前配置项 sa-token.sso.is-check-sign=false 已跳过参数签名校验," + + System.err.println("警告信息:当前配置项 sa-token.sso-[server/client].is-check-sign=false 已跳过参数签名校验," + "此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)"); } 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 index 6f5d08ed..bdeb5acb 100644 --- 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 @@ -21,7 +21,7 @@ import cn.dev33.satoken.util.SaFoxUtil; import java.io.Serializable; /** - * Sa-Token SSO 单点登录模块 配置类 (Client端) + * Sa-Token SSO Client 端 配置类 * * @author click33 * @since 1.30.0 @@ -36,35 +36,35 @@ public class SaSsoClientConfig implements Serializable { public String mode = ""; /** - * 当前 Client 标识 + * 当前 Client 标识(非必填,不填时代表当前应用是一个匿名应用) */ public String client; /** - * 配置 Server 端主机总地址 + * 配置 SSO Server 端主机总地址 */ public String serverUrl; /** - * 单独配置 Server 端单点登录授权地址 + * 单独配置 Server 端:单点登录授权地址 */ public String authUrl = "/sso/auth"; /** - * 单独配置 Server 端查询数据 getData 地址 + * 单独配置 Server 端:单点注销地址 */ - public String getDataUrl = "/sso/getData"; + public String signoutUrl = "/sso/signout"; /** - * 单独配置 Server 端单点注销地址 - */ - public String sloUrl = "/sso/signout"; - - /** - * 单独配置 Server 端推送消息地址 + * 单独配置 Server 端:推送消息地址 */ public String pushUrl = "/sso/pushS"; + /** + * 单独配置 Server 端:查询数据 getData 地址 + */ + public String getDataUrl = "/sso/getData"; + /** * 配置当前 Client 端的登录地址(为空时自动获取) */ @@ -75,6 +75,11 @@ public class SaSsoClientConfig implements Serializable { */ public String currSsoLogoutCall; + /** + * 是否打开模式三(此值为 true 时将使用 http 请求校验 ticket 值) + */ + public Boolean isHttp = false; + /** * 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送) */ @@ -85,18 +90,13 @@ public class SaSsoClientConfig implements Serializable { */ public Boolean regLogoutCall = false; - /** - * 是否打开模式三(此值为 true 时将使用 http 请求校验 ticket 值) - */ - public Boolean isHttp = false; - /** * API 调用签名秘钥 */ public String secretKey; /** - * 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * 是否校验参数签名(为 false 时暂时关闭参数签名校验,此为方便本地调试用的一个配置项,生产环境请务必为true) */ public Boolean isCheckSign = true; @@ -104,28 +104,28 @@ public class SaSsoClientConfig implements Serializable { // 额外添加的一些函数 /** - * @return 获取拼接url:Server 端单点登录授权地址 + * @return 获取拼接 url:Server 端单点登录授权地址 */ public String splicingAuthUrl() { return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl()); } /** - * @return 获取拼接url:Server 端查询数据 getData 地址 + * @return 获取拼接 url:Server 端查询数据 getData 地址 */ public String splicingGetDataUrl() { return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl()); } /** - * @return 获取拼接url:Server 端单点注销地址 + * @return 获取拼接 url:Server 端单点注销地址 */ - public String splicingSloUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl()); + public String splicingSignoutUrl() { + return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSignoutUrl()); } /** - * @return 获取拼接url:单独配置 Server 端推送消息地址 + * @return 获取拼接 url:单独配置 Server 端推送消息地址 */ public String splicingPushUrl() { return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl()); @@ -153,14 +153,14 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return 是否打开单点注销功能 + * @return 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送) */ public Boolean getIsSlo() { return isSlo; } /** - * @param isSlo 是否打开单点注销功能 + * @param isSlo 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送) * @return 对象自身 */ public SaSsoClientConfig setIsSlo(Boolean isSlo) { @@ -169,14 +169,14 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、拉取数据getData) + * @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求校验 ticket 值) */ public Boolean getIsHttp() { return isHttp; } /** - * @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、拉取数据getData) + * @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求校验 ticket 值) * @return 对象自身 */ public SaSsoClientConfig setIsHttp(Boolean isHttp) { @@ -185,14 +185,18 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return 当前 Client 名称标识,用于和 ticket 码的互相锁定 + * 当前 Client 标识(非必填,不填时代表当前应用是一个匿名应用) + * + * @return / */ public String getClient() { return client; } /** - * @param client 当前 Client 名称标识,用于和 ticket 码的互相锁定 + * 当前 Client 标识(非必填,不填时代表当前应用是一个匿名应用) + * + * @param client / */ public SaSsoClientConfig setClient(String client) { this.client = client; @@ -200,14 +204,14 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return 配置的 Server 端单点登录授权地址 + * @return 单独配置 Server 端:单点登录授权地址 */ public String getAuthUrl() { return authUrl; } /** - * @param authUrl 配置 Server 端单点登录授权地址 + * @param authUrl 单独配置 Server 端:单点登录授权地址 * @return 对象自身 */ public SaSsoClientConfig setAuthUrl(String authUrl) { @@ -216,14 +220,14 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return Server 端查询数据 getData 地址 + * @return 单独配置 Server 端:查询数据 getData 地址 */ public String getGetDataUrl() { return getDataUrl; } /** - * @param getDataUrl 配置 Server 端查询数据 getData 地址 + * @param getDataUrl 单独配置 Server 端:查询数据 getData 地址 * @return 对象自身 */ public SaSsoClientConfig setGetDataUrl(String getDataUrl) { @@ -232,23 +236,23 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return 配置 Server 端单点注销地址 + * @return 单独配置 Server 端:单点注销地址 */ - public String getSloUrl() { - return sloUrl; + public String getSignoutUrl() { + return signoutUrl; } /** - * @param sloUrl 配置 Server 端单点注销地址 + * @param signoutUrl 单独配置 Server 端:单点注销地址 * @return 对象自身 */ - public SaSsoClientConfig setSloUrl(String sloUrl) { - this.sloUrl = sloUrl; + public SaSsoClientConfig setSignoutUrl(String signoutUrl) { + this.signoutUrl = signoutUrl; return this; } /** - * 获取 单独配置 Server 端推送消息地址 + * 获取 单独配置 Server 端:推送消息地址 * * @return / */ @@ -257,7 +261,7 @@ public class SaSsoClientConfig implements Serializable { } /** - * 设置 单独配置 Server 端推送消息地址 + * 设置 单独配置 Server 端:推送消息地址 * * @param pushUrl / * @return 对象自身 @@ -300,14 +304,18 @@ public class SaSsoClientConfig implements Serializable { } /** - * @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 + * 配置 SSO Server 端主机总地址 + * + * @return / */ public String getServerUrl() { return serverUrl; } /** - * @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置 + * 配置 SSO Server 端主机总地址 + * + * @param serverUrl / * @return 对象自身 */ public SaSsoClientConfig setServerUrl(String serverUrl) { @@ -336,7 +344,7 @@ public class SaSsoClientConfig implements Serializable { } /** - * 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * 获取 是否校验参数签名(为 false 时暂时关闭参数签名校验,此为方便本地调试用的一个配置项,生产环境请务必为true) * * @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) */ @@ -345,7 +353,7 @@ public class SaSsoClientConfig implements Serializable { } /** - * 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) + * 设置 是否校验参数签名(为 false 时暂时关闭参数签名校验,此为方便本地调试用的一个配置项,生产环境请务必为true) * * @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true) */ @@ -381,8 +389,9 @@ public class SaSsoClientConfig implements Serializable { + ", client=" + client + ", serverUrl=" + serverUrl + ", authUrl=" + authUrl + + ", signoutUrl=" + signoutUrl + + ", pushUrl=" + pushUrl + ", getDataUrl=" + getDataUrl - + ", sloUrl=" + sloUrl + ", currSsoLogin=" + currSsoLogin + ", currSsoLogoutCall=" + currSsoLogoutCall + ", isHttp=" + isHttp diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java index b1e6a909..fefe9896 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java @@ -16,6 +16,8 @@ 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.template.SaSsoServerTemplate; import cn.dev33.satoken.util.SaFoxUtil; @@ -23,29 +25,29 @@ import java.io.Serializable; import java.util.List; /** - * Sa-Token SSO 客户端信息配置 + * Sa-Token SSO 客户端信息配置 (在 Server 端配置允许接入的 Client 信息) * * @author click33 - * @since 1.42.0 + * @since 1.43.0 */ public class SaSsoClientModel implements Serializable { private static final long serialVersionUID = -6541180061782004705L; /** - * 当前 Client 名称标识,用于和 ticket 码的互相锁定 + * Client 名称标识 */ public String client; /** - * 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + * 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket ) */ public String allowUrl = "*"; /** - * 是否打开模式三(此值为 true 时使用 http 调用方式进行消息通知) + * 是否接收推送消息 */ - public Boolean isHttp = false; + public Boolean isPush = false; /** * 是否打开单点注销功能 @@ -63,7 +65,7 @@ public class SaSsoClientModel implements Serializable { public String serverUrl; /** - * 此 Client 端推送消息的地址 + * 此 Client 端推送消息的地址 (如不配置,默认根据 serverUrl + '/sso/pushC' 进行拼接) */ public String pushUrl = "/sso/pushC"; @@ -85,8 +87,12 @@ public class SaSsoClientModel implements Serializable { * * @return / */ - public String splicingNoticeUrl() { - return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl()); + public String splicingPushUrl() { + String _pushUrl = SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl()); + if ( ! SaFoxUtil.isUrl(_pushUrl)) { + throw new SaSsoException("应用 [" + getClient() + "] 推送地址无效:" + _pushUrl).setCode(SaSsoErrorCode.CODE_30023); + } + return _pushUrl; } /** @@ -94,22 +100,22 @@ public class SaSsoClientModel implements Serializable { * * @return / */ - public boolean isValidNoticeUrl() { - return SaFoxUtil.isUrl(splicingNoticeUrl()); + public boolean isValidPushUrl() { + return SaFoxUtil.isUrl(splicingPushUrl()); } // get set /** - * @return 当前 Client 名称标识 + * @return Client 名称标识 */ public String getClient() { return client; } /** - * @param client 当前 Client 名称标识 + * @param client Client 名称标识 */ public SaSsoClientModel setClient(String client) { this.client = client; @@ -117,14 +123,14 @@ public class SaSsoClientModel implements Serializable { } /** - * @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + * @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket ) */ public String getAllowUrl() { return allowUrl; } /** - * @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) + * @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket ) * @return 对象自身 */ public SaSsoClientModel setAllowUrl(String allowUrl) { @@ -140,16 +146,16 @@ public class SaSsoClientModel implements Serializable { /** * @return isHttp 是否打开模式三 */ - public Boolean getIsHttp() { - return isHttp; + public Boolean getIsPush() { + return isPush; } /** - * @param isHttp 是否打开模式三 + * @param isPush 是否打开模式三 * @return 对象自身 */ - public SaSsoClientModel setIsHttp(Boolean isHttp) { - this.isHttp = isHttp; + public SaSsoClientModel setIsPush(Boolean isPush) { + this.isPush = isPush; return this; } @@ -210,16 +216,16 @@ public class SaSsoClientModel implements Serializable { } /** - * 获取 此 Client 端推送消息的地址 + * 获取 此 Client 端推送消息的地址 (如不配置,默认根据 serverUrl + '/sso/pushC' 进行拼接) * - * @return noticeUrl 此 Client 端推送消息的地址 + * @return / */ public String getPushUrl() { return this.pushUrl; } /** - * 设置 此 Client 端推送消息的地址 + * 设置 此 Client 端推送消息的地址 (如不配置,默认根据 serverUrl + '/sso/pushC' 进行拼接) * * @param pushUrl 此 Client 端推送消息的地址 * @return 对象自身 @@ -235,7 +241,7 @@ public class SaSsoClientModel implements Serializable { + "client=" + client + ", allowUrl=" + allowUrl + ", isSlo=" + isSlo - + ", isHttp=" + isHttp + + ", isPush=" + isPush + ", secretKey=" + secretKey + ", serverUrl=" + serverUrl + ", pushUrl=" + pushUrl 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 index a84793fb..1ad410a9 100644 --- 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 @@ -25,7 +25,7 @@ import java.util.List; import java.util.Map; /** - * Sa-Token SSO 单点登录模块 配置类 (Server端) + * Sa-Token SSO Server 端 配置类 * * @author click33 * @since 1.38.0 @@ -43,7 +43,7 @@ public class SaSsoServerConfig implements Serializable { public String mode = ""; /** - * Ticket有效期 (单位: 秒) + * ticket 有效期 (单位: 秒) */ public long ticketTimeout = 60 * 5; @@ -53,7 +53,7 @@ public class SaSsoServerConfig implements Serializable { public String homeRoute; /** - * 是否打开单点注销功能 + * 是否打开单点注销功能 (为 true 时接收 client 端推送的单点注销消息) */ public Boolean isSlo = true; @@ -98,7 +98,7 @@ public class SaSsoServerConfig implements Serializable { // 额外方法 /** - * 以数组形式写入允许的授权回调地址 + * 以数组形式写入允许的授权回调地址 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用) * @param url 所有集合 * @return 对象自身 */ @@ -139,14 +139,14 @@ public class SaSsoServerConfig implements Serializable { } /** - * @return Ticket有效期 (单位: 秒) + * @return ticket 有效期 (单位: 秒) */ public long getTicketTimeout() { return ticketTimeout; } /** - * @param ticketTimeout Ticket有效期 (单位: 秒) + * @param ticketTimeout ticket 有效期 (单位: 秒) * @return 对象自身 */ public SaSsoServerConfig setTicketTimeout(long ticketTimeout) { @@ -192,14 +192,14 @@ public class SaSsoServerConfig implements Serializable { } /** - * @return 是否打开单点注销功能 + * @return 是否打开单点注销功能 (为 true 时接收 client 端推送的单点注销消息) */ public Boolean getIsSlo() { return isSlo; } /** - * @param isSlo 是否打开单点注销功能 + * @param isSlo 是否打开单点注销功能 (为 true 时接收 client 端推送的单点注销消息) * @return 对象自身 */ public SaSsoServerConfig setIsSlo(Boolean isSlo) { diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/error/SaSsoErrorCode.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/error/SaSsoErrorCode.java index e1bb6fe5..88666a86 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/error/SaSsoErrorCode.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/error/SaSsoErrorCode.java @@ -77,4 +77,7 @@ public interface SaSsoErrorCode { /** 无效的消息推送地址 */ int CODE_30023 = 30023; + /** SSO 消息里缺少指定的参数 */ + int CODE_30024 = 30024; + } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/CheckTicketAppendDataFunction.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/CheckTicketAppendDataFunction.java index b99bb802..5405a11a 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/CheckTicketAppendDataFunction.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/function/CheckTicketAppendDataFunction.java @@ -20,7 +20,7 @@ import cn.dev33.satoken.util.SaResult; import java.util.function.BiFunction; /** - * 函数式接口:SSO-Server端:在校验 ticket 后,给 sso-client 端追加返回信息的函数 + * 函数式接口:sso-server 端:在校验 ticket 后,给 sso-client 端追加返回信息的函数 * *

参数:loginId, SaResult 响应参数对象

*

返回:SaResult 响应参数对象

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 index b5dd7566..7e854fa6 100644 --- 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 @@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function; import java.util.function.BiFunction; /** - * 函数式接口:SSO-Server端:登录处理函数 + * 函数式接口:sso-server 端:登录处理函数 * *

参数:账号、密码

*

返回:登录结果

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 index 644a0a94..3f8a52cb 100644 --- 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 @@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function; import java.util.function.Supplier; /** - * 函数式接口:SSO-Server端:未登录时返回的View + * 函数式接口:sso-server 端:未登录时返回的 View * *

参数:无

*

返回:未登录时的 View 视图

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 index 874e8a7e..8e124bc7 100644 --- 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 @@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function; import java.util.function.Function; /** - * 函数式接口:发送Http请求的处理函数 + * 函数式接口:发送 Http 请求的处理函数 * *

参数:要请求的url

*

返回:请求结果

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 index 739d4658..f175d449 100644 --- 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 @@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function; import cn.dev33.satoken.sso.model.SaCheckTicketResult; /** - * 函数式接口:SSO-Client端:自定义校验 ticket 返回值的处理逻辑 (每次从认证中心获取校验 ticket 的结果后调用) + * 函数式接口:sso-client 端:自定义校验 ticket 返回值的处理逻辑 (每次从认证中心获取校验 ticket 的结果后调用) * *

参数:loginId, back

*

返回:返回给前端的值

diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessage.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessage.java index 41098682..2fde8254 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessage.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessage.java @@ -26,7 +26,7 @@ import java.util.LinkedHashMap; import java.util.Map; /** - * Sa-Token SSO 消息 Model + * SSO 消息 Model * * @author click33 * @since 1.43.0 @@ -108,4 +108,19 @@ public class SaSsoMessage extends LinkedHashMap implements SaSet return this; } + // ----------- + + /** + * 获取一个值 (此值必须存在,否则抛出异常 ) + * @param key 键 + * @return 参数值 + */ + public Object getValueNotNull(String key) { + Object value = get(key); + if(SaFoxUtil.isEmpty(value)) { + throw new SaSsoException("缺少参数:" + key).setCode(SaSsoErrorCode.CODE_30024); + } + return value; + } + } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessageHolder.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessageHolder.java index 0b68cde7..56ab9eca 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessageHolder.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/SaSsoMessageHolder.java @@ -25,7 +25,7 @@ import java.util.LinkedHashMap; import java.util.Map; /** - * Sa-Token SSO 消息处理器持有者 + * SSO 消息处理器 - 持有器 * * @author click33 * @since 1.43.0 @@ -33,7 +33,7 @@ import java.util.Map; public class SaSsoMessageHolder { /** - * 所有消息处理器的集合 + * 所有已注册的消息处理器 */ public final Map messageHandleMap = new LinkedHashMap<>(); @@ -61,6 +61,7 @@ public class SaSsoMessageHolder { * 添加指定类型的消息处理器 * * @param handle / + * @return 对象自身 */ public SaSsoMessageHolder addHandle(SaSsoMessageHandle handle) { messageHandleMap.put(handle.getHandlerType(), handle); @@ -81,7 +82,7 @@ public class SaSsoMessageHolder { * * @param ssoTemplate / * @param message / - * @return / + * @return 处理结果 */ public Object handleMessage(SaSsoTemplate ssoTemplate, SaSsoMessage message) { SaSsoMessageHandle handle = messageHandleMap.get(message.getType()); diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/SaSsoMessageHandle.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/SaSsoMessageHandle.java index 32944139..651ad7cf 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/SaSsoMessageHandle.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/SaSsoMessageHandle.java @@ -20,7 +20,7 @@ import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.template.SaSsoTemplate; /** - * Sa-Token SSO 消息 处理器 + * SSO 消息处理器 - 父接口 * * @author click33 * @since 1.43.0 @@ -35,7 +35,7 @@ public interface SaSsoMessageHandle { String getHandlerType(); /** - * 执行方法 + * 具体要执行的处理方法 * * @param ssoTemplate / * @param message / diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/client/SaSsoMessageLogoutCallHandle.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/client/SaSsoMessageLogoutCallHandle.java index 3e1f4d91..5a8ecfb3 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/client/SaSsoMessageLogoutCallHandle.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/client/SaSsoMessageLogoutCallHandle.java @@ -29,7 +29,7 @@ import cn.dev33.satoken.stp.parameter.SaLogoutParameter; import cn.dev33.satoken.util.SaResult; /** - * Sa-Token SSO 消息 处理器 - sso-client 端:处理 单点注销回调 的请求 + * SSO 消息处理器 - sso-client 端:处理 单点注销回调 的请求 * * @author click33 * @since 1.43.0 @@ -53,27 +53,28 @@ public class SaSsoMessageLogoutCallHandle implements SaSsoMessageHandle { * @return / */ public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { + + // 1、获取对象 SaSsoClientTemplate ssoClientTemplate = (SaSsoClientTemplate) ssoTemplate; + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + ParamName paramName = ssoClientTemplate.paramName; + + // 2、判断当前应用是否开启单点注销功能 if( ! ssoClientTemplate.getClientConfig().getIsSlo()) { return SaResult.error("当前 sso-client 端未开启单点注销功能"); } - // 获取对象 - SaRequest req = SaHolder.getRequest(); - StpLogic stpLogic = ssoClientTemplate.getStpLogic(); - ParamName paramName = ssoClientTemplate.paramName; - - // 获取参数 - Object loginId = req.getParamNotNull(paramName.loginId); + // 3、获取参数 + Object loginId = message.getValueNotNull(paramName.loginId); loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId); String deviceId = message.getString(paramName.deviceId); - // 注销当前应用端会话 + // 4、注销当前应用端会话 stpLogic.logout(loginId, new SaLogoutParameter() .setDeviceId(deviceId) ); - // 响应 + // 5、响应 return SaResult.ok("单点注销回调成功"); } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageCheckTicketHandle.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageCheckTicketHandle.java index cc523dcc..6e2a64db 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageCheckTicketHandle.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageCheckTicketHandle.java @@ -16,9 +16,6 @@ package cn.dev33.satoken.sso.message.handle.server; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaRequest; -import cn.dev33.satoken.sso.config.SaSsoServerConfig; import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle; import cn.dev33.satoken.sso.model.TicketModel; @@ -30,7 +27,7 @@ import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.util.SaResult; /** - * Sa-Token SSO 消息 处理器 - sso-server 端:处理校验 ticket 的请求 + * SSO 消息处理器 - sso-server 端:处理校验 ticket 的请求 * * @author click33 * @since 1.43.0 @@ -54,38 +51,23 @@ public class SaSsoMessageCheckTicketHandle implements SaSsoMessageHandle { * @return / */ public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { + + // 1、获取对象 SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate; ParamName paramName = ssoServerTemplate.paramName; - - // 1、获取参数 - SaRequest req = SaHolder.getRequest(); - SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig(); StpLogic stpLogic = ssoServerTemplate.getStpLogic(); - String client = req.getParam(paramName.client); - String ticket = req.getParamNotNull(paramName.ticket); - String sloCallback = req.getParam(paramName.ssoLogoutCall); + String client = message.getString(paramName.client); + String ticket = message.getValueNotNull(paramName.ticket).toString(); + String sloCallback = message.getString(paramName.ssoLogoutCall); - - // 2、校验提供的client是否为非法字符 - if(SaSsoConsts.CLIENT_WILDCARD.equals(client)) { - return SaResult.error("无效 client 标识:" + client); - } - - // 3、校验签名 -// if(ssoServerConfig.getIsCheckSign()) { -// ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.client, paramName.ticket, paramName.ssoLogoutCall); -// } else { -// SaSsoManager.printNoCheckSignWarningByRuntime(); -// } - - // 4、校验ticket,获取 loginId + // 2、校验ticket,获取 loginId TicketModel ticketModel = ssoServerTemplate.checkTicketParamAndDelete(ticket, client); Object loginId = ticketModel.getLoginId(); - // 5、注册此客户端的单点注销回调URL + // 3、注册此客户端的登录信息 ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback); - // 6、给 client 端响应结果 + // 4、给 client 端响应结果 SaResult result = SaResult.ok(); result.setData(loginId); // 兼容历史版本 result.set(paramName.loginId, loginId); @@ -93,7 +75,6 @@ public class SaSsoMessageCheckTicketHandle implements SaSsoMessageHandle { result.set(paramName.deviceId, stpLogic.getLoginDeviceIdByToken(ticketModel.getTokenValue())); result.set(paramName.remainTokenTimeout, stpLogic.getTokenTimeout(ticketModel.getTokenValue())); result.set(paramName.remainSessionTimeout, stpLogic.getSessionTimeoutByLoginId(loginId)); - result = ssoServerTemplate.strategy.checkTicketAppendData.apply(loginId, result); return result; } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageSignoutHandle.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageSignoutHandle.java index 728b007f..b470d7c4 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageSignoutHandle.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/message/handle/server/SaSsoMessageSignoutHandle.java @@ -16,8 +16,6 @@ package cn.dev33.satoken.sso.message.handle.server; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle; import cn.dev33.satoken.sso.name.ParamName; @@ -28,7 +26,7 @@ import cn.dev33.satoken.stp.parameter.SaLogoutParameter; import cn.dev33.satoken.util.SaResult; /** - * Sa-Token SSO 消息 处理器 - sso-server 端:处理 单点注销 的请求 + * SSO 消息处理器 - sso-server 端:处理 单点注销 的请求 * * @author click33 * @since 1.43.0 @@ -52,24 +50,25 @@ public class SaSsoMessageSignoutHandle implements SaSsoMessageHandle { * @return / */ public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { + + // 1、获取对象 SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate; ParamName paramName = ssoServerTemplate.paramName; + // 2、判断当前是否开启了全局单点注销功能 if( ! ssoServerTemplate.getServerConfig().getIsSlo()) { return SaResult.error("当前 sso-server 端未开启单点注销功能"); } - // 获取参数 - SaRequest req = SaHolder.getRequest(); - String loginId = req.getParam(paramName.loginId); - String deviceId = req.getParam(paramName.deviceId); + // 3、获取参数 + Object loginId = message.get(paramName.loginId); + String deviceId = message.getString(paramName.deviceId); - // step.2 单点注销 - SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter() - .setDeviceId(deviceId); + // 4、单点注销 + SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter().setDeviceId(deviceId); ssoServerTemplate.ssoLogout(loginId, logoutParameter); - // 响应 + // 5、响应 return SaResult.ok(); } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaCheckTicketResult.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaCheckTicketResult.java index a6880952..5fe847e3 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaCheckTicketResult.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaCheckTicketResult.java @@ -47,12 +47,9 @@ public class SaCheckTicketResult implements Serializable { /** 此账号在认证中心的 loginId */ public Object centerId; - /** 从 sso-server 返回的所有参数 */ + /** 从 sso-server 返回的原生所有参数 */ public SaResult result; - public SaCheckTicketResult() { - } - @Override public String toString() { return "SaCheckTicketResult{" + diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientInfo.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientInfo.java index 05748f29..76397fe0 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientInfo.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/SaSsoClientInfo.java @@ -21,7 +21,7 @@ import cn.dev33.satoken.sso.util.SaSsoConsts; import java.io.Serializable; /** - * Sa-Token SSO 应用信息 + * Sa-Token SSO 应用信息(注册在 SaSession 上的已登录应用信息列表) * * @author click33 * @since 1.38.0 @@ -45,13 +45,8 @@ public class SaSsoClientInfo implements Serializable { */ public String client; -// /** -// * 此次登录 token 值 -// */ -// public String tokenValue; - /** - * 单点注销回调url + * 单点注销回调 url */ public String sloCallbackUrl; @@ -80,6 +75,8 @@ public class SaSsoClientInfo implements Serializable { } + // get set + /** * 获取 此 client 登录模式(1=模式一,2=模式二,3=模式三) * @@ -120,26 +117,6 @@ public class SaSsoClientInfo implements Serializable { return this; } -// /** -// * 获取 此次登录 token 值 -// * -// * @return tokenValue 此次登录 token 值 -// */ -// public String getTokenValue() { -// return this.tokenValue; -// } -// -// /** -// * 设置 此次登录 token 值 -// * -// * @param tokenValue 此次登录 token 值 -// * @return / -// */ -// public SaSsoClientModel setTokenValue(String tokenValue) { -// this.tokenValue = tokenValue; -// return this; -// } - /** * 获取 单点注销回调url * @@ -205,7 +182,6 @@ public class SaSsoClientInfo implements Serializable { return "SaSsoClientModel{" + "mode=" + mode + ", client='" + client + '\'' + -// ", tokenValue='" + tokenValue + '\'' + ", sloCallbackUrl='" + sloCallbackUrl + '\'' + ", regTime=" + regTime + ", index=" + index + diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java index c766e6f4..4d4218e6 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/model/TicketModel.java @@ -37,11 +37,6 @@ public class TicketModel implements Serializable { */ public String client; -// /** -// * 设备 id -// */ -// public String deviceId; - /** * 对应 loginId */ diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ApiName.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ApiName.java index 5906c742..06d1b8ed 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ApiName.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ApiName.java @@ -32,7 +32,7 @@ public class ApiName { /** SSO-Server端:校验ticket 获取账号id */ public String ssoCheckTicket = "/sso/checkTicket"; - /** SSO-Server端:接受推送消息 */ + /** SSO-Server端:接收推送消息 */ public String ssoPushS = "/sso/pushS"; /** SSO-Server端:获取userinfo */ @@ -53,11 +53,11 @@ public class ApiName { /** SSO-Client端:单点注销的回调 */ public String ssoLogoutCall = "/sso/logoutCall"; - /** SSO-Client端:接受推送消息 */ + /** SSO-Client端:接收推送消息 */ public String ssoPushC = "/sso/pushC"; /** - * 批量修改path,新增固定前缀 + * 批量修改 path,新增固定前缀 * @param prefix 示例值:/sso-user、/sso-admin * @return 对象自身 */ @@ -77,7 +77,7 @@ public class ApiName { } /** - * 批量修改path,替换掉 /sso 固定前缀 + * 批量修改 path,替换掉 /sso 固定前缀 * @param prefix 示例值:/sso-user、/sso-admin * @return 对象自身 */ @@ -106,6 +106,7 @@ public class ApiName { ", ssoPushS='" + ssoPushS + '\'' + ", ssoUserinfo='" + ssoUserinfo + '\'' + ", ssoSignout='" + ssoSignout + '\'' + + ", ssoIsLogin='" + ssoIsLogin + '\'' + ", ssoLogin='" + ssoLogin + '\'' + ", ssoLogout='" + ssoLogout + '\'' + ", ssoLogoutCall='" + ssoLogoutCall + '\'' + diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ParamName.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ParamName.java index 2687677b..5c338872 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ParamName.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/name/ParamName.java @@ -23,40 +23,40 @@ package cn.dev33.satoken.sso.name; */ public class ParamName { - /** redirect参数名称 */ + /** redirect 参数名称 */ public String redirect = "redirect"; - /** ticket参数名称 */ + /** ticket 参数名称 */ public String ticket = "ticket"; - /** back参数名称 */ + /** back 参数名称 */ public String back = "back"; - /** mode参数名称 */ + /** mode 参数名称 */ public String mode = "mode"; - /** loginId参数名称 */ + /** 账号 id */ public String loginId = "loginId"; - /** client参数名称 */ + /** client 应用标识 */ public String client = "client"; - /** tokenName 参数 */ + /** token 名称 */ public String tokenName = "tokenName"; - /** tokenValue 参数 */ + /** token 值 */ public String tokenValue = "tokenValue"; - /** deviceId 参数名称 */ + /** 设备 id */ public String deviceId = "deviceId"; - /** secretkey参数名称 */ + /** 接口参数签名秘钥 */ public String secretkey = "secretkey"; - /** Client端单点注销时-回调URL 参数名称 */ + /** Client 端单点注销时 - 回调 URL 参数名称 */ public String ssoLogoutCall = "ssoLogoutCall"; - /** 是否为超过 maxRegClient 的自动注销 */ + /** 是否为超过 maxRegClient 触发的自动注销 */ public String autoLogout = "autoLogout"; public String name = "name"; @@ -72,13 +72,8 @@ public class ParamName { /** token 剩余有效期 参数名称 */ public String remainTokenTimeout = "remainTokenTimeout"; - /** singleDeviceIdLogout 参数 */ + /** 是否单设备 id 注销 */ public String singleDeviceIdLogout = "singleDeviceIdLogout"; - public String isLogin = "isLogin"; - public String authUrl = "authUrl"; - public String redirectUrl = "redirectUrl"; - public String currSsoLoginUrl = "currSsoLoginUrl"; - } 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 8e818449..8134c12d 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 @@ -70,22 +70,22 @@ public class SaSsoClientProcessor { // ------------------ 路由分发 ------------------ - // ---------- SSO-Client端:登录地址 + // sso-client:登录地址 if(req.isPath(apiName.ssoLogin)) { return ssoLogin(); } - // ---------- SSO-Client端:单点注销 + // sso-client:单点注销 if(req.isPath(apiName.ssoLogout)) { return ssoLogout(); } - // ---------- SSO-Client端:接收消息推送 + // sso-client:接收消息推送 if(req.isPath(apiName.ssoPushC)) { return ssoPushC(); } - // ---------- SSO-Client端:单点注销的回调 [模式三] + // sso-client:单点注销的回调 if(req.isPath(apiName.ssoLogoutCall) && cfg.getRegLogoutCall()) { return ssoLogoutCall(); } @@ -101,55 +101,18 @@ public class SaSsoClientProcessor { 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); /* * 此时有两种情况: - * 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心 - * 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录 + * 情况1:ticket 无值,说明此请求是 sso-client 端访问,需要重定向至 sso-server 认证中心 + * 情况2:ticket 有值,说明此请求从 sso-server 认证中心重定向而来,需要根据 ticket 进行登录 */ if(ticket == null) { - // 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回 - if(stpLogic.isLogin()) { - return res.redirect(back); - } - - // 获取当前项目的 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); + return _goServerAuth(); } else { - // 1、校验ticket,获取 loginId - SaCheckTicketResult ctr = checkTicket(ticket, apiName.ssoLogin); - ctr.centerId = ctr.loginId; - ctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId); - - // 2、如果开发者自定义了ticket结果值处理函数,则使用自定义的函数 - if(ssoClientTemplate.strategy.ticketResultHandle != null) { - return ssoClientTemplate.strategy.ticketResultHandle.run(ctr, back); - } - - // 3、登录并重定向至back地址 - stpLogic.login(ctr.loginId, new SaLoginParameter() - .setTimeout(ctr.remainTokenTimeout) - .setDeviceId(ctr.deviceId) - ); - return res.redirect(back); + return _loginByTicket(); } } @@ -162,9 +125,10 @@ public class SaSsoClientProcessor { SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); // 无论登录时选择的是模式二还是模式三 - // 在注销时都应该按照模式三的方法,通过 http 请求调用 sso-server 的单点注销接口来做到全端下线 + // 在注销时都应该按照模式三的方法,通过 http 请求调用 sso-server 的单点注销接口来做到全端下线 + // 如果按照模式二的方法注销,则会导致按照模式三登录的应用无法参与到单点注销环路中来 if(cfg.getIsSlo()) { - return ssoLogoutByMode3(); + return _ssoLogoutByMode3(); } // 默认返回 @@ -192,63 +156,6 @@ public class SaSsoClientProcessor { return ssoClientTemplate.handleMessage(message); } - /** - * SSO-Client端:单点注销 [模式二] - * @return 处理结果 - */ - public Object ssoLogoutByMode2() { - // 获取对象 - 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 ssoLogoutByMode3() { - // 获取对象 - SaRequest req = SaHolder.getRequest(); - SaResponse res = SaHolder.getResponse(); - StpLogic stpLogic = ssoClientTemplate.getStpLogic(); - boolean singleDeviceIdLogout = req.isParam(ssoClientTemplate.paramName.singleDeviceIdLogout, "true"); - - // 如果未登录,则无需注销 - if( ! stpLogic.isLogin()) { - return ssoLogoutBack(req, res); - } - - // 调用 sso-server 认证中心单点注销API - SaLogoutParameter logoutParameter = stpLogic.createSaLogoutParameter(); - if(singleDeviceIdLogout) { - logoutParameter.setDeviceId(stpLogic.getLoginDeviceId()); - } - Object centerId = ssoClientTemplate.strategy.convertLoginIdToCenterId.run(stpLogic.getLoginId()); - SaSsoMessage message = ssoClientTemplate.buildSloMessage(centerId, logoutParameter); - SaResult result = ssoClientTemplate.pushMessageAsSaResult(message); - - // 校验响应状态码 - if(result.getCode() != null && 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 处理结果 @@ -265,8 +172,6 @@ public class SaSsoClientProcessor { Object loginId = req.getParamNotNull(paramName.loginId); loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId); String deviceId = req.getParam(paramName.deviceId); - // String client = req.getParam(paramName.client); - // String autoLogout = req.getParam(paramName.autoLogout); // 校验参数签名 if(ssoConfig.getIsCheckSign()) { @@ -283,7 +188,109 @@ public class SaSsoClientProcessor { return SaResult.ok("单点注销回调成功"); } - // 工具方法 + // 次级方法 + + /** + * 跳转去 sso-server 认证中心 + * @return / + */ + public Object _goServerAuth() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + ParamName paramName = ssoClientTemplate.paramName; + + // 获取参数 + String back = req.getParam(paramName.back, "/"); + + // 如果当前 sso-client 端已经登录,则无需访问 SSO 认证中心,可以直接返回 + if(stpLogic.isLogin()) { + return res.redirect(back); + } + + // 获取当前项目的 sso 登录中转页地址,形如:http://sso-client.com/sso/login + // 全局配置了就是用全局的,否则使用当前请求的地址 + String currSsoLoginUrl = cfg.getCurrSsoLogin(); + if(SaFoxUtil.isEmpty(currSsoLoginUrl)) { + currSsoLoginUrl = SaHolder.getRequest().getUrl(); + } + // 构建最终授权地址 url,形如:http://sso-server.com/sso/auth?redirectUrl=http://sso-client.com/sso/login?back=http://sso-client.com + String serverAuthUrl = ssoClientTemplate.buildServerAuthUrl(currSsoLoginUrl, back); + return res.redirect(serverAuthUrl); + } + + /** + * 根据认证中心回传的 ticket 进行登录 + * @return / + */ + public Object _loginByTicket() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + ParamName paramName = ssoClientTemplate.paramName; + ApiName apiName = ssoClientTemplate.apiName; + + // 获取参数 + String back = req.getParam(paramName.back, "/"); + String ticket = req.getParam(paramName.ticket); + + // 1、校验 ticket,获取 loginId 等数据 + SaCheckTicketResult ctr = checkTicket(ticket, apiName.ssoLogin); + ctr.centerId = ctr.loginId; + ctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId); + + // 2、如果开发者自定义了 ticket 结果值处理函数,则使用自定义的函数 + if(ssoClientTemplate.strategy.ticketResultHandle != null) { + return ssoClientTemplate.strategy.ticketResultHandle.run(ctr, back); + } + + // 3、登录并重定向至back地址 + stpLogic.login(ctr.loginId, new SaLoginParameter() + .setTimeout(ctr.remainTokenTimeout) + .setDeviceId(ctr.deviceId) + ); + return res.redirect(back); + } + + /** + * SSO-Client端:单点注销 [模式三] + * @return 处理结果 + */ + public Object _ssoLogoutByMode3() { + // 获取对象 + SaRequest req = SaHolder.getRequest(); + SaResponse res = SaHolder.getResponse(); + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + boolean singleDeviceIdLogout = req.isParam(ssoClientTemplate.paramName.singleDeviceIdLogout, "true"); + + // 如果未登录,则无需注销 + if( ! stpLogic.isLogin()) { + return _ssoLogoutBack(req, res); + } + + // 向 sso-server 认证中心推送消息:单点注销 + SaLogoutParameter logoutParameter = stpLogic.createSaLogoutParameter(); + if(singleDeviceIdLogout) { + logoutParameter.setDeviceId(stpLogic.getLoginDeviceId()); + } + Object centerId = ssoClientTemplate.strategy.convertLoginIdToCenterId.run(stpLogic.getLoginId()); + SaSsoMessage message = ssoClientTemplate.buildSloMessage(centerId, logoutParameter); + SaResult result = ssoClientTemplate.pushMessageAsSaResult(message); + + // 如果 sso-server 响应的状态码非200,代表业务失败,将回应的 msg 字段作为异常抛出 + if(result.getCode() == null || SaResult.CODE_SUCCESS != result.getCode()) { + throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30006); + } + + // 极端场景下,sso-server 中心的单点注销可能并不会通知到当前 client 端,所以这里需要再补一刀 + if(stpLogic.isLogin()) { + stpLogic.logout(logoutParameter); + } + return _ssoLogoutBack(req, res); + } /** * 封装:校验ticket,取出loginId,如果 ticket 无效则抛出异常 (适用于模式二或模式三) @@ -294,70 +301,90 @@ public class SaSsoClientProcessor { */ public SaCheckTicketResult checkTicket(String ticket, String currUri) { SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); + + // 两种模式: + // isHttp=true:模式三,使用 http 请求从认证中心校验ticket + // isHttp=false:模式二,直连 redis 中校验 ticket + if(cfg.getIsHttp()) { + return _checkTicketByHttp(ticket, currUri); + } else { + return _checkTicketByRedis(ticket); + } + } + + /** + * 校验 ticket,http 请求方式 + * @param ticket / + * @param currUri / + * @return / + */ + public SaCheckTicketResult _checkTicketByHttp(String ticket, String currUri) { + SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); ApiName apiName = ssoClientTemplate.apiName; ParamName paramName = ssoClientTemplate.paramName; - // --------- 两种模式 - if(cfg.getIsHttp()) { - // q1、使用模式三:使用 http 请求从认证中心校验ticket - - // 计算当前 sso-client 的单点注销回调地址 - String ssoLogoutCall = null; - if(cfg.getRegLogoutCall()) { - // 如果配置了回调地址,就使用配置的值: - if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) { - ssoLogoutCall = cfg.getCurrSsoLogoutCall(); - } - // 如果提供了当前 uri,则根据此值来计算: - else if(SaFoxUtil.isNotEmpty(currUri)) { - ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall); - } - // 否则视为不注册单点注销回调地址 - else { - } + // 计算当前 sso-client 的单点注销回调地址 + String ssoLogoutCall = null; + if(cfg.getRegLogoutCall()) { + // 如果配置了回调地址,就使用配置的值: + if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) { + ssoLogoutCall = cfg.getCurrSsoLogoutCall(); } - - // 发起请求 - SaSsoMessage message = ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCall); - SaResult result = ssoClientTemplate.pushMessageAsSaResult(message); - - // 校验 - if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) { - - SaCheckTicketResult ctr = new SaCheckTicketResult(); - ctr.loginId = result.get(paramName.loginId); - ctr.tokenValue = result.get(paramName.tokenValue, String.class); - ctr.deviceId = result.get(paramName.deviceId, String.class); - ctr.remainTokenTimeout = result.get(paramName.remainTokenTimeout, Long.class); - ctr.remainSessionTimeout = result.get(paramName.remainSessionTimeout, Long.class); - ctr.result = result; - - return ctr; - } else { - // 将 sso-server 回应的消息作为异常抛出 - throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005); + // 如果提供了当前 uri,则根据此值来计算: + else if(SaFoxUtil.isNotEmpty(currUri)) { + ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall); + } + // 否则视为不注册单点注销回调地址 + else { } - } else { - // q2、使用模式二:直连Redis校验ticket - // 注意此处调用了 SaSsoServerProcessor 处理器里的方法, - // 这意味着如果你的 sso-server 端重写了 SaSsoServerProcessor 里的部分方法, - // 而在当前 sso-client 没有按照相应格式重写 SaSsoClientProcessor 里的方法, - // 可能会导致调用失败(注意是可能,而非一定), - // 解决方案为:在当前 sso-client 端也按照 sso-server 端的格式重写 SaSsoClientProcessor 里的方法 - - StpLogic stpLogic = ssoClientTemplate.getStpLogic(); - TicketModel ticketModel = SaSsoServerProcessor.instance.ssoServerTemplate.checkTicketParamAndDelete(ticket, ssoClientTemplate.getClient()); - - SaCheckTicketResult ctr = new SaCheckTicketResult(); - ctr.loginId = ticketModel.getLoginId(); - ctr.tokenValue = ticketModel.getTokenValue(); - ctr.deviceId = stpLogic.getLoginDeviceIdByToken(ticketModel.getTokenValue()); - ctr.remainTokenTimeout = stpLogic.getTokenTimeout(ticketModel.getTokenValue()); - ctr.remainSessionTimeout = stpLogic.getSessionTimeoutByLoginId(ticketModel.getLoginId()); - ctr.result = null; - - return ctr; } + + // 发起请求 + SaSsoMessage message = ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCall); + SaResult result = ssoClientTemplate.pushMessageAsSaResult(message); + + // 如果 sso-server 响应的状态码非200,代表业务失败,将回应的 msg 字段作为异常抛出 + if(result.getCode() == null || result.getCode() != SaResult.CODE_SUCCESS) { + throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005); + } + + // 构建返回结果 + SaCheckTicketResult ctr = new SaCheckTicketResult(); + ctr.loginId = result.get(paramName.loginId); + ctr.tokenValue = result.get(paramName.tokenValue, String.class); + ctr.deviceId = result.get(paramName.deviceId, String.class); + ctr.remainTokenTimeout = result.get(paramName.remainTokenTimeout, Long.class); + ctr.remainSessionTimeout = result.get(paramName.remainSessionTimeout, Long.class); + ctr.result = result; + + return ctr; + } + + /** + * 校验 ticket,直连 redis 方式 + * @param ticket / + * @return / + */ + public SaCheckTicketResult _checkTicketByRedis(String ticket) { + // 直连 redis 校验 ticket + // 注意此处调用了 SaSsoServerProcessor 处理器里的方法, + // 这意味着如果你的 sso-server 端重写了 SaSsoServerProcessor 里的部分方法, + // 而在当前 sso-client 没有按照相应格式重写 SaSsoClientProcessor 里的方法, + // 可能会导致调用失败(注意是可能,而非一定,主要取决于你是否改变了数据读写格式), + // 解决方案为:在当前 sso-client 端也按照 sso-server 端的格式重写 SaSsoClientProcessor 里的方法 + + StpLogic stpLogic = ssoClientTemplate.getStpLogic(); + TicketModel ticketModel = SaSsoServerProcessor.instance.ssoServerTemplate.checkTicketParamAndDelete(ticket, ssoClientTemplate.getClient()); + + SaCheckTicketResult ctr = new SaCheckTicketResult(); + ctr.loginId = ticketModel.getLoginId(); + ctr.tokenValue = ticketModel.getTokenValue(); + ctr.deviceId = stpLogic.getLoginDeviceIdByToken(ticketModel.getTokenValue()); + ctr.remainTokenTimeout = stpLogic.getTokenTimeout(ticketModel.getTokenValue()); + ctr.remainSessionTimeout = stpLogic.getSessionTimeoutByLoginId(ticketModel.getLoginId()); + ctr.result = null; + + return ctr; } /** @@ -366,10 +393,8 @@ public class SaSsoClientProcessor { * @param res SaResponse对象 * @return 返回结果 */ - public Object ssoLogoutBack(SaRequest req, SaResponse res) { + 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 index 5aff4582..ba967566 100644 --- 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 @@ -17,8 +17,8 @@ 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.sso.util.SaSsoConsts; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; 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 3d2e4472..3d180a2e 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 @@ -57,13 +57,13 @@ public class SaSsoServerProcessor { /** * 分发 Server 端所有请求 + * * @return 处理结果 */ public Object dister() { // 获取对象 SaRequest req = SaHolder.getRequest(); - SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); ApiName apiName = ssoServerTemplate.apiName; // ------------------ 路由分发 ------------------ @@ -104,36 +104,44 @@ public class SaSsoServerProcessor { StpLogic stpLogic = ssoServerTemplate.getStpLogic(); ParamName paramName = ssoServerTemplate.paramName; - // ---------- 此处有两种情况分开处理: - // ---- 情况1:在SSO认证中心尚未登录,需要先去登录 + // 两种情况: + // 情况1:在 SSO 认证中心尚未登录,需要显示登录视图,去登录 + // 情况2:在 SSO 认证中心已经登录,需要重定向回 Client 端 + + // 情况1,显示登录视图 if( ! stpLogic.isLogin()) { return ssoServerTemplate.strategy.notLoginView.get(); } - // ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式: + + // 情况2,开始跳转 String mode = req.getParam(paramName.mode, SaSsoConsts.MODE_TICKET); String redirect = req.getParam(paramName.redirect); String client = req.getParam(paramName.client); - // 若 redirect 为空,则选择 homeRoute,若 homeRoute 也为空,则抛出异常 - if(SaFoxUtil.isEmpty(redirect)) { - if(SaFoxUtil.isEmpty(cfg.getHomeRoute())) { - throw new SaSsoException("未指定 redirect 参数,也未配置 homeRoute 路由,无法完成重定向操作").setCode(SaSsoErrorCode.CODE_30014); - } - ssoServerTemplate.strategy.jumpToRedirectUrlNotice.run(cfg.getHomeRoute()); - return res.redirect(cfg.getHomeRoute()); - } - + // 构建最终重定向地址 String redirectUrl = SaSugar.get(() -> { - // 方式1:直接重定向回Client端 (mode=simple) + + // 若 redirect 参数为空,说明用户并不是从 client 重定向来的,而是直接访问的 http://sso-server.com/sso/auth 地址 + // 此时需要跳转到配置的 homeRoute 路由上, + // 若 homeRoute 也为空,则没有明确的跳转地址了,需要抛出异常 + if(SaFoxUtil.isEmpty(redirect)) { + if(SaFoxUtil.isEmpty(cfg.getHomeRoute())) { + throw new SaSsoException("未指定 redirect 参数,也未配置 homeRoute 路由,无法完成重定向操作").setCode(SaSsoErrorCode.CODE_30014); + } + return cfg.getHomeRoute(); + } + + // 方式1:直接重定向回Client端 (mode=simple,一般是模式一) if(mode.equals(SaSsoConsts.MODE_SIMPLE)) { ssoServerTemplate.checkRedirectUrl(client, redirect); return redirect; } else { - // 方式2:带着 ticket 参数重定向回Client端 (mode=ticket) + // 方式2:带着 ticket 参数重定向回Client端 (mode=ticket,一般是模式二、三) // 构建并跳转 String _redirectUrl = ssoServerTemplate.buildRedirectUrl(client, redirect, stpLogic.getLoginId(), stpLogic.getTokenValue()); - // 构建成功,说明 redirect 地址合法,此时需要更新一下该账号的Session有效期 + + // 构建成功,说明 redirect 地址合法,此时需要更新一下当前 token 有效期 if(cfg.getAutoRenewTimeout()) { stpLogic.renewTimeout(stpLogic.getConfigOrGlobal().getTimeout()); } @@ -151,13 +159,14 @@ public class SaSsoServerProcessor { * @return 处理结果 */ public Object ssoDoLogin() { - // 获取对象 + // 获取参数 SaRequest req = SaHolder.getRequest(); - SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig(); ParamName paramName = ssoServerTemplate.paramName; + String name = req.getParam(paramName.name); + String pwd = req.getParam(paramName.pwd); // 处理 - return ssoServerTemplate.strategy.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd)); + return ssoServerTemplate.strategy.doLoginHandle.apply(name, pwd); } /** @@ -168,14 +177,15 @@ public class SaSsoServerProcessor { // 获取对象 SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); - Object loginId = ssoServerTemplate.getStpLogic().getLoginIdDefaultNull(); + StpLogic stpLogic = ssoServerTemplate.getStpLogic(); + Object loginId = stpLogic.getLoginIdDefaultNull(); boolean singleDeviceIdLogout = req.isParam(ssoServerTemplate.paramName.singleDeviceIdLogout, "true"); // 单点注销 if(SaFoxUtil.isNotEmpty(loginId)) { - SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter(); + SaLogoutParameter logoutParameter = stpLogic.createSaLogoutParameter(); if(singleDeviceIdLogout) { - logoutParameter.setDeviceId(ssoServerTemplate.getStpLogic().getLoginDeviceId()); + logoutParameter.setDeviceId(stpLogic.getLoginDeviceId()); } ssoServerTemplate.ssoLogout(loginId, logoutParameter); } @@ -202,7 +212,7 @@ public class SaSsoServerProcessor { return SaResult.error("无效 client 标识:" + client); } - // 3、校验签名 + // 3、校验参数签名 Map paramMap = req.getParamMap(); if(ssoServerConfig.getIsCheckSign()) { ssoServerTemplate.getSignTemplate(client).checkParamMap(paramMap); @@ -210,10 +220,10 @@ public class SaSsoServerProcessor { SaSsoManager.printNoCheckSignWarningByRuntime(); } - // 处理消息 + // 4、处理消息 SaSsoMessage message = new SaSsoMessage(paramMap); if( ! ssoServerTemplate.messageHolder.hasHandle(message.getType())) { - return SaResult.error("未能找到消息处理器: " + message.getType()); + return SaResult.error("未能找到消息处理器:" + message.getType()); } return ssoServerTemplate.handleMessage(message); } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoClientStrategy.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoClientStrategy.java index 6a24937e..e205ce3c 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoClientStrategy.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoClientStrategy.java @@ -19,6 +19,9 @@ import cn.dev33.satoken.SaManager; import cn.dev33.satoken.fun.SaParamRetFunction; import cn.dev33.satoken.sso.function.SendHttpFunction; import cn.dev33.satoken.sso.function.TicketResultHandleFunction; +import cn.dev33.satoken.util.SaResult; + +import java.util.Map; /** * Sa-Token SSO Client 相关策略 @@ -62,4 +65,16 @@ public class SaSsoClientStrategy { return loginId; }; + /** + * 发送 Http 请求,并将响应结果转换为 SaResult + * + * @param url 请求地址 + * @return 返回的结果 + */ + public SaResult requestAsSaResult(String url) { + String body = sendHttp.apply(url); + Map map = SaManager.getSaJsonTemplate().jsonToMap(body); + return new SaResult(map); + } + } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoServerStrategy.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoServerStrategy.java index 90f41d9b..4c9c2197 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoServerStrategy.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/strategy/SaSsoServerStrategy.java @@ -23,6 +23,8 @@ import cn.dev33.satoken.sso.function.NotLoginViewFunction; import cn.dev33.satoken.sso.function.SendHttpFunction; import cn.dev33.satoken.util.SaResult; +import java.util.Map; + /** * Sa-Token SSO Server 相关策略 * @@ -66,4 +68,16 @@ public class SaSsoServerStrategy { return result; }; + /** + * 发送 Http 请求,并将响应结果转换为 SaResult + * + * @param url 请求地址 + * @return 返回的结果 + */ + public SaResult requestAsSaResult(String url) { + String body = sendHttp.apply(url); + Map map = SaManager.getSaJsonTemplate().jsonToMap(body); + return new SaResult(map); + } + } 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 index d7e03dd1..85dc3366 100644 --- 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 @@ -33,7 +33,7 @@ import cn.dev33.satoken.util.SaResult; import java.util.Map; /** - * Sa-Token SSO 模板方法类 (Client端) + * SSO 模板方法类 (Client端) * * @author click33 * @since 1.38.0 @@ -50,10 +50,11 @@ public class SaSsoClientTemplate extends SaSsoTemplate { } - // ------------------- SSO 模式三相关 ------------------- + // ------------------- getData 相关 ------------------- /** * 根据配置的 getData 地址,查询数据 + * * @param paramMap 查询参数 * @return 查询结果 */ @@ -64,6 +65,7 @@ public class SaSsoClientTemplate extends SaSsoTemplate { /** * 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址) + * * @param path 自定义 path * @param paramMap 查询参数 * @return 查询结果 @@ -73,44 +75,6 @@ public class SaSsoClientTemplate extends SaSsoTemplate { return strategy.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 = 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 + "=") ) { - clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back); - } - - // 返回 - return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl); - } - /** * 构建URL:Server端 getData 地址,带签名等参数 * @param paramMap 查询参数 @@ -146,55 +110,49 @@ public class SaSsoClientTemplate extends SaSsoTemplate { return SaFoxUtil.joinParam(url, signParamsStr); } - /** - * 构建消息:校验 ticket - * - * @param ticket ticket码 - * @param ssoLogoutCallUrl 单点注销时的回调URL - * @return 构建完毕的URL - */ - public SaSsoMessage buildCheckTicketMessage(String ticket, String ssoLogoutCallUrl) { - SaSsoClientConfig ssoConfig = getClientConfig(); - SaSsoMessage message = new SaSsoMessage(); - message.setType(SaSsoConsts.MESSAGE_CHECK_TICKET); - message.set(paramName.client, getClient()); - message.set(paramName.ticket, ticket); - message.set(paramName.ssoLogoutCall, ssoLogoutCallUrl); - return message; - } + + // ---------------------- 构建交互地址 ---------------------- /** - * 构建消息:单点注销 - * - * @param loginId 要注销的账号 id - * @param logoutParameter 单点注销 - * @return 单点注销URL + * 构建URL:Server端 单点登录授权地址, + *
形如:http://sso-server.com/sso/auth?redirectUrl=http://sso-client.com/sso/login?back=http://sso-client.com + * @param clientLoginUrl Client端登录地址 + * @param back 回调路径 + * @return [SSO-Server端-认证地址 ] */ - public SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) { + public String buildServerAuthUrl(String clientLoginUrl, String back) { SaSsoClientConfig ssoConfig = getClientConfig(); - SaSsoMessage message = new SaSsoMessage(); - message.setType(SaSsoConsts.MESSAGE_SIGNOUT); - message.set(paramName.client, getClient()); - message.set(paramName.loginId, loginId); - message.set(paramName.deviceId, logoutParameter.getDeviceId()); - return message; + + // 服务端认证地址 + String serverUrl = ssoConfig.splicingAuthUrl(); + + // 拼接客户端标识 + String client = 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 + "=") ) { + clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back); + } + + // 返回 + return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl); } // ------------------- 消息推送 ------------------- - /** - * 发送 Http 请求,并将响应结果转换为 SaResult - * - * @param url 请求地址 - * @return 返回的结果 - */ - public SaResult requestAsSaResult(String url) { - String body = strategy.sendHttp.apply(url); - Map map = SaManager.getSaJsonTemplate().jsonToMap(body); - return new SaResult(map); - } - /** * 向 sso-server 推送消息 * @@ -230,6 +188,40 @@ public class SaSsoClientTemplate extends SaSsoTemplate { return new SaResult(map); } + /** + * 构建消息:校验 ticket + * + * @param ticket ticket码 + * @param ssoLogoutCallUrl 单点注销时的回调URL + * @return 构建完毕的URL + */ + public SaSsoMessage buildCheckTicketMessage(String ticket, String ssoLogoutCallUrl) { + SaSsoClientConfig ssoConfig = getClientConfig(); + SaSsoMessage message = new SaSsoMessage(); + message.setType(SaSsoConsts.MESSAGE_CHECK_TICKET); + message.set(paramName.client, getClient()); + message.set(paramName.ticket, ticket); + message.set(paramName.ssoLogoutCall, ssoLogoutCallUrl); + return message; + } + + /** + * 构建消息:单点注销 + * + * @param loginId 要注销的账号 id + * @param logoutParameter 单点注销 + * @return 单点注销URL + */ + public SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) { + SaSsoClientConfig ssoConfig = getClientConfig(); + SaSsoMessage message = new SaSsoMessage(); + message.setType(SaSsoConsts.MESSAGE_SIGNOUT); + message.set(paramName.client, getClient()); + message.set(paramName.loginId, loginId); + message.set(paramName.deviceId, logoutParameter.getDeviceId()); + return message; + } + // ------------------- Bean 对象获取 ------------------- diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientUtil.java new file mode 100644 index 00000000..b9594569 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoClientUtil.java @@ -0,0 +1,127 @@ +/* + * 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.sso.message.SaSsoMessage; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.stp.parameter.SaLogoutParameter; +import cn.dev33.satoken.util.SaResult; + +import java.util.Map; + +/** + * SSO 模板方法类 (Client端) + * + * @author click33 + * @since 1.38.0 + */ +public class SaSsoClientUtil extends SaSsoTemplate { + + private SaSsoClientUtil() { + } + + /** + * 返回底层使用的 SaSsoClientTemplate 对象 + * @return / + */ + public static SaSsoClientTemplate getSsoTemplate() { + return SaSsoClientProcessor.instance.ssoClientTemplate; + } + + + // ------------------- getData 相关 ------------------- + + /** + * 根据配置的 getData 地址,查询数据 + * + * @param paramMap 查询参数 + * @return 查询结果 + */ + public static Object getData(Map paramMap) { + return SaSsoClientProcessor.instance.ssoClientTemplate.getData(paramMap); + } + + /** + * 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址) + * + * @param path 自定义 path + * @param paramMap 查询参数 + * @return 查询结果 + */ + public static Object getData(String path, Map paramMap) { + return SaSsoClientProcessor.instance.ssoClientTemplate.getData(path, paramMap); + } + + + // ---------------------- 构建交互地址 ---------------------- + + /** + * 构建URL:Server端 单点登录授权地址, + *
形如:http://sso-server.com/sso/auth?redirectUrl=http://sso-client.com/sso/login?back=http://sso-client.com + * @param clientLoginUrl Client端登录地址 + * @param back 回调路径 + * @return [SSO-Server端-认证地址 ] + */ + public static String buildServerAuthUrl(String clientLoginUrl, String back) { + return SaSsoClientProcessor.instance.ssoClientTemplate.buildServerAuthUrl(clientLoginUrl, back); + } + + + // ------------------- 消息推送 ------------------- + + /** + * 向 sso-server 推送消息 + * + * @param message / + * @return / + */ + public static String pushMessage(SaSsoMessage message) { + return SaSsoClientProcessor.instance.ssoClientTemplate.pushMessage(message); + } + + /** + * 向 sso-server 推送消息,并将返回值转为 SaResult + * + * @param message / + * @return / + */ + public static SaResult pushMessageAsSaResult(SaSsoMessage message) { + return SaSsoClientProcessor.instance.ssoClientTemplate.pushMessageAsSaResult(message); + } + + /** + * 构建消息:校验 ticket + * + * @param ticket ticket码 + * @param ssoLogoutCallUrl 单点注销时的回调URL + * @return 构建完毕的URL + */ + public static SaSsoMessage buildCheckTicketMessage(String ticket, String ssoLogoutCallUrl) { + return SaSsoClientProcessor.instance.ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCallUrl); + } + + /** + * 构建消息:单点注销 + * + * @param loginId 要注销的账号 id + * @param logoutParameter 单点注销 + * @return 单点注销URL + */ + public static SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) { + return SaSsoClientProcessor.instance.ssoClientTemplate.buildSloMessage(loginId, logoutParameter); + } + +} 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 index 9306ae8e..855b7ccd 100644 --- 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 @@ -39,7 +39,7 @@ import cn.dev33.satoken.util.SaResult; import java.util.*; /** - * Sa-Token SSO 模板方法类 (Server端) + * SSO 模板方法类 (Server端) * * @author click33 * @since 1.38.0 @@ -58,40 +58,17 @@ public class SaSsoServerTemplate extends SaSsoTemplate { // ---------------------- Ticket 操作 ---------------------- + // 增删改 + /** * 保存 Ticket * @param ticketModel / */ public void saveTicket(TicketModel ticketModel) { long ticketTimeout = getServerConfig().getTicketTimeout(); - SaManager.getSaTokenDao().setObject(splicingTicketSaveKey(ticketModel.getTicket()), ticketModel, ticketTimeout); + SaManager.getSaTokenDao().setObject(splicingTicketModelSaveKey(ticketModel.getTicket()), ticketModel, ticketTimeout); } - /** - * 保存 Ticket 索引 (id 反查 ticket) - * - * @param client 应用端 - * @param ticket ticket码 - * @param loginId 账号id - */ - public void saveTicketIndex(String client, Object loginId, String ticket) { - long ticketTimeout = getServerConfig().getTicketTimeout(); - SaManager.getSaTokenDao().set(splicingTicketIndexKey(client, 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码 @@ -100,36 +77,59 @@ public class SaSsoServerTemplate extends SaSsoTemplate { if(ticket == null) { return; } - SaManager.getSaTokenDao().deleteObject(splicingTicketSaveKey(ticket)); + SaManager.getSaTokenDao().deleteObject(splicingTicketModelSaveKey(ticket)); } /** - * 删除 Ticket索引 + * 根据参数创建一个 ticket 码 * - * @param client 应用标识 - * @param loginId 账号id + * @param client 客户端标识 + * @param loginId 账号 id + * @param tokenValue 会话 Token + * @return Ticket码 */ - public void deleteTicketIndex(String client, Object loginId) { - if(loginId == null) { - return; - } - SaManager.getSaTokenDao().delete(splicingTicketIndexKey(client, loginId)); + public TicketModel createTicket(String client, Object loginId, String tokenValue) { + TicketModel ticketModel = new TicketModel(); + ticketModel.setTicket(randomTicket(loginId)); + ticketModel.setClient(client); + ticketModel.setLoginId(loginId); + ticketModel.setTokenValue(tokenValue); + return ticketModel; } -// /** -// * 删除 Ticket 关联的 client -// * -// * @param ticket Ticket码 -// */ -// public void deleteTicketToClient(String ticket) { -// if(ticket == null) { -// return; -// } -// SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); -// } + /** + * 根据参数创建一个 ticket 码,并保存 + * + * @param client 客户端标识 + * @param loginId 账号 id + * @param tokenValue 会话 Token + * @return Ticket码 + */ + public String createTicketAndSave(String client, Object loginId, String tokenValue) { + // 创建 + TicketModel ticketModel = createTicket(client, loginId, tokenValue); + + // 保存 + saveTicket(ticketModel); + saveTicketIndex(client, loginId, ticketModel.getTicket()); + + // 返回 + return ticketModel.getTicket(); + } /** - * 查询 ticket ,如果 ticket 码无效则返回 null + * 随机一个 Ticket 码 + * @param loginId 账号id + * @return Ticket 码 + */ + public String randomTicket(Object loginId) { + return SaFoxUtil.getRandomString(64); + } + + // 查 + + /** + * 查询 ticket ,如果 ticket 无效则返回 null * * @param ticket Ticket码 * @return 账号id @@ -138,7 +138,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { if(SaFoxUtil.isEmpty(ticket)) { return null; } - return SaManager.getSaTokenDao().getObject(splicingTicketSaveKey(ticket), TicketModel.class); + return SaManager.getSaTokenDao().getObject(splicingTicketModelSaveKey(ticket), TicketModel.class); } /** @@ -165,57 +165,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { return SaFoxUtil.getValueByType(getLoginId(ticket), cs); } - /** - * 查询 指定 client、loginId 其所属的 ticket 值 - * - * @param client 应用 - * @param loginId 账号id - * @return Ticket值 - */ - public String getTicketValue(String client, Object loginId) { - if(loginId == null) { - return null; - } - return SaManager.getSaTokenDao().get(splicingTicketIndexKey(client, 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)); -// } - - // - /** - * 根据参数创建一个 ticket 码 - * - * @param client 客户端标识 - * @param loginId 账号 id - * @param tokenValue 会话 Token - * @return Ticket码 - */ - public String createTicket(String client, Object loginId, String tokenValue) { - // 创建 Ticket - String ticket = randomTicket(loginId); - TicketModel ticketModel = new TicketModel(); - ticketModel.setTicket(ticket); - ticketModel.setClient(client); - ticketModel.setLoginId(loginId); - ticketModel.setTokenValue(tokenValue); - - // 保存 Ticket - saveTicket(ticketModel); - saveTicketIndex(client, loginId, ticket); - - // 返回 Ticket - return ticket; - } + // 校验 /** * 校验 Ticket,无效 ticket 会抛出异常 @@ -271,13 +221,45 @@ public class SaSsoServerTemplate extends SaSsoTemplate { return ticketModel; } + // ticket 索引 + /** - * 随机一个 Ticket码 + * 保存 Ticket 索引 (id 反查 ticket) + * + * @param client 应用端 + * @param ticket ticket码 * @param loginId 账号id - * @return Ticket码 */ - public String randomTicket(Object loginId) { - return SaFoxUtil.getRandomString(64); + public void saveTicketIndex(String client, Object loginId, String ticket) { + long ticketTimeout = getServerConfig().getTicketTimeout(); + SaManager.getSaTokenDao().set(splicingTicketIndexKey(client, loginId), String.valueOf(ticket), ticketTimeout); + } + + /** + * 删除 Ticket 索引 + * + * @param client 应用标识 + * @param loginId 账号id + */ + public void deleteTicketIndex(String client, Object loginId) { + if(loginId == null) { + return; + } + SaManager.getSaTokenDao().delete(splicingTicketIndexKey(client, loginId)); + } + + /** + * 查询 指定 client、loginId 其所属的 ticket 值 + * + * @param client 应用 + * @param loginId 账号id + * @return Ticket值 + */ + public String getTicketValue(String client, Object loginId) { + if(loginId == null) { + return null; + } + return SaManager.getSaTokenDao().get(splicingTicketIndexKey(client, loginId)); } @@ -289,8 +271,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { * @return / */ public List getClients() { - Map clients = getServerConfig().getClients(); - return new ArrayList<>(clients.values()); + return new ArrayList<>(getServerConfig().getClients().values()); } /** @@ -351,7 +332,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { List list = new ArrayList<>(); List clients = getClients(); for(SaSsoClientModel scm : clients) { - if (scm.isValidNoticeUrl()) { + if (scm.getIsPush()) { list.add(scm); } } @@ -379,14 +360,15 @@ public class SaSsoServerTemplate extends SaSsoTemplate { deleteTicket(getTicketValue(client, loginId)); // 创建 新Ticket - String ticket = createTicket(client, loginId, tokenValue); + String ticket = createTicketAndSave(client, loginId, tokenValue); - // 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket) + // 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放 Ticket) return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket); } /** - * 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug + * 对 url 中的 back 参数进行 URL 编码, 解决超链接重定向后参数丢失的 bug + * * @param url url * @return 编码过后的url */ @@ -412,7 +394,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { } /** - * 校验重定向url合法性 + * 校验重定向 url 合法性 * * @param client 应用标识 * @param url 下放ticket的url地址 @@ -513,7 +495,8 @@ public class SaSsoServerTemplate extends SaSsoTemplate { // ------------------- 单点注销 ------------------- /** - * 为指定账号 id 注册单点注销回调信息(模式三) + * 为指定账号 id 注册应用接入信息(模式三) + * * @param loginId 账号id * @param client 指定客户端标识,可为null * @param sloCallbackUrl 单点注销时的回调URL @@ -539,7 +522,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { for (;;) { if(scmList.size() > maxRegClient) { SaSsoClientInfo removeScm = scmList.remove(0); - notifyClientLogout(loginId, null, removeScm, true); + notifyClientLogout(loginId, null, removeScm, true, true); } else { break; } @@ -591,7 +574,6 @@ public class SaSsoServerTemplate extends SaSsoTemplate { public void ssoLogout(Object loginId, SaLogoutParameter logoutParameter) { // 1、消息推送:单点注销 - // TODO 需要把对应的 SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_ 记录也删掉 pushToAllClientByLogoutCall(loginId, logoutParameter); // 2、SaSession 挂载的 Client 端注销会话 @@ -601,7 +583,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { } List scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new); scmList.forEach(scm -> { - notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false); + notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false, false); }); // 3、Server 端本身注销 @@ -614,17 +596,22 @@ public class SaSsoServerTemplate extends SaSsoTemplate { * @param deviceId 指定设备 id * @param scm 客户端信息对象 * @param autoLogout 是否为超过 maxRegClient 的自动注销 + * @param isPushWork 如果该 client 没有注册注销回调地址,是否使用 push 消息的方式进行注销回调通知 */ - public void notifyClientLogout(Object loginId, String deviceId, SaSsoClientInfo scm, boolean autoLogout) { + public void notifyClientLogout(Object loginId, String deviceId, SaSsoClientInfo scm, boolean autoLogout, boolean isPushWork) { // 如果给个null值,不进行任何操作 if(scm == null || scm.mode != SaSsoConsts.SSO_MODE_3) { return; } - // 如果此 Client 并没有注册 单点登录 回调地址,则立即返回 + // 如果此 Client 并没有注册 单点注销 回调地址 String sloCallUrl = scm.getSloCallbackUrl(); if(SaFoxUtil.isEmpty(sloCallUrl)) { + // TODO 代码有效性待验证 + if(isPushWork && SaFoxUtil.isNotEmpty(scm.getClient())) { + pushToClientByLogoutCall(getClient(scm.getClient()), loginId, getStpLogic().createSaLogoutParameter()); + } return; } @@ -646,18 +633,6 @@ public class SaSsoServerTemplate extends SaSsoTemplate { // ------------------- 消息推送 ------------------- - /** - * 发送 Http 请求,并将响应结果转换为 SaResult - * - * @param url 请求地址 - * @return 返回的结果 - */ - public SaResult requestAsSaResult(String url) { - String body = strategy.sendHttp.apply(url);; - Map map = SaManager.getSaJsonTemplate().jsonToMap(body); - return new SaResult(map); - } - /** * 向指定 Client 推送消息 * @param clientModel / @@ -666,7 +641,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { */ public String pushMessage(SaSsoClientModel clientModel, SaSsoMessage message) { message.checkType(); - String noticeUrl = clientModel.splicingNoticeUrl(); + String noticeUrl = clientModel.splicingPushUrl(); String paramsStr = getSignTemplate(clientModel.getClient()).addSignParamsAndJoin(message); String finalUrl = SaFoxUtil.joinParam(noticeUrl, paramsStr); return strategy.sendHttp.apply(finalUrl); @@ -714,8 +689,8 @@ public class SaSsoServerTemplate extends SaSsoTemplate { * @param message / */ public void pushToAllClient(SaSsoMessage message) { - List mode3Clients = getNeedPushClients(); - for (SaSsoClientModel client : mode3Clients) { + List needPushClients = getNeedPushClients(); + for (SaSsoClientModel client : needPushClients) { pushMessage(client, message); } } @@ -730,15 +705,25 @@ public class SaSsoServerTemplate extends SaSsoTemplate { List npClients = getNeedPushClients(); for (SaSsoClientModel client : npClients) { if(client.getIsSlo()) { - SaSsoMessage message = new SaSsoMessage(); - message.setType(SaSsoConsts.MESSAGE_LOGOUT_CALL); - message.set(paramName.loginId, loginId); - message.set(paramName.deviceId, logoutParameter.getDeviceId()); - pushMessage(client, message); + pushToClientByLogoutCall(client, loginId, logoutParameter); } } } + /** + * 向指定 Client 推送消息:单点注销回调 + * + * @param loginId / + * @param logoutParameter 注销参数 + */ + public void pushToClientByLogoutCall(SaSsoClientModel client, Object loginId, SaLogoutParameter logoutParameter) { + SaSsoMessage message = new SaSsoMessage(); + message.setType(SaSsoConsts.MESSAGE_LOGOUT_CALL); + message.set(paramName.loginId, loginId); + message.set(paramName.deviceId, logoutParameter.getDeviceId()); + pushMessage(client, message); + } + // ------------------- Bean 获取 ------------------- @@ -776,25 +761,16 @@ public class SaSsoServerTemplate extends SaSsoTemplate { // ------------------- 返回相应key ------------------- /** - * 拼接key:Ticket 查 账号Id + * 拼接key:TicketModel * @param ticket ticket值 * @return key */ - public String splicingTicketSaveKey(String ticket) { + public String splicingTicketModelSaveKey(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 + * 拼接key:Ticket 索引 * * @param client 应用标识 * @param id 账号id @@ -804,7 +780,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate { if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) { client = SaSsoConsts.CLIENT_ANON; } - return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + client + ":" + id; + return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-index:" + client + ":" + id; } } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerUtil.java new file mode 100644 index 00000000..42f15460 --- /dev/null +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerUtil.java @@ -0,0 +1,300 @@ +/* + * 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.sso.config.SaSsoClientModel; +import cn.dev33.satoken.sso.message.SaSsoMessage; +import cn.dev33.satoken.sso.model.TicketModel; +import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.stp.parameter.SaLogoutParameter; +import cn.dev33.satoken.util.SaResult; + +import java.util.List; + +/** + * SSO 工具类 (Server端) + * + * @author click33 + * @since 1.43.0 + */ +public class SaSsoServerUtil extends SaSsoTemplate { + + private SaSsoServerUtil() { + } + + /** + * 返回底层使用的 SaSsoServerTemplate 对象 + * @return / + */ + public static SaSsoServerTemplate getSsoTemplate() { + return SaSsoServerProcessor.instance.ssoServerTemplate; + } + + + // ---------------------- Ticket 操作 ---------------------- + + // 增删改 + + /** + * 删除 Ticket + * @param ticket Ticket码 + */ + public static void deleteTicket(String ticket) { + SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicket(ticket); + } + + /** + * 根据参数创建一个 ticket 码,并保存 + * + * @param client 客户端标识 + * @param loginId 账号 id + * @param tokenValue 会话 Token + * @return Ticket码 + */ + public static String createTicketAndSave(String client, Object loginId, String tokenValue) { + return SaSsoServerProcessor.instance.ssoServerTemplate.createTicketAndSave(client, loginId, tokenValue); + } + + // 查 + + /** + * 查询 ticket ,如果 ticket 无效则返回 null + * + * @param ticket Ticket码 + * @return 账号id + */ + public static TicketModel getTicket(String ticket) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getTicket(ticket); + } + + /** + * 查询 ticket 指向的 loginId,如果 ticket 码无效则返回 null + * @param ticket Ticket码 + * @return 账号id + */ + public static Object getLoginId(String ticket) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket); + } + + /** + * 查询 ticket 指向的 loginId,并转换为指定类型 + * @param 要转换的类型 + * @param ticket Ticket码 + * @param cs 要转换的类型 + * @return 账号id + */ + public static T getLoginId(String ticket, Class cs) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket, cs); + } + + // 校验 + + /** + * 校验 Ticket,无效 ticket 会抛出异常 + * + * @param ticket Ticket码 + * @return / + */ + public static TicketModel checkTicket(String ticket) { + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket); + } + + /** + * 校验 Ticket 码,无效 ticket 会抛出异常,如果此ticket是有效的,则立即删除 + * @param ticket Ticket码 + * @return 账号id + */ + public static TicketModel checkTicketParamAndDelete(String ticket) { + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicketParamAndDelete(ticket); + } + + /** + * 校验 Ticket,无效 ticket 会抛出异常,如果此ticket是有效的,则立即删除 + * + * @param ticket Ticket码 + * @param client client 标识 + * @return / + */ + public static TicketModel checkTicketParamAndDelete(String ticket, String client) { + return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicketParamAndDelete(ticket, client); + } + + // ticket 索引 + + /** + * 查询 指定 client、loginId 其所属的 ticket 值 + * + * @param client 应用 + * @param loginId 账号id + * @return Ticket值 + */ + public static String getTicketValue(String client, Object loginId) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getTicketValue(client, loginId); + } + + + // ---------------------- Client 信息获取 ---------------------- + + /** + * 获取所有 Client + * + * @return / + */ + public static List getClients() { + return SaSsoServerProcessor.instance.ssoServerTemplate.getClients(); + } + + /** + * 获取应用信息,无效 client 返回 null + * + * @param client / + * @return / + */ + public static SaSsoClientModel getClient(String client) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getClient(client); + } + + /** + * 获取应用信息,无效 client 则抛出异常 + * + * @param client / + * @return / + */ + public static SaSsoClientModel getClientNotNull(String client) { + return SaSsoServerProcessor.instance.ssoServerTemplate.getClientNotNull(client); + } + + /** + * 获取匿名 client 信息 + * + * @return / + */ + public static SaSsoClientModel getAnonClient() { + return SaSsoServerProcessor.instance.ssoServerTemplate.getAnonClient(); + } + + /** + * 获取所有需要接收消息推送的 Client + * + * @return / + */ + public static List getNeedPushClients() { + return SaSsoServerProcessor.instance.ssoServerTemplate.getNeedPushClients(); + } + + + // ------------------- 重定向 URL 构建与校验 ------------------- + + /** + * 构建 URL:sso-server 端向 sso-client 下放 ticket 的地址 + * + * @param client 客户端标识 + * @param redirect sso-client 端的重定向地址 + * @param loginId 账号 id + * @param tokenValue 会话 token + * @return / + */ + public static String buildRedirectUrl(String client, String redirect, Object loginId, String tokenValue) { + return SaSsoServerProcessor.instance.ssoServerTemplate.buildRedirectUrl(client, redirect, loginId, tokenValue); + } + + /** + * 校验重定向 url 合法性 + * + * @param client 应用标识 + * @param url 下放ticket的url地址 + */ + public static void checkRedirectUrl(String client, String url) { + SaSsoServerProcessor.instance.ssoServerTemplate.checkRedirectUrl(client, url); + } + + + // ------------------- 单点注销 ------------------- + + /** + * 指定账号单点注销 + * + * @param loginId 指定账号 + */ + public static void ssoLogout(Object loginId) { + SaSsoServerProcessor.instance.ssoServerTemplate.ssoLogout(loginId); + } + + /** + * 指定账号单点注销 + * + * @param loginId 指定账号 + * @param logoutParameter 注销参数 + */ + public static void ssoLogout(Object loginId, SaLogoutParameter logoutParameter) { + SaSsoServerProcessor.instance.ssoServerTemplate.ssoLogout(loginId, logoutParameter); + } + + + // ------------------- 消息推送 ------------------- + + /** + * 向指定 Client 推送消息 + * @param clientModel / + * @param message / + * @return / + */ + public static String pushMessage(SaSsoClientModel clientModel, SaSsoMessage message) { + return SaSsoServerProcessor.instance.ssoServerTemplate.pushMessage(clientModel, message); + } + + /** + * 向指定 client 推送消息,并将返回值转为 SaResult + * + * @param clientModel / + * @param message / + * @return / + */ + public static SaResult pushMessageAsSaResult(SaSsoClientModel clientModel, SaSsoMessage message) { + return SaSsoServerProcessor.instance.ssoServerTemplate.pushMessageAsSaResult(clientModel, message); + } + + /** + * 向指定 Client 推送消息 + * @param client / + * @param message / + * @return / + */ + public static String pushMessage(String client, SaSsoMessage message) { + return SaSsoServerProcessor.instance.ssoServerTemplate.pushMessage(client, message); + } + + /** + * 向指定 client 推送消息,并将返回值转为 SaResult + * + * @param client / + * @param message / + * @return / + */ + public static SaResult pushMessageAsSaResult(String client, SaSsoMessage message) { + return SaSsoServerProcessor.instance.ssoServerTemplate.pushMessageAsSaResult(client, message); + } + + /** + * 向所有 Client 推送消息 + * + * @param message / + */ + public static void pushToAllClient(SaSsoMessage message) { + SaSsoServerProcessor.instance.ssoServerTemplate.pushToAllClient(message); + } + +} 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 index fd597b3d..ce72fee7 100644 --- 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 @@ -15,19 +15,15 @@ */ package cn.dev33.satoken.sso.template; -import cn.dev33.satoken.SaManager; import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.message.SaSsoMessageHolder; 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 模板方法类 (公共端) + * SSO 模板方法类 (公共端) * * @author click33 * @since 1.30.0 @@ -74,18 +70,11 @@ public class SaSsoTemplate { // ----------- 消息处理 + /** + * SSO 消息处理器 - 持有器 + */ public SaSsoMessageHolder messageHolder = new SaSsoMessageHolder(); -// /** -// * 发送 Http 请求 -// * -// * @param url / -// * @return / -// */ -// public String request(String url) { -// return SaManager.getSaHttpTemplate().get(url); -// } - /** * 处理指定消息 * @@ -95,5 +84,4 @@ public class SaSsoTemplate { return messageHolder.handleMessage(this, message); } - } diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java index f7b23bdd..cfd3b477 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoUtil.java @@ -23,10 +23,13 @@ import java.util.Map; /** * Sa-Token-SSO 单点登录模块 工具类 + * + *

请更换为 SaSsoServerUtil 或 SaSsoClientUtil

* * @author click33 * @since 1.30.0 */ +@Deprecated public class SaSsoUtil { // ---------------------- Ticket 操作 ---------------------- @@ -40,7 +43,7 @@ public class SaSsoUtil { * @return Ticket码 */ public static String createTicket(String client, Object loginId, String deviceId) { - return SaSsoServerProcessor.instance.ssoServerTemplate.createTicket(client, loginId, deviceId); + return SaSsoServerProcessor.instance.ssoServerTemplate.createTicketAndSave(client, loginId, deviceId); } /** diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java index b51f4f5b..e9423455 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/util/SaSsoConsts.java @@ -58,9 +58,6 @@ public class SaSsoConsts { /** SSO 模式3 */ public static final int SSO_MODE_3 = 3; -// /** 消息类型:单点注销 */ -// public static final String MESSAGE_LOGOUT = "logout"; - /** 消息类型:校验 ticket */ public static final String MESSAGE_CHECK_TICKET = "checkTicket";