refactor: 重构 sa-token-sso 模块代码及注释,提高代码可读性

This commit is contained in:
click33 2025-05-05 12:55:52 +08:00
parent b59fa26edf
commit 213d98d848
36 changed files with 1075 additions and 649 deletions

View File

@ -22,7 +22,7 @@ public class SsoClientController implements Render {
@Mapping("/") @Mapping("/")
public String index() { public String index() {
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" + String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" + "<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " + "<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +

View File

@ -18,7 +18,7 @@ public class SsoClientController {
@RequestMapping("/") @RequestMapping("/")
public String index() { public String index() {
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" + String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" + "<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " + "<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +

View File

@ -20,7 +20,7 @@ public class SsoClientController {
// 首页 // 首页
@RequestMapping("/") @RequestMapping("/")
public String index() { public String index() {
String solUrl = SaSsoManager.getClientConfig().splicingSloUrl(); String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" + String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" + "<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a> " + "<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a> " +

View File

@ -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() { public static void printNoCheckSignWarningByStartup() {
System.err.println("-----------------------------------------------------------------------------"); System.err.println("-----------------------------------------------------------------------------");
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("此模式仅为方便本地调试使用,生产环境下请务必配置为 true 配置项默认为true");
System.err.println("-----------------------------------------------------------------------------"); System.err.println("-----------------------------------------------------------------------------");
} }
// 在运行时检测到 sa-token.sso.is-check-sign=false 输出警告信息 // 在运行时检测到 sa-token.sso-[server/client].is-check-sign=false 输出警告信息
public static void printNoCheckSignWarningByRuntime() { 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"); "此模式仅为方便本地调试使用,生产环境下请务必配置为 true 配置项默认为true");
} }

View File

@ -21,7 +21,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Sa-Token SSO 单点登录模块 配置类 Client端 * Sa-Token SSO Client 配置类
* *
* @author click33 * @author click33
* @since 1.30.0 * @since 1.30.0
@ -36,35 +36,35 @@ public class SaSsoClientConfig implements Serializable {
public String mode = ""; public String mode = "";
/** /**
* 当前 Client 标识 * 当前 Client 标识非必填不填时代表当前应用是一个匿名应用
*/ */
public String client; public String client;
/** /**
* 配置 Server 端主机总地址 * 配置 SSO Server 端主机总地址
*/ */
public String serverUrl; public String serverUrl;
/** /**
* 单独配置 Server 单点登录授权地址 * 单独配置 Server 单点登录授权地址
*/ */
public String authUrl = "/sso/auth"; public String authUrl = "/sso/auth";
/** /**
* 单独配置 Server 查询数据 getData 地址 * 单独配置 Server 单点注销地址
*/ */
public String getDataUrl = "/sso/getData"; public String signoutUrl = "/sso/signout";
/** /**
* 单独配置 Server 端单点注销地址 * 单独配置 Server 推送消息地址
*/
public String sloUrl = "/sso/signout";
/**
* 单独配置 Server 端推送消息地址
*/ */
public String pushUrl = "/sso/pushS"; public String pushUrl = "/sso/pushS";
/**
* 单独配置 Server 查询数据 getData 地址
*/
public String getDataUrl = "/sso/getData";
/** /**
* 配置当前 Client 端的登录地址为空时自动获取 * 配置当前 Client 端的登录地址为空时自动获取
*/ */
@ -75,6 +75,11 @@ public class SaSsoClientConfig implements Serializable {
*/ */
public String currSsoLogoutCall; public String currSsoLogoutCall;
/**
* 是否打开模式三此值为 true 时将使用 http 请求校验 ticket
*/
public Boolean isHttp = false;
/** /**
* 是否打开单点注销功能 ( true 接收单点注销回调消息推送) * 是否打开单点注销功能 ( true 接收单点注销回调消息推送)
*/ */
@ -85,18 +90,13 @@ public class SaSsoClientConfig implements Serializable {
*/ */
public Boolean regLogoutCall = false; public Boolean regLogoutCall = false;
/**
* 是否打开模式三此值为 true 时将使用 http 请求校验 ticket
*/
public Boolean isHttp = false;
/** /**
* API 调用签名秘钥 * API 调用签名秘钥
*/ */
public String secretKey; public String secretKey;
/** /**
* 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true * 是否校验参数签名 false 时暂时关闭参数签名校验此为方便本地调试用的一个配置项生产环境请务必为true
*/ */
public Boolean isCheckSign = true; public Boolean isCheckSign = true;
@ -104,28 +104,28 @@ public class SaSsoClientConfig implements Serializable {
// 额外添加的一些函数 // 额外添加的一些函数
/** /**
* @return 获取拼接urlServer 端单点登录授权地址 * @return 获取拼接 urlServer 端单点登录授权地址
*/ */
public String splicingAuthUrl() { public String splicingAuthUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl()); return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl());
} }
/** /**
* @return 获取拼接urlServer 端查询数据 getData 地址 * @return 获取拼接 urlServer 端查询数据 getData 地址
*/ */
public String splicingGetDataUrl() { public String splicingGetDataUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl()); return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
} }
/** /**
* @return 获取拼接urlServer 端单点注销地址 * @return 获取拼接 urlServer 端单点注销地址
*/ */
public String splicingSloUrl() { public String splicingSignoutUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl()); return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSignoutUrl());
} }
/** /**
* @return 获取拼接url单独配置 Server 端推送消息地址 * @return 获取拼接 url单独配置 Server 端推送消息地址
*/ */
public String splicingPushUrl() { public String splicingPushUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl()); return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl());
@ -153,14 +153,14 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return 是否打开单点注销功能 * @return 是否打开单点注销功能 ( true 接收单点注销回调消息推送)
*/ */
public Boolean getIsSlo() { public Boolean getIsSlo() {
return isSlo; return isSlo;
} }
/** /**
* @param isSlo 是否打开单点注销功能 * @param isSlo 是否打开单点注销功能 ( true 接收单点注销回调消息推送)
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setIsSlo(Boolean isSlo) { 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() { public Boolean getIsHttp() {
return isHttp; return isHttp;
} }
/** /**
* @param isHttp 是否打开模式三此值为 true 时将使用 http 请求校验ticket值单点注销拉取数据getData * @param isHttp 是否打开模式三此值为 true 时将使用 http 请求校验 ticket
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setIsHttp(Boolean isHttp) { public SaSsoClientConfig setIsHttp(Boolean isHttp) {
@ -185,14 +185,18 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return 当前 Client 名称标识用于和 ticket 码的互相锁定 * 当前 Client 标识非必填不填时代表当前应用是一个匿名应用
*
* @return /
*/ */
public String getClient() { public String getClient() {
return client; return client;
} }
/** /**
* @param client 当前 Client 名称标识用于和 ticket 码的互相锁定 * 当前 Client 标识非必填不填时代表当前应用是一个匿名应用
*
* @param client /
*/ */
public SaSsoClientConfig setClient(String client) { public SaSsoClientConfig setClient(String client) {
this.client = client; this.client = client;
@ -200,14 +204,14 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return 配置 Server 单点登录授权地址 * @return 单独配置 Server 单点登录授权地址
*/ */
public String getAuthUrl() { public String getAuthUrl() {
return authUrl; return authUrl;
} }
/** /**
* @param authUrl 配置 Server 单点登录授权地址 * @param authUrl 单独配置 Server 单点登录授权地址
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setAuthUrl(String authUrl) { public SaSsoClientConfig setAuthUrl(String authUrl) {
@ -216,14 +220,14 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return Server 查询数据 getData 地址 * @return 单独配置 Server 查询数据 getData 地址
*/ */
public String getGetDataUrl() { public String getGetDataUrl() {
return getDataUrl; return getDataUrl;
} }
/** /**
* @param getDataUrl 配置 Server 查询数据 getData 地址 * @param getDataUrl 单独配置 Server 查询数据 getData 地址
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setGetDataUrl(String getDataUrl) { public SaSsoClientConfig setGetDataUrl(String getDataUrl) {
@ -232,23 +236,23 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return 配置 Server 单点注销地址 * @return 单独配置 Server 单点注销地址
*/ */
public String getSloUrl() { public String getSignoutUrl() {
return sloUrl; return signoutUrl;
} }
/** /**
* @param sloUrl 配置 Server 单点注销地址 * @param signoutUrl 单独配置 Server 单点注销地址
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setSloUrl(String sloUrl) { public SaSsoClientConfig setSignoutUrl(String signoutUrl) {
this.sloUrl = sloUrl; this.signoutUrl = signoutUrl;
return this; return this;
} }
/** /**
* 获取 单独配置 Server 推送消息地址 * 获取 单独配置 Server 推送消息地址
* *
* @return / * @return /
*/ */
@ -257,7 +261,7 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* 设置 单独配置 Server 推送消息地址 * 设置 单独配置 Server 推送消息地址
* *
* @param pushUrl / * @param pushUrl /
* @return 对象自身 * @return 对象自身
@ -300,14 +304,18 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* @return 配置的 Server 端主机总地址拼接在 authUrlcheckTicketUrlgetDataUrlsloUrl 属性前面用以简化各种 url 配置 * 配置 SSO Server 端主机总地址
*
* @return /
*/ */
public String getServerUrl() { public String getServerUrl() {
return serverUrl; return serverUrl;
} }
/** /**
* @param serverUrl 配置 Server 端主机总地址拼接在 authUrlcheckTicketUrlgetDataUrlsloUrl 属性前面用以简化各种 url 配置 * 配置 SSO Server 端主机总地址
*
* @param serverUrl /
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientConfig setServerUrl(String serverUrl) { public SaSsoClientConfig setServerUrl(String serverUrl) {
@ -336,7 +344,7 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* 获取 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true * 获取 是否校验参数签名 false 时暂时关闭参数签名校验此为方便本地调试用的一个配置项生产环境请务必为true
* *
* @return isCheckSign 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true * @return isCheckSign 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true
*/ */
@ -345,7 +353,7 @@ public class SaSsoClientConfig implements Serializable {
} }
/** /**
* 设置 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true * 设置 是否校验参数签名 false 时暂时关闭参数签名校验此为方便本地调试用的一个配置项生产环境请务必为true
* *
* @param isCheckSign 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true * @param isCheckSign 是否校验参数签名方便本地调试用的一个配置项生产环境请务必为true
*/ */
@ -381,8 +389,9 @@ public class SaSsoClientConfig implements Serializable {
+ ", client=" + client + ", client=" + client
+ ", serverUrl=" + serverUrl + ", serverUrl=" + serverUrl
+ ", authUrl=" + authUrl + ", authUrl=" + authUrl
+ ", signoutUrl=" + signoutUrl
+ ", pushUrl=" + pushUrl
+ ", getDataUrl=" + getDataUrl + ", getDataUrl=" + getDataUrl
+ ", sloUrl=" + sloUrl
+ ", currSsoLogin=" + currSsoLogin + ", currSsoLogin=" + currSsoLogin
+ ", currSsoLogoutCall=" + currSsoLogoutCall + ", currSsoLogoutCall=" + currSsoLogoutCall
+ ", isHttp=" + isHttp + ", isHttp=" + isHttp

View File

@ -16,6 +16,8 @@
package cn.dev33.satoken.sso.config; 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.sso.template.SaSsoServerTemplate;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
@ -23,29 +25,29 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
/** /**
* Sa-Token SSO 客户端信息配置 * Sa-Token SSO 客户端信息配置 Server 端配置允许接入的 Client 信息
* *
* @author click33 * @author click33
* @since 1.42.0 * @since 1.43.0
*/ */
public class SaSsoClientModel implements Serializable { public class SaSsoClientModel implements Serializable {
private static final long serialVersionUID = -6541180061782004705L; private static final long serialVersionUID = -6541180061782004705L;
/** /**
* 当前 Client 名称标识用于和 ticket 码的互相锁定 * Client 名称标识
*/ */
public String client; public String client;
/** /**
* 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) * 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket )
*/ */
public String allowUrl = "*"; 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; public String serverUrl;
/** /**
* Client 端推送消息的地址 * Client 端推送消息的地址 (如不配置默认根据 serverUrl + '/sso/pushC' 进行拼接)
*/ */
public String pushUrl = "/sso/pushC"; public String pushUrl = "/sso/pushC";
@ -85,8 +87,12 @@ public class SaSsoClientModel implements Serializable {
* *
* @return / * @return /
*/ */
public String splicingNoticeUrl() { public String splicingPushUrl() {
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getPushUrl()); 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 / * @return /
*/ */
public boolean isValidNoticeUrl() { public boolean isValidPushUrl() {
return SaFoxUtil.isUrl(splicingNoticeUrl()); return SaFoxUtil.isUrl(splicingPushUrl());
} }
// get set // get set
/** /**
* @return 当前 Client 名称标识 * @return Client 名称标识
*/ */
public String getClient() { public String getClient() {
return client; return client;
} }
/** /**
* @param client 当前 Client 名称标识 * @param client Client 名称标识
*/ */
public SaSsoClientModel setClient(String client) { public SaSsoClientModel setClient(String client) {
this.client = client; this.client = client;
@ -117,14 +123,14 @@ public class SaSsoClientModel implements Serializable {
} }
/** /**
* @return 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) * @return 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket )
*/ */
public String getAllowUrl() { public String getAllowUrl() {
return allowUrl; return allowUrl;
} }
/** /**
* @param allowUrl 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) * @param allowUrl 所有允许的授权回调地址多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket )
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientModel setAllowUrl(String allowUrl) { public SaSsoClientModel setAllowUrl(String allowUrl) {
@ -140,16 +146,16 @@ public class SaSsoClientModel implements Serializable {
/** /**
* @return isHttp 是否打开模式三 * @return isHttp 是否打开模式三
*/ */
public Boolean getIsHttp() { public Boolean getIsPush() {
return isHttp; return isPush;
} }
/** /**
* @param isHttp 是否打开模式三 * @param isPush 是否打开模式三
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoClientModel setIsHttp(Boolean isHttp) { public SaSsoClientModel setIsPush(Boolean isPush) {
this.isHttp = isHttp; this.isPush = isPush;
return this; return this;
} }
@ -210,16 +216,16 @@ public class SaSsoClientModel implements Serializable {
} }
/** /**
* 获取 Client 端推送消息的地址 * 获取 Client 端推送消息的地址 (如不配置默认根据 serverUrl + '/sso/pushC' 进行拼接)
* *
* @return noticeUrl Client 端推送消息的地址 * @return /
*/ */
public String getPushUrl() { public String getPushUrl() {
return this.pushUrl; return this.pushUrl;
} }
/** /**
* 设置 Client 端推送消息的地址 * 设置 Client 端推送消息的地址 (如不配置默认根据 serverUrl + '/sso/pushC' 进行拼接)
* *
* @param pushUrl Client 端推送消息的地址 * @param pushUrl Client 端推送消息的地址
* @return 对象自身 * @return 对象自身
@ -235,7 +241,7 @@ public class SaSsoClientModel implements Serializable {
+ "client=" + client + "client=" + client
+ ", allowUrl=" + allowUrl + ", allowUrl=" + allowUrl
+ ", isSlo=" + isSlo + ", isSlo=" + isSlo
+ ", isHttp=" + isHttp + ", isPush=" + isPush
+ ", secretKey=" + secretKey + ", secretKey=" + secretKey
+ ", serverUrl=" + serverUrl + ", serverUrl=" + serverUrl
+ ", pushUrl=" + pushUrl + ", pushUrl=" + pushUrl

View File

@ -25,7 +25,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Sa-Token SSO 单点登录模块 配置类 Server端 * Sa-Token SSO Server 配置类
* *
* @author click33 * @author click33
* @since 1.38.0 * @since 1.38.0
@ -43,7 +43,7 @@ public class SaSsoServerConfig implements Serializable {
public String mode = ""; public String mode = "";
/** /**
* Ticket有效期 (单位: ) * ticket 有效期 (单位: )
*/ */
public long ticketTimeout = 60 * 5; public long ticketTimeout = 60 * 5;
@ -53,7 +53,7 @@ public class SaSsoServerConfig implements Serializable {
public String homeRoute; public String homeRoute;
/** /**
* 是否打开单点注销功能 * 是否打开单点注销功能 ( true 时接收 client 端推送的单点注销消息)
*/ */
public Boolean isSlo = true; public Boolean isSlo = true;
@ -98,7 +98,7 @@ public class SaSsoServerConfig implements Serializable {
// 额外方法 // 额外方法
/** /**
* 以数组形式写入允许的授权回调地址 * 以数组形式写入允许的授权回调地址 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用)
* @param url 所有集合 * @param url 所有集合
* @return 对象自身 * @return 对象自身
*/ */
@ -139,14 +139,14 @@ public class SaSsoServerConfig implements Serializable {
} }
/** /**
* @return Ticket有效期 (单位: ) * @return ticket 有效期 (单位: )
*/ */
public long getTicketTimeout() { public long getTicketTimeout() {
return ticketTimeout; return ticketTimeout;
} }
/** /**
* @param ticketTimeout Ticket有效期 (单位: ) * @param ticketTimeout ticket 有效期 (单位: )
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoServerConfig setTicketTimeout(long ticketTimeout) { public SaSsoServerConfig setTicketTimeout(long ticketTimeout) {
@ -192,14 +192,14 @@ public class SaSsoServerConfig implements Serializable {
} }
/** /**
* @return 是否打开单点注销功能 * @return 是否打开单点注销功能 ( true 时接收 client 端推送的单点注销消息)
*/ */
public Boolean getIsSlo() { public Boolean getIsSlo() {
return isSlo; return isSlo;
} }
/** /**
* @param isSlo 是否打开单点注销功能 * @param isSlo 是否打开单点注销功能 ( true 时接收 client 端推送的单点注销消息)
* @return 对象自身 * @return 对象自身
*/ */
public SaSsoServerConfig setIsSlo(Boolean isSlo) { public SaSsoServerConfig setIsSlo(Boolean isSlo) {

View File

@ -77,4 +77,7 @@ public interface SaSsoErrorCode {
/** 无效的消息推送地址 */ /** 无效的消息推送地址 */
int CODE_30023 = 30023; int CODE_30023 = 30023;
/** SSO 消息里缺少指定的参数 */
int CODE_30024 = 30024;
} }

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.util.SaResult;
import java.util.function.BiFunction; import java.util.function.BiFunction;
/** /**
* 函数式接口SSO-Server在校验 ticket sso-client 端追加返回信息的函数 * 函数式接口sso-server 在校验 ticket sso-client 端追加返回信息的函数
* *
* <p> 参数loginId, SaResult 响应参数对象 </p> * <p> 参数loginId, SaResult 响应参数对象 </p>
* <p> 返回SaResult 响应参数对象 </p> * <p> 返回SaResult 响应参数对象 </p>

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function;
import java.util.function.BiFunction; import java.util.function.BiFunction;
/** /**
* 函数式接口SSO-Server登录处理函数 * 函数式接口sso-server 登录处理函数
* *
* <p> 参数账号密码 </p> * <p> 参数账号密码 </p>
* <p> 返回登录结果 </p> * <p> 返回登录结果 </p>

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* 函数式接口SSO-Server端未登录时返回的View * 函数式接口sso-server 未登录时返回的 View
* *
* <p> 参数 </p> * <p> 参数 </p>
* <p> 返回未登录时的 View 视图 </p> * <p> 返回未登录时的 View 视图 </p>

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function;
import java.util.function.Function; import java.util.function.Function;
/** /**
* 函数式接口发送Http请求的处理函数 * 函数式接口发送 Http 请求的处理函数
* *
* <p> 参数要请求的url </p> * <p> 参数要请求的url </p>
* <p> 返回请求结果 </p> * <p> 返回请求结果 </p>

View File

@ -18,7 +18,7 @@ package cn.dev33.satoken.sso.function;
import cn.dev33.satoken.sso.model.SaCheckTicketResult; import cn.dev33.satoken.sso.model.SaCheckTicketResult;
/** /**
* 函数式接口SSO-Client自定义校验 ticket 返回值的处理逻辑 每次从认证中心获取校验 ticket 的结果后调用 * 函数式接口sso-client 自定义校验 ticket 返回值的处理逻辑 每次从认证中心获取校验 ticket 的结果后调用
* *
* <p> 参数loginId, back </p> * <p> 参数loginId, back </p>
* <p> 返回返回给前端的值 </p> * <p> 返回返回给前端的值 </p>

View File

@ -26,7 +26,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
* Sa-Token SSO 消息 Model * SSO 消息 Model
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -108,4 +108,19 @@ public class SaSsoMessage extends LinkedHashMap<String, Object> implements SaSet
return this; 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;
}
} }

View File

@ -25,7 +25,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
* Sa-Token SSO 消息处理器持有者 * SSO 消息处理器 - 持有器
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -33,7 +33,7 @@ import java.util.Map;
public class SaSsoMessageHolder { public class SaSsoMessageHolder {
/** /**
* 所有消息处理器的集合 * 所有已注册的消息处理器
*/ */
public final Map<String, SaSsoMessageHandle> messageHandleMap = new LinkedHashMap<>(); public final Map<String, SaSsoMessageHandle> messageHandleMap = new LinkedHashMap<>();
@ -61,6 +61,7 @@ public class SaSsoMessageHolder {
* 添加指定类型的消息处理器 * 添加指定类型的消息处理器
* *
* @param handle / * @param handle /
* @return 对象自身
*/ */
public SaSsoMessageHolder addHandle(SaSsoMessageHandle handle) { public SaSsoMessageHolder addHandle(SaSsoMessageHandle handle) {
messageHandleMap.put(handle.getHandlerType(), handle); messageHandleMap.put(handle.getHandlerType(), handle);
@ -81,7 +82,7 @@ public class SaSsoMessageHolder {
* *
* @param ssoTemplate / * @param ssoTemplate /
* @param message / * @param message /
* @return / * @return 处理结果
*/ */
public Object handleMessage(SaSsoTemplate ssoTemplate, SaSsoMessage message) { public Object handleMessage(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
SaSsoMessageHandle handle = messageHandleMap.get(message.getType()); SaSsoMessageHandle handle = messageHandleMap.get(message.getType());

View File

@ -20,7 +20,7 @@ import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.template.SaSsoTemplate; import cn.dev33.satoken.sso.template.SaSsoTemplate;
/** /**
* Sa-Token SSO 消息 处理器 * SSO 消息处理器 - 父接口
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -35,7 +35,7 @@ public interface SaSsoMessageHandle {
String getHandlerType(); String getHandlerType();
/** /**
* 执行方法 * 具体要执行的处理方法
* *
* @param ssoTemplate / * @param ssoTemplate /
* @param message / * @param message /

View File

@ -29,7 +29,7 @@ import cn.dev33.satoken.stp.parameter.SaLogoutParameter;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
/** /**
* Sa-Token SSO 消息 处理器 - sso-client 处理 单点注销回调 的请求 * SSO 消息处理器 - sso-client 处理 单点注销回调 的请求
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -53,27 +53,28 @@ public class SaSsoMessageLogoutCallHandle implements SaSsoMessageHandle {
* @return / * @return /
*/ */
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
// 1获取对象
SaSsoClientTemplate ssoClientTemplate = (SaSsoClientTemplate) ssoTemplate; SaSsoClientTemplate ssoClientTemplate = (SaSsoClientTemplate) ssoTemplate;
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
ParamName paramName = ssoClientTemplate.paramName;
// 2判断当前应用是否开启单点注销功能
if( ! ssoClientTemplate.getClientConfig().getIsSlo()) { if( ! ssoClientTemplate.getClientConfig().getIsSlo()) {
return SaResult.error("当前 sso-client 端未开启单点注销功能"); return SaResult.error("当前 sso-client 端未开启单点注销功能");
} }
// 获取对象 // 3获取参数
SaRequest req = SaHolder.getRequest(); Object loginId = message.getValueNotNull(paramName.loginId);
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
ParamName paramName = ssoClientTemplate.paramName;
// 获取参数
Object loginId = req.getParamNotNull(paramName.loginId);
loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId); loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId);
String deviceId = message.getString(paramName.deviceId); String deviceId = message.getString(paramName.deviceId);
// 注销当前应用端会话 // 4注销当前应用端会话
stpLogic.logout(loginId, new SaLogoutParameter() stpLogic.logout(loginId, new SaLogoutParameter()
.setDeviceId(deviceId) .setDeviceId(deviceId)
); );
// 响应 // 5响应
return SaResult.ok("单点注销回调成功"); return SaResult.ok("单点注销回调成功");
} }

View File

@ -16,9 +16,6 @@
package cn.dev33.satoken.sso.message.handle.server; 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.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle; import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.model.TicketModel; import cn.dev33.satoken.sso.model.TicketModel;
@ -30,7 +27,7 @@ import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
/** /**
* Sa-Token SSO 消息 处理器 - sso-server 处理校验 ticket 的请求 * SSO 消息处理器 - sso-server 处理校验 ticket 的请求
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -54,38 +51,23 @@ public class SaSsoMessageCheckTicketHandle implements SaSsoMessageHandle {
* @return / * @return /
*/ */
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
// 1获取对象
SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate; SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate;
ParamName paramName = ssoServerTemplate.paramName; ParamName paramName = ssoServerTemplate.paramName;
// 1获取参数
SaRequest req = SaHolder.getRequest();
SaSsoServerConfig ssoServerConfig = ssoServerTemplate.getServerConfig();
StpLogic stpLogic = ssoServerTemplate.getStpLogic(); StpLogic stpLogic = ssoServerTemplate.getStpLogic();
String client = req.getParam(paramName.client); String client = message.getString(paramName.client);
String ticket = req.getParamNotNull(paramName.ticket); String ticket = message.getValueNotNull(paramName.ticket).toString();
String sloCallback = req.getParam(paramName.ssoLogoutCall); String sloCallback = message.getString(paramName.ssoLogoutCall);
// 2校验ticket获取 loginId
// 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
TicketModel ticketModel = ssoServerTemplate.checkTicketParamAndDelete(ticket, client); TicketModel ticketModel = ssoServerTemplate.checkTicketParamAndDelete(ticket, client);
Object loginId = ticketModel.getLoginId(); Object loginId = ticketModel.getLoginId();
// 5注册此客户端的单点注销回调URL // 3注册此客户端的登录信息
ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback); ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback);
// 6 client 端响应结果 // 4 client 端响应结果
SaResult result = SaResult.ok(); SaResult result = SaResult.ok();
result.setData(loginId); // 兼容历史版本 result.setData(loginId); // 兼容历史版本
result.set(paramName.loginId, 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.deviceId, stpLogic.getLoginDeviceIdByToken(ticketModel.getTokenValue()));
result.set(paramName.remainTokenTimeout, stpLogic.getTokenTimeout(ticketModel.getTokenValue())); result.set(paramName.remainTokenTimeout, stpLogic.getTokenTimeout(ticketModel.getTokenValue()));
result.set(paramName.remainSessionTimeout, stpLogic.getSessionTimeoutByLoginId(loginId)); result.set(paramName.remainSessionTimeout, stpLogic.getSessionTimeoutByLoginId(loginId));
result = ssoServerTemplate.strategy.checkTicketAppendData.apply(loginId, result); result = ssoServerTemplate.strategy.checkTicketAppendData.apply(loginId, result);
return result; return result;
} }

View File

@ -16,8 +16,6 @@
package cn.dev33.satoken.sso.message.handle.server; 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.SaSsoMessage;
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle; import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
import cn.dev33.satoken.sso.name.ParamName; import cn.dev33.satoken.sso.name.ParamName;
@ -28,7 +26,7 @@ import cn.dev33.satoken.stp.parameter.SaLogoutParameter;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
/** /**
* Sa-Token SSO 消息 处理器 - sso-server 处理 单点注销 的请求 * SSO 消息处理器 - sso-server 处理 单点注销 的请求
* *
* @author click33 * @author click33
* @since 1.43.0 * @since 1.43.0
@ -52,24 +50,25 @@ public class SaSsoMessageSignoutHandle implements SaSsoMessageHandle {
* @return / * @return /
*/ */
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) { public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message) {
// 1获取对象
SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate; SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate;
ParamName paramName = ssoServerTemplate.paramName; ParamName paramName = ssoServerTemplate.paramName;
// 2判断当前是否开启了全局单点注销功能
if( ! ssoServerTemplate.getServerConfig().getIsSlo()) { if( ! ssoServerTemplate.getServerConfig().getIsSlo()) {
return SaResult.error("当前 sso-server 端未开启单点注销功能"); return SaResult.error("当前 sso-server 端未开启单点注销功能");
} }
// 获取参数 // 3获取参数
SaRequest req = SaHolder.getRequest(); Object loginId = message.get(paramName.loginId);
String loginId = req.getParam(paramName.loginId); String deviceId = message.getString(paramName.deviceId);
String deviceId = req.getParam(paramName.deviceId);
// step.2 单点注销 // 4单点注销
SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter() SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter().setDeviceId(deviceId);
.setDeviceId(deviceId);
ssoServerTemplate.ssoLogout(loginId, logoutParameter); ssoServerTemplate.ssoLogout(loginId, logoutParameter);
// 响应 // 5响应
return SaResult.ok(); return SaResult.ok();
} }

View File

@ -47,12 +47,9 @@ public class SaCheckTicketResult implements Serializable {
/** 此账号在认证中心的 loginId */ /** 此账号在认证中心的 loginId */
public Object centerId; public Object centerId;
/** 从 sso-server 返回的所有参数 */ /** 从 sso-server 返回的原生所有参数 */
public SaResult result; public SaResult result;
public SaCheckTicketResult() {
}
@Override @Override
public String toString() { public String toString() {
return "SaCheckTicketResult{" + return "SaCheckTicketResult{" +

View File

@ -21,7 +21,7 @@ import cn.dev33.satoken.sso.util.SaSsoConsts;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Sa-Token SSO 应用信息 * Sa-Token SSO 应用信息注册在 SaSession 上的已登录应用信息列表
* *
* @author click33 * @author click33
* @since 1.38.0 * @since 1.38.0
@ -45,13 +45,8 @@ public class SaSsoClientInfo implements Serializable {
*/ */
public String client; public String client;
// /**
// * 此次登录 token
// */
// public String tokenValue;
/** /**
* 单点注销回调url * 单点注销回调 url
*/ */
public String sloCallbackUrl; public String sloCallbackUrl;
@ -80,6 +75,8 @@ public class SaSsoClientInfo implements Serializable {
} }
// get set
/** /**
* 获取 client 登录模式1=模式一2=模式二3=模式三 * 获取 client 登录模式1=模式一2=模式二3=模式三
* *
@ -120,26 +117,6 @@ public class SaSsoClientInfo implements Serializable {
return this; 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 * 获取 单点注销回调url
* *
@ -205,7 +182,6 @@ public class SaSsoClientInfo implements Serializable {
return "SaSsoClientModel{" + return "SaSsoClientModel{" +
"mode=" + mode + "mode=" + mode +
", client='" + client + '\'' + ", client='" + client + '\'' +
// ", tokenValue='" + tokenValue + '\'' +
", sloCallbackUrl='" + sloCallbackUrl + '\'' + ", sloCallbackUrl='" + sloCallbackUrl + '\'' +
", regTime=" + regTime + ", regTime=" + regTime +
", index=" + index + ", index=" + index +

View File

@ -37,11 +37,6 @@ public class TicketModel implements Serializable {
*/ */
public String client; public String client;
// /**
// * 设备 id
// */
// public String deviceId;
/** /**
* 对应 loginId * 对应 loginId
*/ */

View File

@ -32,7 +32,7 @@ public class ApiName {
/** SSO-Server端校验ticket 获取账号id */ /** SSO-Server端校验ticket 获取账号id */
public String ssoCheckTicket = "/sso/checkTicket"; public String ssoCheckTicket = "/sso/checkTicket";
/** SSO-Server端推送消息 */ /** SSO-Server端推送消息 */
public String ssoPushS = "/sso/pushS"; public String ssoPushS = "/sso/pushS";
/** SSO-Server端获取userinfo */ /** SSO-Server端获取userinfo */
@ -53,11 +53,11 @@ public class ApiName {
/** SSO-Client端单点注销的回调 */ /** SSO-Client端单点注销的回调 */
public String ssoLogoutCall = "/sso/logoutCall"; public String ssoLogoutCall = "/sso/logoutCall";
/** SSO-Client端推送消息 */ /** SSO-Client端推送消息 */
public String ssoPushC = "/sso/pushC"; public String ssoPushC = "/sso/pushC";
/** /**
* 批量修改path新增固定前缀 * 批量修改 path新增固定前缀
* @param prefix 示例值/sso-user/sso-admin * @param prefix 示例值/sso-user/sso-admin
* @return 对象自身 * @return 对象自身
*/ */
@ -77,7 +77,7 @@ public class ApiName {
} }
/** /**
* 批量修改path替换掉 /sso 固定前缀 * 批量修改 path替换掉 /sso 固定前缀
* @param prefix 示例值/sso-user/sso-admin * @param prefix 示例值/sso-user/sso-admin
* @return 对象自身 * @return 对象自身
*/ */
@ -106,6 +106,7 @@ public class ApiName {
", ssoPushS='" + ssoPushS + '\'' + ", ssoPushS='" + ssoPushS + '\'' +
", ssoUserinfo='" + ssoUserinfo + '\'' + ", ssoUserinfo='" + ssoUserinfo + '\'' +
", ssoSignout='" + ssoSignout + '\'' + ", ssoSignout='" + ssoSignout + '\'' +
", ssoIsLogin='" + ssoIsLogin + '\'' +
", ssoLogin='" + ssoLogin + '\'' + ", ssoLogin='" + ssoLogin + '\'' +
", ssoLogout='" + ssoLogout + '\'' + ", ssoLogout='" + ssoLogout + '\'' +
", ssoLogoutCall='" + ssoLogoutCall + '\'' + ", ssoLogoutCall='" + ssoLogoutCall + '\'' +

View File

@ -23,40 +23,40 @@ package cn.dev33.satoken.sso.name;
*/ */
public class ParamName { public class ParamName {
/** redirect参数名称 */ /** redirect 参数名称 */
public String redirect = "redirect"; public String redirect = "redirect";
/** ticket参数名称 */ /** ticket 参数名称 */
public String ticket = "ticket"; public String ticket = "ticket";
/** back参数名称 */ /** back 参数名称 */
public String back = "back"; public String back = "back";
/** mode参数名称 */ /** mode 参数名称 */
public String mode = "mode"; public String mode = "mode";
/** loginId参数名称 */ /** 账号 id */
public String loginId = "loginId"; public String loginId = "loginId";
/** client参数名称 */ /** client 应用标识 */
public String client = "client"; public String client = "client";
/** tokenName 参数 */ /** token 名称 */
public String tokenName = "tokenName"; public String tokenName = "tokenName";
/** tokenValue 参数 */ /** token */
public String tokenValue = "tokenValue"; public String tokenValue = "tokenValue";
/** deviceId 参数名称 */ /** 设备 id */
public String deviceId = "deviceId"; public String deviceId = "deviceId";
/** secretkey参数名称 */ /** 接口参数签名秘钥 */
public String secretkey = "secretkey"; public String secretkey = "secretkey";
/** Client端单点注销时-回调URL 参数名称 */ /** Client 端单点注销时 - 回调 URL 参数名称 */
public String ssoLogoutCall = "ssoLogoutCall"; public String ssoLogoutCall = "ssoLogoutCall";
/** 是否为超过 maxRegClient 的自动注销 */ /** 是否为超过 maxRegClient 触发的自动注销 */
public String autoLogout = "autoLogout"; public String autoLogout = "autoLogout";
public String name = "name"; public String name = "name";
@ -72,13 +72,8 @@ public class ParamName {
/** token 剩余有效期 参数名称 */ /** token 剩余有效期 参数名称 */
public String remainTokenTimeout = "remainTokenTimeout"; public String remainTokenTimeout = "remainTokenTimeout";
/** singleDeviceIdLogout 参数 */ /** 是否单设备 id 注销 */
public String singleDeviceIdLogout = "singleDeviceIdLogout"; public String singleDeviceIdLogout = "singleDeviceIdLogout";
public String isLogin = "isLogin";
public String authUrl = "authUrl";
public String redirectUrl = "redirectUrl";
public String currSsoLoginUrl = "currSsoLoginUrl";
} }

View File

@ -70,22 +70,22 @@ public class SaSsoClientProcessor {
// ------------------ 路由分发 ------------------ // ------------------ 路由分发 ------------------
// ---------- SSO-Client端登录地址 // sso-client登录地址
if(req.isPath(apiName.ssoLogin)) { if(req.isPath(apiName.ssoLogin)) {
return ssoLogin(); return ssoLogin();
} }
// ---------- SSO-Client端单点注销 // sso-client单点注销
if(req.isPath(apiName.ssoLogout)) { if(req.isPath(apiName.ssoLogout)) {
return ssoLogout(); return ssoLogout();
} }
// ---------- SSO-Client端接收消息推送 // sso-client接收消息推送
if(req.isPath(apiName.ssoPushC)) { if(req.isPath(apiName.ssoPushC)) {
return ssoPushC(); return ssoPushC();
} }
// ---------- SSO-Client端单点注销的回调 [模式三] // sso-client单点注销的回调
if(req.isPath(apiName.ssoLogoutCall) && cfg.getRegLogoutCall()) { if(req.isPath(apiName.ssoLogoutCall) && cfg.getRegLogoutCall()) {
return ssoLogoutCall(); return ssoLogoutCall();
} }
@ -101,55 +101,18 @@ public class SaSsoClientProcessor {
public Object ssoLogin() { public Object ssoLogin() {
// 获取对象 // 获取对象
SaRequest req = SaHolder.getRequest(); SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
ApiName apiName = ssoClientTemplate.apiName;
ParamName paramName = ssoClientTemplate.paramName; ParamName paramName = ssoClientTemplate.paramName;
// 获取参数
String back = req.getParam(paramName.back, "/");
String ticket = req.getParam(paramName.ticket); String ticket = req.getParam(paramName.ticket);
/* /*
* 此时有两种情况: * 此时有两种情况:
* 情况1ticket无值说明此请求是Client端访问需要重定向至SSO认证中心 * 情况1ticket 无值说明此请求是 sso-client 端访问需要重定向至 sso-server 认证中心
* 情况2ticket有值说明此请求从SSO认证中心重定向而来需要根据ticket进行登录 * 情况2ticket 有值说明此请求从 sso-server 认证中心重定向而来需要根据 ticket 进行登录
*/ */
if(ticket == null) { if(ticket == null) {
// 如果当前Client端已经登录则无需访问SSO认证中心可以直接返回 return _goServerAuth();
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);
} else { } else {
// 1校验ticket获取 loginId return _loginByTicket();
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);
} }
} }
@ -162,9 +125,10 @@ public class SaSsoClientProcessor {
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
// 无论登录时选择的是模式二还是模式三 // 无论登录时选择的是模式二还是模式三
// 在注销时都应该按照模式三的方法通过 http 请求调用 sso-server 的单点注销接口来做到全端下线 // 在注销时都应该按照模式三的方法通过 http 请求调用 sso-server 的单点注销接口来做到全端下线
// 如果按照模式二的方法注销则会导致按照模式三登录的应用无法参与到单点注销环路中来
if(cfg.getIsSlo()) { if(cfg.getIsSlo()) {
return ssoLogoutByMode3(); return _ssoLogoutByMode3();
} }
// 默认返回 // 默认返回
@ -192,63 +156,6 @@ public class SaSsoClientProcessor {
return ssoClientTemplate.handleMessage(message); 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端单点注销的回调 [模式三] * SSO-Client端单点注销的回调 [模式三]
* @return 处理结果 * @return 处理结果
@ -265,8 +172,6 @@ public class SaSsoClientProcessor {
Object loginId = req.getParamNotNull(paramName.loginId); Object loginId = req.getParamNotNull(paramName.loginId);
loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId); loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(loginId);
String deviceId = req.getParam(paramName.deviceId); String deviceId = req.getParam(paramName.deviceId);
// String client = req.getParam(paramName.client);
// String autoLogout = req.getParam(paramName.autoLogout);
// 校验参数签名 // 校验参数签名
if(ssoConfig.getIsCheckSign()) { if(ssoConfig.getIsCheckSign()) {
@ -283,7 +188,109 @@ public class SaSsoClientProcessor {
return SaResult.ok("单点注销回调成功"); 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 无效则抛出异常 适用于模式二或模式三 * 封装校验ticket取出loginId如果 ticket 无效则抛出异常 适用于模式二或模式三
@ -294,70 +301,90 @@ public class SaSsoClientProcessor {
*/ */
public SaCheckTicketResult checkTicket(String ticket, String currUri) { public SaCheckTicketResult checkTicket(String ticket, String currUri) {
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig(); SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
// 两种模式
// isHttp=true模式三使用 http 请求从认证中心校验ticket
// isHttp=false模式二直连 redis 中校验 ticket
if(cfg.getIsHttp()) {
return _checkTicketByHttp(ticket, currUri);
} else {
return _checkTicketByRedis(ticket);
}
}
/**
* 校验 tickethttp 请求方式
* @param ticket /
* @param currUri /
* @return /
*/
public SaCheckTicketResult _checkTicketByHttp(String ticket, String currUri) {
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
ApiName apiName = ssoClientTemplate.apiName; ApiName apiName = ssoClientTemplate.apiName;
ParamName paramName = ssoClientTemplate.paramName; ParamName paramName = ssoClientTemplate.paramName;
// --------- 两种模式 // 计算当前 sso-client 的单点注销回调地址
if(cfg.getIsHttp()) { String ssoLogoutCall = null;
// q1使用模式三使用 http 请求从认证中心校验ticket if(cfg.getRegLogoutCall()) {
// 如果配置了回调地址就使用配置的值
// 计算当前 sso-client 的单点注销回调地址 if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) {
String ssoLogoutCall = null; ssoLogoutCall = cfg.getCurrSsoLogoutCall();
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 {
}
} }
// 如果提供了当前 uri则根据此值来计算
// 发起请求 else if(SaFoxUtil.isNotEmpty(currUri)) {
SaSsoMessage message = ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCall); ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall);
SaResult result = ssoClientTemplate.pushMessageAsSaResult(message); }
// 否则视为不注册单点注销回调地址
// 校验 else {
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);
} }
} 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对象 * @param res SaResponse对象
* @return 返回结果 * @return 返回结果
*/ */
public Object ssoLogoutBack(SaRequest req, SaResponse res) { public Object _ssoLogoutBack(SaRequest req, SaResponse res) {
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoClientTemplate.paramName); return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoClientTemplate.paramName);
} }
} }

View File

@ -17,8 +17,8 @@ package cn.dev33.satoken.sso.processor;
import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse; 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.name.ParamName;
import cn.dev33.satoken.sso.util.SaSsoConsts;
import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;

View File

@ -57,13 +57,13 @@ public class SaSsoServerProcessor {
/** /**
* 分发 Server 端所有请求 * 分发 Server 端所有请求
*
* @return 处理结果 * @return 处理结果
*/ */
public Object dister() { public Object dister() {
// 获取对象 // 获取对象
SaRequest req = SaHolder.getRequest(); SaRequest req = SaHolder.getRequest();
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
ApiName apiName = ssoServerTemplate.apiName; ApiName apiName = ssoServerTemplate.apiName;
// ------------------ 路由分发 ------------------ // ------------------ 路由分发 ------------------
@ -104,36 +104,44 @@ public class SaSsoServerProcessor {
StpLogic stpLogic = ssoServerTemplate.getStpLogic(); StpLogic stpLogic = ssoServerTemplate.getStpLogic();
ParamName paramName = ssoServerTemplate.paramName; ParamName paramName = ssoServerTemplate.paramName;
// ---------- 此处有两种情况分开处理 // 两种情况
// ---- 情况1在SSO认证中心尚未登录需要先去登录 // 情况1 SSO 认证中心尚未登录需要显示登录视图去登录
// 情况2 SSO 认证中心已经登录需要重定向回 Client
// 情况1显示登录视图
if( ! stpLogic.isLogin()) { if( ! stpLogic.isLogin()) {
return ssoServerTemplate.strategy.notLoginView.get(); return ssoServerTemplate.strategy.notLoginView.get();
} }
// ---- 情况2在SSO认证中心已经登录需要重定向回 Client 而这又分为两种方式
// 情况2开始跳转
String mode = req.getParam(paramName.mode, SaSsoConsts.MODE_TICKET); String mode = req.getParam(paramName.mode, SaSsoConsts.MODE_TICKET);
String redirect = req.getParam(paramName.redirect); String redirect = req.getParam(paramName.redirect);
String client = req.getParam(paramName.client); 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(() -> { 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)) { if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
ssoServerTemplate.checkRedirectUrl(client, redirect); ssoServerTemplate.checkRedirectUrl(client, redirect);
return redirect; return redirect;
} else { } else {
// 方式2带着 ticket 参数重定向回Client端 (mode=ticket) // 方式2带着 ticket 参数重定向回Client端 (mode=ticket一般是模式二)
// 构建并跳转 // 构建并跳转
String _redirectUrl = ssoServerTemplate.buildRedirectUrl(client, redirect, stpLogic.getLoginId(), stpLogic.getTokenValue()); String _redirectUrl = ssoServerTemplate.buildRedirectUrl(client, redirect, stpLogic.getLoginId(), stpLogic.getTokenValue());
// 构建成功说明 redirect 地址合法此时需要更新一下该账号的Session有效期
// 构建成功说明 redirect 地址合法此时需要更新一下当前 token 有效期
if(cfg.getAutoRenewTimeout()) { if(cfg.getAutoRenewTimeout()) {
stpLogic.renewTimeout(stpLogic.getConfigOrGlobal().getTimeout()); stpLogic.renewTimeout(stpLogic.getConfigOrGlobal().getTimeout());
} }
@ -151,13 +159,14 @@ public class SaSsoServerProcessor {
* @return 处理结果 * @return 处理结果
*/ */
public Object ssoDoLogin() { public Object ssoDoLogin() {
// 获取对象 // 获取参数
SaRequest req = SaHolder.getRequest(); SaRequest req = SaHolder.getRequest();
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
ParamName paramName = ssoServerTemplate.paramName; 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(); SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse(); 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"); boolean singleDeviceIdLogout = req.isParam(ssoServerTemplate.paramName.singleDeviceIdLogout, "true");
// 单点注销 // 单点注销
if(SaFoxUtil.isNotEmpty(loginId)) { if(SaFoxUtil.isNotEmpty(loginId)) {
SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter(); SaLogoutParameter logoutParameter = stpLogic.createSaLogoutParameter();
if(singleDeviceIdLogout) { if(singleDeviceIdLogout) {
logoutParameter.setDeviceId(ssoServerTemplate.getStpLogic().getLoginDeviceId()); logoutParameter.setDeviceId(stpLogic.getLoginDeviceId());
} }
ssoServerTemplate.ssoLogout(loginId, logoutParameter); ssoServerTemplate.ssoLogout(loginId, logoutParameter);
} }
@ -202,7 +212,7 @@ public class SaSsoServerProcessor {
return SaResult.error("无效 client 标识:" + client); return SaResult.error("无效 client 标识:" + client);
} }
// 3校验签名 // 3校验参数签名
Map<String, String> paramMap = req.getParamMap(); Map<String, String> paramMap = req.getParamMap();
if(ssoServerConfig.getIsCheckSign()) { if(ssoServerConfig.getIsCheckSign()) {
ssoServerTemplate.getSignTemplate(client).checkParamMap(paramMap); ssoServerTemplate.getSignTemplate(client).checkParamMap(paramMap);
@ -210,10 +220,10 @@ public class SaSsoServerProcessor {
SaSsoManager.printNoCheckSignWarningByRuntime(); SaSsoManager.printNoCheckSignWarningByRuntime();
} }
// 处理消息 // 4处理消息
SaSsoMessage message = new SaSsoMessage(paramMap); SaSsoMessage message = new SaSsoMessage(paramMap);
if( ! ssoServerTemplate.messageHolder.hasHandle(message.getType())) { if( ! ssoServerTemplate.messageHolder.hasHandle(message.getType())) {
return SaResult.error("未能找到消息处理器: " + message.getType()); return SaResult.error("未能找到消息处理器" + message.getType());
} }
return ssoServerTemplate.handleMessage(message); return ssoServerTemplate.handleMessage(message);
} }

View File

@ -19,6 +19,9 @@ import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.fun.SaParamRetFunction; import cn.dev33.satoken.fun.SaParamRetFunction;
import cn.dev33.satoken.sso.function.SendHttpFunction; import cn.dev33.satoken.sso.function.SendHttpFunction;
import cn.dev33.satoken.sso.function.TicketResultHandleFunction; import cn.dev33.satoken.sso.function.TicketResultHandleFunction;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/** /**
* Sa-Token SSO Client 相关策略 * Sa-Token SSO Client 相关策略
@ -62,4 +65,16 @@ public class SaSsoClientStrategy {
return loginId; return loginId;
}; };
/**
* 发送 Http 请求并将响应结果转换为 SaResult
*
* @param url 请求地址
* @return 返回的结果
*/
public SaResult requestAsSaResult(String url) {
String body = sendHttp.apply(url);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
} }

View File

@ -23,6 +23,8 @@ import cn.dev33.satoken.sso.function.NotLoginViewFunction;
import cn.dev33.satoken.sso.function.SendHttpFunction; import cn.dev33.satoken.sso.function.SendHttpFunction;
import cn.dev33.satoken.util.SaResult; import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/** /**
* Sa-Token SSO Server 相关策略 * Sa-Token SSO Server 相关策略
* *
@ -66,4 +68,16 @@ public class SaSsoServerStrategy {
return result; return result;
}; };
/**
* 发送 Http 请求并将响应结果转换为 SaResult
*
* @param url 请求地址
* @return 返回的结果
*/
public SaResult requestAsSaResult(String url) {
String body = sendHttp.apply(url);
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
} }

View File

@ -33,7 +33,7 @@ import cn.dev33.satoken.util.SaResult;
import java.util.Map; import java.util.Map;
/** /**
* Sa-Token SSO 模板方法类 Client端 * SSO 模板方法类 Client端
* *
* @author click33 * @author click33
* @since 1.38.0 * @since 1.38.0
@ -50,10 +50,11 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
} }
// ------------------- SSO 模式三相关 ------------------- // ------------------- getData 相关 -------------------
/** /**
* 根据配置的 getData 地址查询数据 * 根据配置的 getData 地址查询数据
*
* @param paramMap 查询参数 * @param paramMap 查询参数
* @return 查询结果 * @return 查询结果
*/ */
@ -64,6 +65,7 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
/** /**
* 根据自定义 path 地址查询数据 此方法需要配置 sa-token.sso.server-url 地址 * 根据自定义 path 地址查询数据 此方法需要配置 sa-token.sso.server-url 地址
*
* @param path 自定义 path * @param path 自定义 path
* @param paramMap 查询参数 * @param paramMap 查询参数
* @return 查询结果 * @return 查询结果
@ -73,44 +75,6 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return strategy.sendHttp.apply(url); return strategy.sendHttp.apply(url);
} }
// ---------------------- 构建URL ----------------------
/**
* 构建URLServer端 单点登录地址
* @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);
}
/** /**
* 构建URLServer端 getData 地址带签名等参数 * 构建URLServer端 getData 地址带签名等参数
* @param paramMap 查询参数 * @param paramMap 查询参数
@ -146,55 +110,49 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return SaFoxUtil.joinParam(url, signParamsStr); 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;
}
/** /**
* 构建消息单点注销 * 构建URLServer端 单点登录授权地址
* * <br/> 形如http://sso-server.com/sso/auth?redirectUrl=http://sso-client.com/sso/login?back=http://sso-client.com
* @param loginId 要注销的账号 id * @param clientLoginUrl Client端登录地址
* @param logoutParameter 单点注销 * @param back 回调路径
* @return 单点注销URL * @return [SSO-Server端-认证地址 ]
*/ */
public SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) { public String buildServerAuthUrl(String clientLoginUrl, String back) {
SaSsoClientConfig ssoConfig = getClientConfig(); SaSsoClientConfig ssoConfig = getClientConfig();
SaSsoMessage message = new SaSsoMessage();
message.setType(SaSsoConsts.MESSAGE_SIGNOUT); // 服务端认证地址
message.set(paramName.client, getClient()); String serverUrl = ssoConfig.splicingAuthUrl();
message.set(paramName.loginId, loginId);
message.set(paramName.deviceId, logoutParameter.getDeviceId()); // 拼接客户端标识
return message; 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<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
/** /**
* sso-server 推送消息 * sso-server 推送消息
* *
@ -230,6 +188,40 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
return new SaResult(map); 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 对象获取 ------------------- // ------------------- Bean 对象获取 -------------------

View File

@ -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<String, Object> 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<String, Object> paramMap) {
return SaSsoClientProcessor.instance.ssoClientTemplate.getData(path, paramMap);
}
// ---------------------- 构建交互地址 ----------------------
/**
* 构建URLServer端 单点登录授权地址
* <br/> 形如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);
}
}

View File

@ -39,7 +39,7 @@ import cn.dev33.satoken.util.SaResult;
import java.util.*; import java.util.*;
/** /**
* Sa-Token SSO 模板方法类 Server端 * SSO 模板方法类 Server端
* *
* @author click33 * @author click33
* @since 1.38.0 * @since 1.38.0
@ -58,40 +58,17 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
// ---------------------- Ticket 操作 ---------------------- // ---------------------- Ticket 操作 ----------------------
// 增删改
/** /**
* 保存 Ticket * 保存 Ticket
* @param ticketModel / * @param ticketModel /
*/ */
public void saveTicket(TicketModel ticketModel) { public void saveTicket(TicketModel ticketModel) {
long ticketTimeout = getServerConfig().getTicketTimeout(); 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 * 删除 Ticket
* @param ticket Ticket码 * @param ticket Ticket码
@ -100,36 +77,59 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
if(ticket == null) { if(ticket == null) {
return; return;
} }
SaManager.getSaTokenDao().deleteObject(splicingTicketSaveKey(ticket)); SaManager.getSaTokenDao().deleteObject(splicingTicketModelSaveKey(ticket));
} }
/** /**
* 删除 Ticket索引 * 根据参数创建一个 ticket
* *
* @param client 应用标识 * @param client 客户端标识
* @param loginId 账号id * @param loginId 账号 id
* @param tokenValue 会话 Token
* @return Ticket码
*/ */
public void deleteTicketIndex(String client, Object loginId) { public TicketModel createTicket(String client, Object loginId, String tokenValue) {
if(loginId == null) { TicketModel ticketModel = new TicketModel();
return; ticketModel.setTicket(randomTicket(loginId));
} ticketModel.setClient(client);
SaManager.getSaTokenDao().delete(splicingTicketIndexKey(client, loginId)); ticketModel.setLoginId(loginId);
ticketModel.setTokenValue(tokenValue);
return ticketModel;
} }
// /** /**
// * 删除 Ticket 关联的 client * 根据参数创建一个 ticket 并保存
// * *
// * @param ticket Ticket码 * @param client 客户端标识
// */ * @param loginId 账号 id
// public void deleteTicketToClient(String ticket) { * @param tokenValue 会话 Token
// if(ticket == null) { * @return Ticket码
// return; */
// } public String createTicketAndSave(String client, Object loginId, String tokenValue) {
// SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket)); // 创建
// } 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码 * @param ticket Ticket码
* @return 账号id * @return 账号id
@ -138,7 +138,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
if(SaFoxUtil.isEmpty(ticket)) { if(SaFoxUtil.isEmpty(ticket)) {
return null; 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); return SaFoxUtil.getValueByType(getLoginId(ticket), cs);
} }
/** // 校验
* 查询 指定 clientloginId 其所属的 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 会抛出异常 * 校验 Ticket无效 ticket 会抛出异常
@ -271,13 +221,45 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
return ticketModel; return ticketModel;
} }
// ticket 索引
/** /**
* 随机一个 Ticket码 * 保存 Ticket 索引 id 反查 ticket
*
* @param client 应用端
* @param ticket ticket码
* @param loginId 账号id * @param loginId 账号id
* @return Ticket码
*/ */
public String randomTicket(Object loginId) { public void saveTicketIndex(String client, Object loginId, String ticket) {
return SaFoxUtil.getRandomString(64); 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));
}
/**
* 查询 指定 clientloginId 其所属的 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 / * @return /
*/ */
public List<SaSsoClientModel> getClients() { public List<SaSsoClientModel> getClients() {
Map<String, SaSsoClientModel> clients = getServerConfig().getClients(); return new ArrayList<>(getServerConfig().getClients().values());
return new ArrayList<>(clients.values());
} }
/** /**
@ -351,7 +332,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
List<SaSsoClientModel> list = new ArrayList<>(); List<SaSsoClientModel> list = new ArrayList<>();
List<SaSsoClientModel> clients = getClients(); List<SaSsoClientModel> clients = getClients();
for(SaSsoClientModel scm : clients) { for(SaSsoClientModel scm : clients) {
if (scm.isValidNoticeUrl()) { if (scm.getIsPush()) {
list.add(scm); list.add(scm);
} }
} }
@ -379,14 +360,15 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
deleteTicket(getTicketValue(client, loginId)); deleteTicket(getTicketValue(client, loginId));
// 创建 新Ticket // 创建 新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); return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket);
} }
/** /**
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug * url 中的 back 参数进行 URL 编码, 解决超链接重定向后参数丢失的 bug
*
* @param url url * @param url url
* @return 编码过后的url * @return 编码过后的url
*/ */
@ -412,7 +394,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
} }
/** /**
* 校验重定向url合法性 * 校验重定向 url 合法性
* *
* @param client 应用标识 * @param client 应用标识
* @param url 下放ticket的url地址 * @param url 下放ticket的url地址
@ -513,7 +495,8 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
// ------------------- 单点注销 ------------------- // ------------------- 单点注销 -------------------
/** /**
* 为指定账号 id 注册单点注销回调信息模式三 * 为指定账号 id 注册应用接入信息模式三
*
* @param loginId 账号id * @param loginId 账号id
* @param client 指定客户端标识可为null * @param client 指定客户端标识可为null
* @param sloCallbackUrl 单点注销时的回调URL * @param sloCallbackUrl 单点注销时的回调URL
@ -539,7 +522,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
for (;;) { for (;;) {
if(scmList.size() > maxRegClient) { if(scmList.size() > maxRegClient) {
SaSsoClientInfo removeScm = scmList.remove(0); SaSsoClientInfo removeScm = scmList.remove(0);
notifyClientLogout(loginId, null, removeScm, true); notifyClientLogout(loginId, null, removeScm, true, true);
} else { } else {
break; break;
} }
@ -591,7 +574,6 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
public void ssoLogout(Object loginId, SaLogoutParameter logoutParameter) { public void ssoLogout(Object loginId, SaLogoutParameter logoutParameter) {
// 1消息推送单点注销 // 1消息推送单点注销
// TODO 需要把对应的 SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_ 记录也删掉
pushToAllClientByLogoutCall(loginId, logoutParameter); pushToAllClientByLogoutCall(loginId, logoutParameter);
// 2SaSession 挂载的 Client 端注销会话 // 2SaSession 挂载的 Client 端注销会话
@ -601,7 +583,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
} }
List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new); List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
scmList.forEach(scm -> { scmList.forEach(scm -> {
notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false); notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false, false);
}); });
// 3Server 端本身注销 // 3Server 端本身注销
@ -614,17 +596,22 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
* @param deviceId 指定设备 id * @param deviceId 指定设备 id
* @param scm 客户端信息对象 * @param scm 客户端信息对象
* @param autoLogout 是否为超过 maxRegClient 的自动注销 * @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值不进行任何操作 // 如果给个null值不进行任何操作
if(scm == null || scm.mode != SaSsoConsts.SSO_MODE_3) { if(scm == null || scm.mode != SaSsoConsts.SSO_MODE_3) {
return; return;
} }
// 如果此 Client 并没有注册 单点登录 回调地址则立即返回 // 如果此 Client 并没有注册 单点注销 回调地址
String sloCallUrl = scm.getSloCallbackUrl(); String sloCallUrl = scm.getSloCallbackUrl();
if(SaFoxUtil.isEmpty(sloCallUrl)) { if(SaFoxUtil.isEmpty(sloCallUrl)) {
// TODO 代码有效性待验证
if(isPushWork && SaFoxUtil.isNotEmpty(scm.getClient())) {
pushToClientByLogoutCall(getClient(scm.getClient()), loginId, getStpLogic().createSaLogoutParameter());
}
return; 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<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
return new SaResult(map);
}
/** /**
* 向指定 Client 推送消息 * 向指定 Client 推送消息
* @param clientModel / * @param clientModel /
@ -666,7 +641,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
*/ */
public String pushMessage(SaSsoClientModel clientModel, SaSsoMessage message) { public String pushMessage(SaSsoClientModel clientModel, SaSsoMessage message) {
message.checkType(); message.checkType();
String noticeUrl = clientModel.splicingNoticeUrl(); String noticeUrl = clientModel.splicingPushUrl();
String paramsStr = getSignTemplate(clientModel.getClient()).addSignParamsAndJoin(message); String paramsStr = getSignTemplate(clientModel.getClient()).addSignParamsAndJoin(message);
String finalUrl = SaFoxUtil.joinParam(noticeUrl, paramsStr); String finalUrl = SaFoxUtil.joinParam(noticeUrl, paramsStr);
return strategy.sendHttp.apply(finalUrl); return strategy.sendHttp.apply(finalUrl);
@ -714,8 +689,8 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
* @param message / * @param message /
*/ */
public void pushToAllClient(SaSsoMessage message) { public void pushToAllClient(SaSsoMessage message) {
List<SaSsoClientModel> mode3Clients = getNeedPushClients(); List<SaSsoClientModel> needPushClients = getNeedPushClients();
for (SaSsoClientModel client : mode3Clients) { for (SaSsoClientModel client : needPushClients) {
pushMessage(client, message); pushMessage(client, message);
} }
} }
@ -730,15 +705,25 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
List<SaSsoClientModel> npClients = getNeedPushClients(); List<SaSsoClientModel> npClients = getNeedPushClients();
for (SaSsoClientModel client : npClients) { for (SaSsoClientModel client : npClients) {
if(client.getIsSlo()) { if(client.getIsSlo()) {
SaSsoMessage message = new SaSsoMessage(); pushToClientByLogoutCall(client, loginId, logoutParameter);
message.setType(SaSsoConsts.MESSAGE_LOGOUT_CALL);
message.set(paramName.loginId, loginId);
message.set(paramName.deviceId, logoutParameter.getDeviceId());
pushMessage(client, message);
} }
} }
} }
/**
* 向指定 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 获取 ------------------- // ------------------- Bean 获取 -------------------
@ -776,25 +761,16 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
// ------------------- 返回相应key ------------------- // ------------------- 返回相应key -------------------
/** /**
* 拼接keyTicket 账号Id * 拼接keyTicketModel
* @param ticket ticket值 * @param ticket ticket值
* @return key * @return key
*/ */
public String splicingTicketSaveKey(String ticket) { public String splicingTicketModelSaveKey(String ticket) {
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket; return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
} }
// /**
// * 拼接keyTicket 所属的 client
// * @param ticket ticket值
// * @return key
// */
// public String splicingTicketToClientSaveKey(String ticket) {
// return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket;
// }
/** /**
* 拼接key账号Id 反查 Ticket * 拼接keyTicket 索引
* *
* @param client 应用标识 * @param client 应用标识
* @param id 账号id * @param id 账号id
@ -804,7 +780,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) { if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
client = SaSsoConsts.CLIENT_ANON; client = SaSsoConsts.CLIENT_ANON;
} }
return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + client + ":" + id; return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-index:" + client + ":" + id;
} }
} }

View File

@ -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 <T> 要转换的类型
* @param ticket Ticket码
* @param cs 要转换的类型
* @return 账号id
*/
public static <T> T getLoginId(String ticket, Class<T> 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 索引
/**
* 查询 指定 clientloginId 其所属的 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<SaSsoClientModel> 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<SaSsoClientModel> getNeedPushClients() {
return SaSsoServerProcessor.instance.ssoServerTemplate.getNeedPushClients();
}
// ------------------- 重定向 URL 构建与校验 -------------------
/**
* 构建 URLsso-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);
}
}

View File

@ -15,19 +15,15 @@
*/ */
package cn.dev33.satoken.sso.template; package cn.dev33.satoken.sso.template;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.message.SaSsoMessage;
import cn.dev33.satoken.sso.message.SaSsoMessageHolder; import cn.dev33.satoken.sso.message.SaSsoMessageHolder;
import cn.dev33.satoken.sso.name.ApiName; import cn.dev33.satoken.sso.name.ApiName;
import cn.dev33.satoken.sso.name.ParamName; import cn.dev33.satoken.sso.name.ParamName;
import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/** /**
* Sa-Token SSO 模板方法类 公共端 * SSO 模板方法类 公共端
* *
* @author click33 * @author click33
* @since 1.30.0 * @since 1.30.0
@ -74,18 +70,11 @@ public class SaSsoTemplate {
// ----------- 消息处理 // ----------- 消息处理
/**
* SSO 消息处理器 - 持有器
*/
public SaSsoMessageHolder messageHolder = new SaSsoMessageHolder(); 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); return messageHolder.handleMessage(this, message);
} }
} }

View File

@ -23,10 +23,13 @@ import java.util.Map;
/** /**
* Sa-Token-SSO 单点登录模块 工具类 * Sa-Token-SSO 单点登录模块 工具类
*
* <h2> 请更换为 SaSsoServerUtil SaSsoClientUtil <h2/>
* *
* @author click33 * @author click33
* @since 1.30.0 * @since 1.30.0
*/ */
@Deprecated
public class SaSsoUtil { public class SaSsoUtil {
// ---------------------- Ticket 操作 ---------------------- // ---------------------- Ticket 操作 ----------------------
@ -40,7 +43,7 @@ public class SaSsoUtil {
* @return Ticket码 * @return Ticket码
*/ */
public static String createTicket(String client, Object loginId, String deviceId) { 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);
} }
/** /**

View File

@ -58,9 +58,6 @@ public class SaSsoConsts {
/** SSO 模式3 */ /** SSO 模式3 */
public static final int SSO_MODE_3 = 3; public static final int SSO_MODE_3 = 3;
// /** 消息类型:单点注销 */
// public static final String MESSAGE_LOGOUT = "logout";
/** 消息类型:校验 ticket */ /** 消息类型:校验 ticket */
public static final String MESSAGE_CHECK_TICKET = "checkTicket"; public static final String MESSAGE_CHECK_TICKET = "checkTicket";