mirror of
https://gitee.com/dromara/sa-token.git
synced 2024-12-02 11:57:40 +08:00
更换单点注销接口响应格式
This commit is contained in:
parent
e320a8e45b
commit
5e3795e29e
@ -0,0 +1,22 @@
|
||||
package cn.dev33.satoken.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 JSON 转换失败
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
public class SaJsonConvertException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 6806129545290134144L;
|
||||
|
||||
/**
|
||||
* 一个异常:代表 JSON 转换失败
|
||||
*/
|
||||
public SaJsonConvertException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
}
|
@ -21,12 +21,34 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
||||
public static final int CODE_SUCCESS = 200;
|
||||
public static final int CODE_ERROR = 500;
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
public SaResult() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建
|
||||
* @param code 状态码
|
||||
* @param msg 信息
|
||||
* @param data 数据
|
||||
*/
|
||||
public SaResult(int code, String msg, Object data) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
this.setData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Map 快速构建
|
||||
* @param map /
|
||||
*/
|
||||
public SaResult(Map<String, Object> map) {
|
||||
for (String key: map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取code
|
||||
* @return code
|
||||
@ -138,9 +160,16 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
||||
public String toString() {
|
||||
return "{"
|
||||
+ "\"code\": " + this.getCode()
|
||||
+ ", \"msg\": \"" + this.getMsg() + "\""
|
||||
+ ", \"data\": \"" + this.getData() + "\""
|
||||
+ ", \"msg\": " + transValue(this.getMsg())
|
||||
+ ", \"data\": " + transValue(this.getData())
|
||||
+ "}";
|
||||
}
|
||||
|
||||
private String transValue(Object value) {
|
||||
if(value instanceof String) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,8 +37,9 @@ http://{host}:{port}/sso/doLogin
|
||||
| name | 是 | 用户名 |
|
||||
| pwd | 是 | 密码 |
|
||||
|
||||
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `setDoLoginHandle` 函数中,另外需要注意:
|
||||
此接口并非只能携带 name、pwd 参数,因为你可以在 setDoLoginHandle 函数里通过 `SaHolder.getRequest().getParam("xxx")` 来获取其它参数。
|
||||
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `sso.setDoLoginHandle` 函数中,此函数的返回值即是此接口的响应值。
|
||||
|
||||
另外需要注意:此接口并非只能携带 name、pwd 参数,因为你可以在方法里通过 `SaHolder.getRequest().getParam("xxx")` 来获取前端提交的其它参数。
|
||||
|
||||
|
||||
### 3、Ticket 校验接口
|
||||
@ -52,12 +53,29 @@ http://{host}:{port}/sso/checkTicket
|
||||
|
||||
| 参数 | 是否必填 | 说明 |
|
||||
| :-------- | :-------- | :-------- |
|
||||
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
|
||||
| ticket | 是 | 在步骤 1 中授权重定向时的 ticket 参数 |
|
||||
| ssoLogoutCall | 否 | 单点注销时的回调通知地址,只在SSO模式三单点注销时需要携带此参数|
|
||||
|
||||
返回值场景:
|
||||
- 返回空,代表校验失败。
|
||||
- 返回具体的 loginId,例如10001,代表校验成功,值为此 ticket 码代表的用户id。
|
||||
- 校验成功时:
|
||||
|
||||
``` js
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": "10001" // 此 ticket 指向的 loginId
|
||||
}
|
||||
```
|
||||
|
||||
- 校验失败时:
|
||||
|
||||
``` js
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "无效ticket:vESj0MtqrtSoucz4DDHJnsqU3u7AKFzbj0KH57EfJvuhkX1uAH23DuNrMYSjTnEq",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 4、单点注销接口
|
||||
@ -90,6 +108,17 @@ http://{host}:{port}/sso/logout
|
||||
}
|
||||
```
|
||||
|
||||
如果单点注销失败,将返回:
|
||||
|
||||
``` js
|
||||
{
|
||||
"code": 500, // 200表示请求成功,非200标识请求失败
|
||||
"msg": "无效秘钥:xxx", // 失败原因
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
SSO 认证中心只有这四个接口,接下来让我一起来看一下 Client 端的对接流程:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
|
||||
|
@ -279,12 +279,16 @@ public class SaSsoConfig implements Serializable {
|
||||
/**
|
||||
* SSO-Server端:未登录时返回的View
|
||||
*/
|
||||
public Supplier<Object> notLoginView = () -> "当前会话在SSO-Server认证中心尚未登录";
|
||||
public Supplier<Object> notLoginView = () -> {
|
||||
return "当前会话在SSO-Server认证中心尚未登录";
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Server端:登录函数
|
||||
*/
|
||||
public BiFunction<String, String, Object> doLoginHandle = (name, pwd) -> SaResult.error();
|
||||
public BiFunction<String, String, Object> doLoginHandle = (name, pwd) -> {
|
||||
return SaResult.error();
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
@ -294,7 +298,9 @@ public class SaSsoConfig implements Serializable {
|
||||
/**
|
||||
* SSO-Client端:发送Http请求的处理函数
|
||||
*/
|
||||
public Function<String, Object> sendHttp = url -> {throw new SaTokenException("请配置Http处理器");};
|
||||
public Function<String, String> sendHttp = url -> {
|
||||
throw new SaTokenException("请配置 Http 请求处理器");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@ -349,7 +355,7 @@ public class SaSsoConfig implements Serializable {
|
||||
* @param sendHttp SSO-Client端:发送Http请求的处理函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSendHttp(Function<String, Object> sendHttp) {
|
||||
public SaSsoConfig setSendHttp(Function<String, String> sendHttp) {
|
||||
this.sendHttp = sendHttp;
|
||||
return this;
|
||||
}
|
||||
@ -357,7 +363,7 @@ public class SaSsoConfig implements Serializable {
|
||||
/**
|
||||
* @return 函数 SSO-Client端:发送Http请求的处理函数
|
||||
*/
|
||||
public Function<String, Object> getSendHttp() {
|
||||
public Function<String, String> getSendHttp() {
|
||||
return sendHttp;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
package cn.dev33.satoken.sso;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
@ -19,6 +22,8 @@ import cn.dev33.satoken.util.SaResult;
|
||||
*/
|
||||
public class SaSsoHandle {
|
||||
|
||||
// ----------- SSO-Server 端路由分发
|
||||
|
||||
/**
|
||||
* 处理Server端所有请求
|
||||
* @return 处理结果
|
||||
@ -46,14 +51,14 @@ public class SaSsoHandle {
|
||||
return ssoCheckTicket();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [模式一] (不带loginId参数)
|
||||
// SSO-Server端:单点注销 [用户访问式] (不带loginId参数)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.getIsSlo() && req.hasParam(ParamName.loginId) == false) {
|
||||
return ssoServerLogoutType1();
|
||||
return ssoLogoutByUserVisit();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [模式三] (带loginId参数)
|
||||
// SSO-Server端:单点注销 [Client调用式] (带loginId参数 & isHttp=true)
|
||||
if(req.isPath(Api.ssoLogout) && cfg.getIsHttp() && cfg.getIsSlo() && req.hasParam(ParamName.loginId)) {
|
||||
return ssoServerLogout();
|
||||
return ssoLogoutByClientHttp();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
@ -116,38 +121,47 @@ public class SaSsoHandle {
|
||||
String ticket = req.getParam(ParamName.ticket);
|
||||
String sloCallback = req.getParam(ParamName.ssoLogoutCall);
|
||||
|
||||
// 校验ticket,获取对应的账号id
|
||||
// 校验ticket,获取 loginId
|
||||
Object loginId = SaSsoUtil.checkTicket(ticket);
|
||||
|
||||
// 注册此客户端的单点注销回调URL
|
||||
SaSsoUtil.registerSloCallbackUrl(loginId, sloCallback);
|
||||
|
||||
// 返回给Client端
|
||||
return loginId;
|
||||
// 给 client 端响应结果
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
} else {
|
||||
return SaResult.data(loginId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [模式一]
|
||||
* SSO-Server端:单点注销 [用户访问式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogoutType1() {
|
||||
public static Object ssoLogoutByUserVisit() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
String loginId = req.getParam(ParamName.loginId);
|
||||
|
||||
// 开始处理
|
||||
// step.1 遍历 Client 端注销
|
||||
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.getSendHttp().apply(url));
|
||||
|
||||
// step.2 Server 端注销
|
||||
stpLogic.logout();
|
||||
|
||||
// 返回
|
||||
// 完成
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [模式三]
|
||||
* SSO-Server端:单点注销 [Client调用式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public static Object ssoServerLogout() {
|
||||
public static Object ssoLogoutByClientHttp() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
@ -157,21 +171,22 @@ public class SaSsoHandle {
|
||||
String loginId = req.getParam(ParamName.loginId);
|
||||
String secretkey = req.getParam(ParamName.secretkey);
|
||||
|
||||
// 遍历通知Client端注销会话
|
||||
// step.1 校验秘钥
|
||||
SaSsoUtil.checkSecretkey(secretkey);
|
||||
|
||||
// step.2 遍历通知Client端注销会话
|
||||
// step.2 遍历 Client 端注销
|
||||
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.getSendHttp().apply(url));
|
||||
|
||||
// step.3 Server端注销
|
||||
// step.3 Server 端注销
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 完成
|
||||
return SaSsoConsts.OK;
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
||||
// ----------- SSO-Client 端路由分发
|
||||
|
||||
/**
|
||||
* 处理Client端所有请求
|
||||
* @return 处理结果
|
||||
@ -236,20 +251,21 @@ public class SaSsoHandle {
|
||||
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(SaHolder.getRequest().getUrl(), back);
|
||||
return res.redirect(serverAuthUrl);
|
||||
} else {
|
||||
// ------- 1、校验ticket,获取账号id
|
||||
// ------- 1、校验ticket,获取 loginId
|
||||
Object loginId = checkTicket(ticket, Api.ssoLogin);
|
||||
|
||||
// Be: 如果开发者自定义了处理逻辑
|
||||
if(cfg.getTicketResultHandle() != null) {
|
||||
return cfg.getTicketResultHandle().apply(loginId, back);
|
||||
}
|
||||
// ------- 2、如果loginId有值,说明ticket有效,进行登录并重定向至back地址
|
||||
if(loginId != null ) {
|
||||
|
||||
// ------- 2、如果 loginId 无值,说明 ticket 无效
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoExceptionCode.CODE_20004);
|
||||
} else {
|
||||
// 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址
|
||||
stpLogic.login(loginId);
|
||||
return res.redirect(back);
|
||||
} else {
|
||||
// 如果ticket无效:
|
||||
throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoExceptionCode.CODE_20004);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,7 +295,6 @@ public class SaSsoHandle {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||
|
||||
// 如果未登录,则无需注销
|
||||
@ -289,13 +304,15 @@ public class SaSsoHandle {
|
||||
|
||||
// 调用SSO-Server认证中心API,进行注销
|
||||
String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId());
|
||||
String body = String.valueOf(cfg.getSendHttp().apply(url));
|
||||
if(SaSsoConsts.OK.equals(body) == false) {
|
||||
return SaResult.error("单点注销失败");
|
||||
}
|
||||
SaResult result = request(url);
|
||||
|
||||
// 返回
|
||||
// 校验
|
||||
if(result.getCode() == SaResult.CODE_SUCCESS) {
|
||||
return ssoLogoutBack(req, res);
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoExceptionCode.CODE_20006);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,11 +328,17 @@ public class SaSsoHandle {
|
||||
String loginId = req.getParam(ParamName.loginId);
|
||||
String secretkey = req.getParam(ParamName.secretkey);
|
||||
|
||||
// 注销当前应用端会话
|
||||
SaSsoUtil.checkSecretkey(secretkey);
|
||||
stpLogic.logout(loginId);
|
||||
return SaSsoConsts.OK;
|
||||
|
||||
// 响应
|
||||
return SaResult.ok("单点注销回调成功");
|
||||
}
|
||||
|
||||
|
||||
// ----------- 工具方法
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
@ -348,20 +371,41 @@ public class SaSsoHandle {
|
||||
*/
|
||||
public static Object checkTicket(String ticket, String currUri) {
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
|
||||
// --------- 两种模式
|
||||
if(cfg.getIsHttp()) {
|
||||
// 模式三:使用http请求校验ticket
|
||||
// 模式三:使用 http 请求从认证中心校验ticket
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.getIsSlo()) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, Api.ssoLogoutCall);
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
Object body = cfg.getSendHttp().apply(checkUrl);
|
||||
return (SaFoxUtil.isEmpty(body) ? null : body);
|
||||
SaResult result = request(checkUrl);
|
||||
|
||||
// 校验
|
||||
if(result.getCode() == SaResult.CODE_SUCCESS) {
|
||||
return result.getData();
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoExceptionCode.CODE_20005);
|
||||
}
|
||||
} else {
|
||||
// 模式二:直连Redis校验ticket
|
||||
return SaSsoUtil.checkTicket(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
* @param url 请求地址
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public static SaResult request(String url) {
|
||||
String body = SaSsoManager.getConfig().getSendHttp().apply(url);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(body);
|
||||
return new SaResult(map);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ public class SaSsoExceptionCode {
|
||||
/** 提供的 ticket 是无效的 */
|
||||
public static final int CODE_20004 = 20004;
|
||||
|
||||
/** 在模式三下,sso-client 调用 sso-server 端 校验ticket接口 时,得到的响应是校验失败 */
|
||||
public static final int CODE_20005 = 20005;
|
||||
|
||||
/** 在模式三下,sso-client 调用 sso-server 端 单点注销接口 时,得到的响应是注销失败 */
|
||||
public static final int CODE_20006 = 20006;
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import java.util.Map;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.SaJsonConvertException;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
|
||||
/**
|
||||
@ -31,7 +31,7 @@ public class SaJsonTemplateForJackson implements SaJsonTemplate {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaTokenException(e);
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ public class SaJsonTemplateForJackson implements SaJsonTemplate {
|
||||
Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
|
||||
return map;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaTokenException(e);
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import java.util.Map;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.SaJsonConvertException;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
|
||||
/**
|
||||
@ -31,7 +31,7 @@ public class SaJsonTemplateForJackson implements SaJsonTemplate {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaTokenException(e);
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ public class SaJsonTemplateForJackson implements SaJsonTemplate {
|
||||
Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
|
||||
return map;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaTokenException(e);
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user