mirror of
https://gitee.com/dromara/sa-token.git
synced 2024-12-04 04:47:49 +08:00
优化SSO模块代码
This commit is contained in:
parent
6a3b058dd3
commit
952fa04658
@ -1,7 +1,7 @@
|
|||||||
package cn.dev33.satoken.sso;
|
package cn.dev33.satoken.sso;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token-SSO模块相关常量
|
* Sa-Token-SSO模块相关常量
|
||||||
* @author kong
|
* @author kong
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -22,7 +22,7 @@ public class SaSsoConsts {
|
|||||||
/** SSO-Server端:校验ticket 获取账号id */
|
/** SSO-Server端:校验ticket 获取账号id */
|
||||||
public static String ssoCheckTicket = "/sso/checkTicket";
|
public static String ssoCheckTicket = "/sso/checkTicket";
|
||||||
|
|
||||||
/** SSO-Server端 (and Client端):单点注销 */
|
/** SSO-Server端 (and Client端):单点注销地址 */
|
||||||
public static String ssoLogout = "/sso/logout";
|
public static String ssoLogout = "/sso/logout";
|
||||||
|
|
||||||
/** SSO-Client端:登录地址 */
|
/** SSO-Client端:登录地址 */
|
||||||
@ -57,6 +57,9 @@ public class SaSsoConsts {
|
|||||||
/** Client端单点注销时-回调URL 参数名称 */
|
/** Client端单点注销时-回调URL 参数名称 */
|
||||||
public static String ssoLogoutCall = "ssoLogoutCall";
|
public static String ssoLogoutCall = "ssoLogoutCall";
|
||||||
|
|
||||||
|
public static String name = "name";
|
||||||
|
public static String pwd = "pwd";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Client端单点注销回调URL的Set集合,存储在Session中使用的key */
|
/** Client端单点注销回调URL的Set集合,存储在Session中使用的key */
|
||||||
|
@ -19,59 +19,35 @@ import cn.dev33.satoken.util.SaResult;
|
|||||||
public class SaSsoHandle {
|
public class SaSsoHandle {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Server端请求
|
* 处理所有Server端请求
|
||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
*/
|
*/
|
||||||
public static Object serverRequest() {
|
public static Object serverRequest() {
|
||||||
|
|
||||||
// 获取变量
|
// 获取变量
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
SaResponse res = SaHolder.getResponse();
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
SaSsoConfig sso = SaManager.getConfig().getSso();
|
|
||||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
// ------------------ 路由分发 ------------------
|
||||||
|
|
||||||
// ---------- SSO-Server端:单点登录授权地址
|
// SSO-Server端:授权地址
|
||||||
if(req.isPath(Api.ssoAuth)) {
|
if(req.isPath(Api.ssoAuth)) {
|
||||||
// ---------- 此处两种情况分开处理:
|
return ssoAuth();
|
||||||
// 情况1:在SSO认证中心尚未登录,则先去登登录
|
|
||||||
if(stpLogic.isLogin() == false) {
|
|
||||||
return sso.notLoginView.get();
|
|
||||||
}
|
|
||||||
// 情况2:在SSO认证中心已经登录,开始构建授权重定向地址,下放ticket
|
|
||||||
String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
|
|
||||||
return res.redirect(redirectUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Server端:RestAPI 登录接口
|
// SSO-Server端:RestAPI 登录接口
|
||||||
if(req.isPath(Api.ssoDoLogin)) {
|
if(req.isPath(Api.ssoDoLogin)) {
|
||||||
return sso.doLoginHandle.apply(req.getParam("name"), req.getParam("pwd"));
|
return ssoDoLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Server端:校验ticket 获取账号id
|
// SSO-Server端:校验ticket 获取账号id
|
||||||
if(req.isPath(Api.ssoCheckTicket) && sso.isHttp) {
|
if(req.isPath(Api.ssoCheckTicket) && cfg.isHttp) {
|
||||||
String ticket = req.getParam(ParamName.ticket);
|
return ssoCheckTicket();
|
||||||
String sloCallback = req.getParam(ParamName.ssoLogoutCall);
|
|
||||||
|
|
||||||
// 校验ticket,获取对应的账号id
|
|
||||||
Object loginId = SaSsoUtil.checkTicket(ticket);
|
|
||||||
|
|
||||||
// 注册此客户端的单点注销回调URL
|
|
||||||
SaSsoUtil.registerSloCallbackUrl(loginId, sloCallback);
|
|
||||||
|
|
||||||
// 返回给Client端
|
|
||||||
return loginId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Server端:单点注销
|
// SSO-Server端:单点注销
|
||||||
if(req.isPath(Api.ssoLogout) && sso.isSlo) {
|
if(req.isPath(Api.ssoLogout) && cfg.isSlo) {
|
||||||
String loginId = req.getParam(ParamName.loginId);
|
return ssoServerLogout();
|
||||||
String secretkey = req.getParam(ParamName.secretkey);
|
|
||||||
|
|
||||||
// 遍历通知Client端注销会话
|
|
||||||
SaSsoUtil.singleLogout(secretkey, loginId, url -> sso.sendHttp.apply(url));
|
|
||||||
|
|
||||||
// 完成
|
|
||||||
return SaSsoConsts.OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认返回
|
// 默认返回
|
||||||
@ -79,102 +55,245 @@ public class SaSsoHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Client端请求
|
* SSO-Server端:授权地址
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoAuth() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaResponse res = SaHolder.getResponse();
|
||||||
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// ---------- 此处两种情况分开处理:
|
||||||
|
// 情况1:在SSO认证中心尚未登录,则先去登登录
|
||||||
|
if(stpLogic.isLogin() == false) {
|
||||||
|
return cfg.notLoginView.get();
|
||||||
|
}
|
||||||
|
// 情况2:在SSO认证中心已经登录,开始构建授权重定向地址,下放ticket
|
||||||
|
String redirectUrl = SaSsoUtil.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(ParamName.redirect));
|
||||||
|
return res.redirect(redirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Server端:RestAPI 登录接口
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoDoLogin() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
|
|
||||||
|
// 处理
|
||||||
|
return cfg.doLoginHandle.apply(req.getParam(ParamName.name), req.getParam(ParamName.pwd));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Server端:校验ticket 获取账号id
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoCheckTicket() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
|
||||||
|
// 获取参数
|
||||||
|
String ticket = req.getParam(ParamName.ticket);
|
||||||
|
String sloCallback = req.getParam(ParamName.ssoLogoutCall);
|
||||||
|
|
||||||
|
// 校验ticket,获取对应的账号id
|
||||||
|
Object loginId = SaSsoUtil.checkTicket(ticket);
|
||||||
|
|
||||||
|
// 注册此客户端的单点注销回调URL
|
||||||
|
SaSsoUtil.registerSloCallbackUrl(loginId, sloCallback);
|
||||||
|
|
||||||
|
// 返回给Client端
|
||||||
|
return loginId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Server端:单点注销
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoServerLogout() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// 获取参数
|
||||||
|
String loginId = req.getParam(ParamName.loginId);
|
||||||
|
String secretkey = req.getParam(ParamName.secretkey);
|
||||||
|
|
||||||
|
// 遍历通知Client端注销会话
|
||||||
|
// SaSsoUtil.singleLogout(secretkey, loginId, url -> cfg.sendHttp.apply(url));
|
||||||
|
// step.1 校验秘钥
|
||||||
|
SaSsoUtil.checkSecretkey(secretkey);
|
||||||
|
|
||||||
|
// step.2 遍历通知Client端注销会话
|
||||||
|
SaSsoUtil.forEachSloUrl(loginId, url -> cfg.sendHttp.apply(url));
|
||||||
|
|
||||||
|
// step.3 Server端注销
|
||||||
|
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
|
||||||
|
|
||||||
|
// 完成
|
||||||
|
return SaSsoConsts.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有Client端请求
|
||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
*/
|
*/
|
||||||
public static Object clientRequest() {
|
public static Object clientRequest() {
|
||||||
|
|
||||||
// 获取变量
|
// 获取变量
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
SaResponse res = SaHolder.getResponse();
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
SaSsoConfig sso = SaManager.getConfig().getSso();
|
|
||||||
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
|
||||||
|
|
||||||
|
// ------------------ 路由分发 ------------------
|
||||||
|
|
||||||
// ---------- SSO-Client端:登录地址
|
// ---------- SSO-Client端:登录地址
|
||||||
if(req.isPath(Api.ssoLogin)) {
|
if(req.isPath(Api.ssoLogin)) {
|
||||||
String back = req.getParam(ParamName.back, "/");
|
return ssoLogin();
|
||||||
String ticket = req.getParam(ParamName.ticket);
|
|
||||||
|
|
||||||
// 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回
|
|
||||||
if(stpLogic.isLogin()) {
|
|
||||||
return res.redirect(back);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* 此时有两种情况:
|
|
||||||
* 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心
|
|
||||||
* 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录
|
|
||||||
*/
|
|
||||||
if(ticket == null) {
|
|
||||||
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(SaHolder.getRequest().getUrl(), back);
|
|
||||||
return res.redirect(serverAuthUrl);
|
|
||||||
} else {
|
|
||||||
// ------- 1、校验ticket,获取账号id
|
|
||||||
Object loginId = null;
|
|
||||||
if(sso.isHttp) {
|
|
||||||
// 方式1:使用http请求校验ticket
|
|
||||||
String ssoLogoutCall = null;
|
|
||||||
if(sso.isSlo) {
|
|
||||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(Api.ssoLogin, Api.ssoLogoutCall);
|
|
||||||
}
|
|
||||||
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
|
||||||
Object body = sso.sendHttp.apply(checkUrl);
|
|
||||||
loginId = (SaFoxUtil.isEmpty(body) ? null : body);
|
|
||||||
} else {
|
|
||||||
// 方式2:直连Redis校验ticket
|
|
||||||
loginId = SaSsoUtil.checkTicket(ticket);
|
|
||||||
}
|
|
||||||
// ------- 2、如果loginId有值,说明ticket有效,进行登录并重定向至back地址
|
|
||||||
if(loginId != null ) {
|
|
||||||
stpLogic.login(loginId);
|
|
||||||
return res.redirect(back);
|
|
||||||
} else {
|
|
||||||
// 如果ticket无效:
|
|
||||||
return sso.ticketInvalidView.apply(ticket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Client端:单点注销 [模式二]
|
// ---------- SSO-Client端:单点注销 [模式二]
|
||||||
if(req.isPath(Api.ssoLogout) && sso.isSlo && sso.isHttp == false) {
|
if(req.isPath(Api.ssoLogout) && cfg.isSlo && cfg.isHttp == false) {
|
||||||
stpLogic.logout();
|
return ssoLogoutType2();
|
||||||
if(req.getParam(ParamName.back) == null) {
|
|
||||||
return SaResult.ok("单点注销成功");
|
|
||||||
} else {
|
|
||||||
return res.redirect(req.getParam(ParamName.back, "/"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Client端:单点注销 [模式三]
|
// ---------- SSO-Client端:单点注销 [模式三]
|
||||||
if(req.isPath(Api.ssoLogout) && sso.isSlo && sso.isHttp) {
|
if(req.isPath(Api.ssoLogout) && cfg.isSlo && cfg.isHttp) {
|
||||||
// 如果未登录,则无需注销
|
return ssoLogoutType3();
|
||||||
if(stpLogic.isLogin() == false) {
|
|
||||||
return SaResult.ok();
|
|
||||||
}
|
|
||||||
// 调用SSO-Server认证中心API,进行注销
|
|
||||||
String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId());
|
|
||||||
String body = String.valueOf(sso.sendHttp.apply(url));
|
|
||||||
if(SaSsoConsts.OK.equals(body)) {
|
|
||||||
if(req.getParam(ParamName.back) == null) {
|
|
||||||
return SaResult.ok("单点注销成功");
|
|
||||||
} else {
|
|
||||||
return res.redirect(req.getParam(ParamName.back, "/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SaResult.error("单点注销失败");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- SSO-Client端:单点注销的回调 [模式三]
|
// ---------- SSO-Client端:单点注销的回调 [模式三]
|
||||||
if(req.isPath(Api.ssoLogoutCall) && sso.isSlo && sso.isHttp) {
|
if(req.isPath(Api.ssoLogoutCall) && cfg.isSlo && cfg.isHttp) {
|
||||||
String loginId = req.getParam(ParamName.loginId);
|
return ssoLogoutCall();
|
||||||
String secretkey = req.getParam(ParamName.secretkey);
|
|
||||||
|
|
||||||
SaSsoUtil.checkSecretkey(secretkey);
|
|
||||||
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
|
|
||||||
return SaSsoConsts.OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认返回
|
// 默认返回
|
||||||
return SaSsoConsts.NOT_HANDLE;
|
return SaSsoConsts.NOT_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Client端:登录地址
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoLogin() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaResponse res = SaHolder.getResponse();
|
||||||
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// 获取参数
|
||||||
|
String back = req.getParam(ParamName.back, "/");
|
||||||
|
String ticket = req.getParam(ParamName.ticket);
|
||||||
|
|
||||||
|
// 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回
|
||||||
|
if(stpLogic.isLogin()) {
|
||||||
|
return res.redirect(back);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 此时有两种情况:
|
||||||
|
* 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心
|
||||||
|
* 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录
|
||||||
|
*/
|
||||||
|
if(ticket == null) {
|
||||||
|
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(SaHolder.getRequest().getUrl(), back);
|
||||||
|
return res.redirect(serverAuthUrl);
|
||||||
|
} else {
|
||||||
|
// ------- 1、校验ticket,获取账号id
|
||||||
|
Object loginId = null;
|
||||||
|
if(cfg.isHttp) {
|
||||||
|
// 方式1:使用http请求校验ticket
|
||||||
|
String ssoLogoutCall = null;
|
||||||
|
if(cfg.isSlo) {
|
||||||
|
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(Api.ssoLogin, Api.ssoLogoutCall);
|
||||||
|
}
|
||||||
|
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||||
|
Object body = cfg.sendHttp.apply(checkUrl);
|
||||||
|
loginId = (SaFoxUtil.isEmpty(body) ? null : body);
|
||||||
|
} else {
|
||||||
|
// 方式2:直连Redis校验ticket
|
||||||
|
loginId = SaSsoUtil.checkTicket(ticket);
|
||||||
|
}
|
||||||
|
// ------- 2、如果loginId有值,说明ticket有效,进行登录并重定向至back地址
|
||||||
|
if(loginId != null ) {
|
||||||
|
stpLogic.login(loginId);
|
||||||
|
return res.redirect(back);
|
||||||
|
} else {
|
||||||
|
// 如果ticket无效:
|
||||||
|
return cfg.ticketInvalidView.apply(ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Client端:单点注销 [模式二]
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoLogoutType2() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaResponse res = SaHolder.getResponse();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// 开始处理
|
||||||
|
stpLogic.logout();
|
||||||
|
if(req.getParam(ParamName.back) == null) {
|
||||||
|
return SaResult.ok("单点注销成功");
|
||||||
|
} else {
|
||||||
|
return res.redirect(req.getParam(ParamName.back, "/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Client端:单点注销 [模式三]
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoLogoutType3() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
SaResponse res = SaHolder.getResponse();
|
||||||
|
SaSsoConfig cfg = SaManager.getConfig().getSso();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// 如果未登录,则无需注销
|
||||||
|
if(stpLogic.isLogin() == false) {
|
||||||
|
return SaResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用SSO-Server认证中心API,进行注销
|
||||||
|
String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId());
|
||||||
|
String body = String.valueOf(cfg.sendHttp.apply(url));
|
||||||
|
if(SaSsoConsts.OK.equals(body)) {
|
||||||
|
if(req.getParam(ParamName.back) == null) {
|
||||||
|
return SaResult.ok("单点注销成功");
|
||||||
|
} else {
|
||||||
|
return res.redirect(req.getParam(ParamName.back, "/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SaResult.error("单点注销失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSO-Client端:单点注销的回调 [模式三]
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static Object ssoLogoutCall() {
|
||||||
|
// 获取变量
|
||||||
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic;
|
||||||
|
|
||||||
|
// 获取参数
|
||||||
|
String loginId = req.getParam(ParamName.loginId);
|
||||||
|
String secretkey = req.getParam(ParamName.secretkey);
|
||||||
|
|
||||||
|
SaSsoUtil.checkSecretkey(secretkey);
|
||||||
|
stpLogic.logoutByTokenValue(stpLogic.getTokenValueByLoginId(loginId));
|
||||||
|
return SaSsoConsts.OK;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,77 +8,87 @@ import java.util.Set;
|
|||||||
import cn.dev33.satoken.SaManager;
|
import cn.dev33.satoken.SaManager;
|
||||||
import cn.dev33.satoken.config.SaSsoConfig;
|
import cn.dev33.satoken.config.SaSsoConfig;
|
||||||
import cn.dev33.satoken.exception.SaTokenException;
|
import cn.dev33.satoken.exception.SaTokenException;
|
||||||
|
import cn.dev33.satoken.session.SaSession;
|
||||||
import cn.dev33.satoken.sso.SaSsoConsts.ParamName;
|
import cn.dev33.satoken.sso.SaSsoConsts.ParamName;
|
||||||
import cn.dev33.satoken.stp.StpLogic;
|
import cn.dev33.satoken.stp.StpLogic;
|
||||||
import cn.dev33.satoken.util.SaFoxUtil;
|
import cn.dev33.satoken.util.SaFoxUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token-SSO 单点登录模块
|
* Sa-Token-SSO 单点登录模块
|
||||||
* @author kong
|
* @author kong
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SaSsoTemplate {
|
public class SaSsoTemplate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单点登录模块使用的 StpLogic 对象
|
||||||
|
*/
|
||||||
public StpLogic stpLogic;
|
public StpLogic stpLogic;
|
||||||
public SaSsoTemplate(StpLogic stpLogic) {
|
public SaSsoTemplate(StpLogic stpLogic) {
|
||||||
this.stpLogic = stpLogic;
|
this.stpLogic = stpLogic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------- Ticket 操作 ----------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 Ticket码
|
* 根据 账号id 创建一个 Ticket码
|
||||||
* @param loginId 账号id
|
* @param loginId 账号id
|
||||||
* @return 票据
|
* @return Ticket码
|
||||||
*/
|
*/
|
||||||
public String createTicket(Object loginId) {
|
public String createTicket(Object loginId) {
|
||||||
// 随机一个ticket
|
// 创建 Ticket
|
||||||
String ticket = randomTicket(loginId);
|
String ticket = randomTicket(loginId);
|
||||||
|
|
||||||
// 保存入库
|
// 保存 Ticket
|
||||||
long ticketTimeout = SaManager.getConfig().getSso().getTicketTimeout();
|
saveTicket(ticket, loginId);
|
||||||
SaManager.getSaTokenDao().set(splicingKeyTicketToId(ticket), String.valueOf(loginId), ticketTimeout);
|
saveTicketIndex(ticket, loginId);
|
||||||
SaManager.getSaTokenDao().set(splicingKeyIdToTicket(loginId), String.valueOf(ticket), ticketTimeout);
|
|
||||||
|
|
||||||
// 返回
|
// 返回 Ticket
|
||||||
return ticket;
|
return ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除一个 Ticket码
|
* 保存 Ticket
|
||||||
* @param ticket Ticket码
|
* @param ticket ticket码
|
||||||
|
* @param loginId 账号id
|
||||||
*/
|
*/
|
||||||
public void deleteTicket(String ticket) {
|
public void saveTicket(String ticket, Object loginId) {
|
||||||
Object loginId = getLoginId(ticket);
|
long ticketTimeout = SaManager.getConfig().getSso().getTicketTimeout();
|
||||||
if(loginId != null) {
|
SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout);
|
||||||
SaManager.getSaTokenDao().delete(splicingKeyTicketToId(ticket));
|
|
||||||
SaManager.getSaTokenDao().delete(splicingKeyIdToTicket(loginId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:Server端向Client下放ticke的地址
|
* 保存 Ticket 索引
|
||||||
|
* @param ticket ticket码
|
||||||
* @param loginId 账号id
|
* @param loginId 账号id
|
||||||
* @param redirect Client端提供的重定向地址
|
|
||||||
* @return see note
|
|
||||||
*/
|
*/
|
||||||
public String buildRedirectUrl(Object loginId, String redirect) {
|
public void saveTicketIndex(String ticket, Object loginId) {
|
||||||
// 校验重定向地址
|
long ticketTimeout = SaManager.getConfig().getSso().getTicketTimeout();
|
||||||
checkRedirectUrl(redirect);
|
SaManager.getSaTokenDao().set(splicingTicketIndexKey(loginId), String.valueOf(ticket), ticketTimeout);
|
||||||
|
|
||||||
// 删掉旧ticket
|
|
||||||
String oldTicket = SaManager.getSaTokenDao().get(splicingKeyIdToTicket(loginId));
|
|
||||||
if(oldTicket != null) {
|
|
||||||
deleteTicket(oldTicket);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取新ticket
|
|
||||||
String ticket = createTicket(loginId);
|
|
||||||
|
|
||||||
// 构建 授权重定向地址
|
|
||||||
redirect = encodeBackParam(redirect);
|
|
||||||
String redirectUrl = SaFoxUtil.joinParam(redirect, ParamName.ticket, ticket);
|
|
||||||
return redirectUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 Ticket
|
||||||
|
* @param ticket Ticket码
|
||||||
|
*/
|
||||||
|
public void deleteTicket(String ticket) {
|
||||||
|
if(ticket == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaManager.getSaTokenDao().delete(splicingTicketSaveKey(ticket));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 Ticket索引
|
||||||
|
* @param loginId 账号id
|
||||||
|
*/
|
||||||
|
public void deleteTicketIndex(Object loginId) {
|
||||||
|
if(loginId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaManager.getSaTokenDao().delete(splicingTicketIndexKey(loginId));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 Ticket码 获取账号id,如果Ticket码无效则返回null
|
* 根据 Ticket码 获取账号id,如果Ticket码无效则返回null
|
||||||
* @param ticket Ticket码
|
* @param ticket Ticket码
|
||||||
@ -88,7 +98,7 @@ public class SaSsoTemplate {
|
|||||||
if(SaFoxUtil.isEmpty(ticket)) {
|
if(SaFoxUtil.isEmpty(ticket)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return SaManager.getSaTokenDao().get(splicingKeyTicketToId(ticket));
|
return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,9 +111,21 @@ public class SaSsoTemplate {
|
|||||||
public <T> T getLoginId(String ticket, Class<T> cs) {
|
public <T> T getLoginId(String ticket, Class<T> cs) {
|
||||||
return SaFoxUtil.getValueByType(getLoginId(ticket), cs);
|
return SaFoxUtil.getValueByType(getLoginId(ticket), cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验ticket码,获取账号id,如果ticket可以有效,则立刻删除
|
* 查询 指定账号id的 Ticket值
|
||||||
|
* @param loginId 账号id
|
||||||
|
* @return Ticket值
|
||||||
|
*/
|
||||||
|
public String getTicketValue(Object loginId) {
|
||||||
|
if(loginId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return SaManager.getSaTokenDao().get(splicingTicketIndexKey(loginId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验ticket码,获取账号id,如果此ticket是有效的,则立即删除
|
||||||
* @param ticket Ticket码
|
* @param ticket Ticket码
|
||||||
* @return 账号id
|
* @return 账号id
|
||||||
*/
|
*/
|
||||||
@ -111,45 +133,31 @@ public class SaSsoTemplate {
|
|||||||
Object loginId = getLoginId(ticket);
|
Object loginId = getLoginId(ticket);
|
||||||
if(loginId != null) {
|
if(loginId != null) {
|
||||||
deleteTicket(ticket);
|
deleteTicket(ticket);
|
||||||
|
deleteTicketIndex(loginId);
|
||||||
}
|
}
|
||||||
return loginId;
|
return loginId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验重定向url合法性
|
* 随机一个 Ticket码
|
||||||
* @param url 下放ticket的url地址
|
* @param loginId 账号id
|
||||||
|
* @return Ticket码
|
||||||
*/
|
*/
|
||||||
public void checkRedirectUrl(String url) {
|
public String randomTicket(Object loginId) {
|
||||||
|
return SaFoxUtil.getRandomString(64);
|
||||||
// 1、是否是一个有效的url
|
|
||||||
if(SaFoxUtil.isUrl(url) == false) {
|
|
||||||
throw new SaTokenException("无效回调地址:" + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2、截取掉?后面的部分
|
|
||||||
int qIndex = url.indexOf("?");
|
|
||||||
if(qIndex != -1) {
|
|
||||||
url = url.substring(0, qIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3、是否在[允许地址列表]之中
|
|
||||||
String authUrl = SaManager.getConfig().getSso().getAllowUrl().replaceAll(" ", "");
|
|
||||||
List<String> authUrlList = Arrays.asList(authUrl.split(","));
|
|
||||||
if(SaManager.getSaTokenAction().hasElement(authUrlList, url) == false) {
|
|
||||||
throw new SaTokenException("非法回调地址:" + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证通过
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------- 构建URL ----------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:Server端 单点登录地址
|
* 构建URL:Server端 单点登录地址
|
||||||
* @param clientLoginUrl Client端登录地址
|
* @param clientLoginUrl Client端登录地址
|
||||||
* @param back 回调路径
|
* @param back 回调路径
|
||||||
* @return [SSO-Server端-认证地址 ]
|
* @return [SSO-Server端-认证地址 ]
|
||||||
*/
|
*/
|
||||||
public String buildServerAuthUrl(String clientLoginUrl, String back) {
|
public String buildServerAuthUrl(String clientLoginUrl, String back) {
|
||||||
|
|
||||||
// 服务端认证地址
|
// 服务端认证地址
|
||||||
String serverUrl = SaManager.getConfig().getSso().getAuthUrl();
|
String serverUrl = SaManager.getConfig().getSso().getAuthUrl();
|
||||||
|
|
||||||
@ -165,6 +173,55 @@ public class SaSsoTemplate {
|
|||||||
return serverAuthUrl;
|
return serverAuthUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建URL:Server端向Client下放ticke的地址
|
||||||
|
* @param loginId 账号id
|
||||||
|
* @param redirect Client端提供的重定向地址
|
||||||
|
* @return see note
|
||||||
|
*/
|
||||||
|
public String buildRedirectUrl(Object loginId, String redirect) {
|
||||||
|
|
||||||
|
// 校验 重定向地址 是否合法
|
||||||
|
checkRedirectUrl(redirect);
|
||||||
|
|
||||||
|
// 删掉 旧Ticket
|
||||||
|
deleteTicket(getTicketValue(loginId));
|
||||||
|
|
||||||
|
// 创建 新Ticket
|
||||||
|
String ticket = createTicket(loginId);
|
||||||
|
|
||||||
|
// 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket)
|
||||||
|
return SaFoxUtil.joinParam(encodeBackParam(redirect), ParamName.ticket, ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验重定向url合法性
|
||||||
|
* @param url 下放ticket的url地址
|
||||||
|
*/
|
||||||
|
public void checkRedirectUrl(String url) {
|
||||||
|
|
||||||
|
// 1、是否是一个有效的url
|
||||||
|
if(SaFoxUtil.isUrl(url) == false) {
|
||||||
|
throw new SaTokenException("无效redirect:" + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、截取掉?后面的部分
|
||||||
|
int qIndex = url.indexOf("?");
|
||||||
|
if(qIndex != -1) {
|
||||||
|
url = url.substring(0, qIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3、是否在[允许地址列表]之中
|
||||||
|
String authUrl = SaManager.getConfig().getSso().getAllowUrl().replaceAll(" ", "");
|
||||||
|
List<String> authUrlList = Arrays.asList(authUrl.split(","));
|
||||||
|
if(SaManager.getSaTokenAction().hasElement(authUrlList, url) == false) {
|
||||||
|
throw new SaTokenException("非法redirect:" + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验通过 √
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
|
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
|
||||||
* @param url url
|
* @param url url
|
||||||
@ -191,17 +248,8 @@ public class SaSsoTemplate {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 随机一个 Ticket码
|
|
||||||
* @param loginId 账号id
|
|
||||||
* @return 票据
|
|
||||||
*/
|
|
||||||
public String randomTicket(Object loginId) {
|
|
||||||
return SaFoxUtil.getRandomString(64);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------- SSO 模式三 -------------------
|
// ------------------- SSO 模式三相关 -------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验secretkey秘钥是否有效
|
* 校验secretkey秘钥是否有效
|
||||||
@ -215,18 +263,23 @@ public class SaSsoTemplate {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:校验ticket的URL
|
* 构建URL:校验ticket的URL
|
||||||
|
* <p> 在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id
|
||||||
* @param ticket ticket码
|
* @param ticket ticket码
|
||||||
* @param ssoLogoutCallUrl 单点注销时的回调URL
|
* @param ssoLogoutCallUrl 单点注销时的回调URL
|
||||||
* @return 构建完毕的URL
|
* @return 构建完毕的URL
|
||||||
*/
|
*/
|
||||||
public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
|
public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
|
||||||
|
// 裸地址
|
||||||
String url = SaManager.getConfig().getSso().getCheckTicketUrl();
|
String url = SaManager.getConfig().getSso().getCheckTicketUrl();
|
||||||
|
|
||||||
// 拼接ticket参数
|
// 拼接ticket参数
|
||||||
url = SaFoxUtil.joinParam(url, ParamName.ticket, ticket);
|
url = SaFoxUtil.joinParam(url, ParamName.ticket, ticket);
|
||||||
|
|
||||||
// 拼接单点注销时的回调URL
|
// 拼接单点注销时的回调URL
|
||||||
if(ssoLogoutCallUrl != null) {
|
if(ssoLogoutCallUrl != null) {
|
||||||
url = SaFoxUtil.joinParam(url, ParamName.ssoLogoutCall, ssoLogoutCallUrl);
|
url = SaFoxUtil.joinParam(url, ParamName.ssoLogoutCall, ssoLogoutCallUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回
|
// 返回
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@ -240,9 +293,10 @@ public class SaSsoTemplate {
|
|||||||
if(loginId == null || sloCallbackUrl == null || sloCallbackUrl.isEmpty()) {
|
if(loginId == null || sloCallbackUrl == null || sloCallbackUrl.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<String> urlSet = stpLogic.getSessionByLoginId(loginId).get(SaSsoConsts.SLO_CALLBACK_SET_KEY, ()-> new HashSet<String>());
|
SaSession session = stpLogic.getSessionByLoginId(loginId);
|
||||||
|
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, ()-> new HashSet<String>());
|
||||||
urlSet.add(sloCallbackUrl);
|
urlSet.add(sloCallbackUrl);
|
||||||
stpLogic.getSessionByLoginId(loginId).set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
|
session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -304,20 +358,23 @@ public class SaSsoTemplate {
|
|||||||
* @param ticket ticket值
|
* @param ticket ticket值
|
||||||
* @return key
|
* @return key
|
||||||
*/
|
*/
|
||||||
public String splicingKeyTicketToId(String ticket) {
|
public String splicingTicketSaveKey(String ticket) {
|
||||||
return SaManager.getConfig().getTokenName() + ":ticket:" + ticket;
|
return SaManager.getConfig().getTokenName() + ":ticket:" + ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拼接key:账号Id 反查 Ticket
|
* 拼接key:账号Id 反查 Ticket
|
||||||
* @param id 账号id
|
* @param id 账号id
|
||||||
* @return key
|
* @return key
|
||||||
*/
|
*/
|
||||||
public String splicingKeyIdToTicket(Object id) {
|
public String splicingTicketIndexKey(Object id) {
|
||||||
return SaManager.getConfig().getTokenName() + ":id-ticket:" + id;
|
return SaManager.getConfig().getTokenName() + ":id-ticket:" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单点注销回调函数
|
||||||
|
* @author kong
|
||||||
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
static interface CallSloUrlFunction{
|
static interface CallSloUrlFunction{
|
||||||
/**
|
/**
|
||||||
@ -327,5 +384,4 @@ public class SaSsoTemplate {
|
|||||||
public void run(String url);
|
public void run(String url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import cn.dev33.satoken.sso.SaSsoTemplate.CallSloUrlFunction;
|
|||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token-SSO 单点登录工具类
|
* Sa-Token-SSO 单点登录模块 工具类
|
||||||
* @author kong
|
* @author kong
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -15,33 +15,34 @@ public class SaSsoUtil {
|
|||||||
*/
|
*/
|
||||||
public static SaSsoTemplate saSsoTemplate = new SaSsoTemplate(StpUtil.stpLogic);
|
public static SaSsoTemplate saSsoTemplate = new SaSsoTemplate(StpUtil.stpLogic);
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------- Ticket 操作 ----------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 Ticket票据
|
* 根据 账号id 创建一个 Ticket码
|
||||||
* @param loginId 账号id
|
* @param loginId 账号id
|
||||||
* @return 票据
|
* @return Ticket码
|
||||||
*/
|
*/
|
||||||
public static String createTicket(Object loginId) {
|
public static String createTicket(Object loginId) {
|
||||||
return saSsoTemplate.createTicket(loginId);
|
return saSsoTemplate.createTicket(loginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除一个 Ticket码
|
* 删除 Ticket
|
||||||
* @param ticket Ticket码
|
* @param ticket Ticket码
|
||||||
*/
|
*/
|
||||||
public static void deleteTicket(String ticket) {
|
public static void deleteTicket(String ticket) {
|
||||||
saSsoTemplate.deleteTicket(ticket);
|
saSsoTemplate.deleteTicket(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建URL:Server端向Client下放ticke的地址
|
|
||||||
* @param loginId 账号id
|
|
||||||
* @param redirect Client端提供的重定向地址
|
|
||||||
* @return see note
|
|
||||||
*/
|
|
||||||
public static String buildRedirectUrl(Object loginId, String redirect) {
|
|
||||||
return saSsoTemplate.buildRedirectUrl(loginId, redirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 Ticket索引
|
||||||
|
* @param loginId 账号id
|
||||||
|
*/
|
||||||
|
public static void deleteTicketIndex(Object loginId) {
|
||||||
|
saSsoTemplate.deleteTicketIndex(loginId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 Ticket码 获取账号id,如果Ticket码无效则返回null
|
* 根据 Ticket码 获取账号id,如果Ticket码无效则返回null
|
||||||
* @param ticket Ticket码
|
* @param ticket Ticket码
|
||||||
@ -63,7 +64,7 @@ public class SaSsoUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验ticket码,获取账号id,如果ticket可以有效,则立刻删除
|
* 校验ticket码,获取账号id,如果此ticket是有效的,则立即删除
|
||||||
* @param ticket Ticket码
|
* @param ticket Ticket码
|
||||||
* @return 账号id
|
* @return 账号id
|
||||||
*/
|
*/
|
||||||
@ -71,13 +72,8 @@ public class SaSsoUtil {
|
|||||||
return saSsoTemplate.checkTicket(ticket);
|
return saSsoTemplate.checkTicket(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验重定向url合法性
|
// ---------------------- 构建URL ----------------------
|
||||||
* @param url 下放ticket的url地址
|
|
||||||
*/
|
|
||||||
public static void checkAuthUrl(String url) {
|
|
||||||
saSsoTemplate.checkRedirectUrl(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:Server端 单点登录地址
|
* 构建URL:Server端 单点登录地址
|
||||||
@ -89,6 +85,24 @@ public class SaSsoUtil {
|
|||||||
return saSsoTemplate.buildServerAuthUrl(clientLoginUrl, back);
|
return saSsoTemplate.buildServerAuthUrl(clientLoginUrl, back);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建URL:Server端向Client下放ticke的地址
|
||||||
|
* @param loginId 账号id
|
||||||
|
* @param redirect Client端提供的重定向地址
|
||||||
|
* @return see note
|
||||||
|
*/
|
||||||
|
public static String buildRedirectUrl(Object loginId, String redirect) {
|
||||||
|
return saSsoTemplate.buildRedirectUrl(loginId, redirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验重定向url合法性
|
||||||
|
* @param url 下放ticket的url地址
|
||||||
|
*/
|
||||||
|
public static void checkAuthUrl(String url) {
|
||||||
|
saSsoTemplate.checkRedirectUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------- SSO 模式三 -------------------
|
// ------------------- SSO 模式三 -------------------
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@
|
|||||||
<a href="../index.html">首页</a>
|
<a href="../index.html">首页</a>
|
||||||
<!-- <a href="index.html">文档</a> -->
|
<!-- <a href="index.html">文档</a> -->
|
||||||
<a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a>
|
<a href="http://sa-app.dev33.cn/wall.html?name=sa-token" target="_blank">需求墙</a>
|
||||||
<a href="#/more/tj-gzh">推荐公众号</a>
|
<!-- <a href="#/more/tj-gzh">推荐公众号</a> -->
|
||||||
|
<a href="#/more/link">生态</a>
|
||||||
<a href="#/more/update-log">更新日志</a>
|
<a href="#/more/update-log">更新日志</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="main-box">
|
<div class="main-box">
|
||||||
|
@ -7,10 +7,10 @@ body{font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu
|
|||||||
#main h2 {font-size: 1.6rem;}
|
#main h2 {font-size: 1.6rem;}
|
||||||
#main h3 {font-size: 1.25rem;}
|
#main h3 {font-size: 1.25rem;}
|
||||||
|
|
||||||
.main-box .markdown-section{ padding: 30px 20px; max-width: 65%}
|
.main-box .markdown-section{ padding: 30px 20px; max-width: 75%; margin-left: 10%;}
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
.logo-box {display: none;}
|
.logo-box {display: none;}
|
||||||
.main-box .markdown-section{max-width: 1000px;}
|
.main-box .markdown-section{max-width: 1000px; margin-left: auto;}
|
||||||
}
|
}
|
||||||
/* @media screen and (min-width: 1700px) {
|
/* @media screen and (min-width: 1700px) {
|
||||||
.main-box .markdown-section{max-width: 70%;}
|
.main-box .markdown-section{max-width: 70%;}
|
||||||
|
@ -77,14 +77,23 @@
|
|||||||
### 我想让用户修改密码后立即掉线重新登录,应该怎么做?
|
### 我想让用户修改密码后立即掉线重新登录,应该怎么做?
|
||||||
框架内置 [强制指定账号下线] 的APi,在执行修改密码逻辑之后调用此API即可: `StpUtil.logout()`
|
框架内置 [强制指定账号下线] 的APi,在执行修改密码逻辑之后调用此API即可: `StpUtil.logout()`
|
||||||
|
|
||||||
|
|
||||||
### 整合Redis时先选择了默认jdk序列化,后又改成jackson序列化,程序开始报错,SerializationException?
|
### 整合Redis时先选择了默认jdk序列化,后又改成jackson序列化,程序开始报错,SerializationException?
|
||||||
两者的序列化算法不一致导致的反序列化失败,如果要更改序列化方式,则需要先将Redis中历史数据清除,再做更新
|
两者的序列化算法不一致导致的反序列化失败,如果要更改序列化方式,则需要先将Redis中历史数据清除,再做更新
|
||||||
|
|
||||||
|
|
||||||
|
### 代码鉴权、注解鉴权、路由拦截鉴权,我该如何选择?
|
||||||
|
这个问题没有标准答案,这里只能给你提供一些建议,从鉴权粒度的角度来看:
|
||||||
|
1. 路由拦截鉴权:粒度最粗,只能粗略的拦截一个模块进行权限认证
|
||||||
|
2. 注解鉴权:粒度较细,可以详细到方法级,比较灵活
|
||||||
|
3. 代码鉴权:粒度最细,不光可以控制到方法级,甚至可以if语句决定是否鉴权
|
||||||
|
|
||||||
|
So:从鉴权粒度的角度来看,需要针对一个模块鉴权的时候,就用路由拦截鉴权,需要控制到方法级的时候,就用注解鉴权,需要根据条件判断是否鉴权的时候,就用代码鉴权
|
||||||
|
|
||||||
|
|
||||||
### 还是有不明白到的地方?
|
### 还是有不明白到的地方?
|
||||||
请在`github`提交`issues`,或者加入qq群交流(群链接在[首页](README?id=交流群))
|
请在`gitee` 、 `github` 提交 `issues`,或者加入qq群交流(群链接在[首页](README?id=交流群))
|
||||||
|
|
||||||
|
|
||||||
### 我能为这个框架贡献代码吗?
|
### 我能为这个框架贡献代码吗?
|
||||||
**可以**,请参照首页的提交pr步骤 ,[贡献代码](README?id=贡献代码)
|
**可以**,如果有好的想法,请直接提交pr步骤
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# 友情链接
|
# Sa-Token 相关项目
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 集成Sa-Token的开源项目:
|
### 使用 Sa-Token 的开源项目:
|
||||||
|
|
||||||
[**[ sa-plus ]** 一个基于springboot架构的快速开发框架,内置代码生成器](https://gitee.com/click33/sa-plus)
|
[**[ sa-plus ]** 一个基于springboot架构的快速开发框架,内置代码生成器](https://gitee.com/click33/sa-plus)
|
||||||
|
|
||||||
@ -12,20 +12,24 @@
|
|||||||
|
|
||||||
[**[ helio-starters ]** 基于JDK15 + Spring Boot 2.4 + Sa-Token + Mybatis-Plus的单体Boot版脚手架和微服务Cloud版脚手架,带有配套后台管理前端模板及代码生成器](https://gitee.com/uncarbon97/helio-starters)
|
[**[ helio-starters ]** 基于JDK15 + Spring Boot 2.4 + Sa-Token + Mybatis-Plus的单体Boot版脚手架和微服务Cloud版脚手架,带有配套后台管理前端模板及代码生成器](https://gitee.com/uncarbon97/helio-starters)
|
||||||
|
|
||||||
|
[**[ Sa-Token-Study ]** 以demo示例的方式讲解 Sa-Token 源码涉及到的技术点,连载中……](https://gitee.com/click33/sa-token-study)
|
||||||
|
|
||||||
|
如果您的开源项目使用了Sa-Token,欢迎提交...
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
#### 推荐项目:
|
|
||||||
|
|
||||||
[[OkHttps] - 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
### 推荐项目:
|
||||||
|
|
||||||
[[hasor] - 轻量级ioc/aop框架,采用"微内核+插件"的设计思想](https://gitee.com/zycgit/hasor)
|
[[ OkHttps ] - 一个轻量级http通信框架,API设计无比优雅,支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||||
|
|
||||||
[[sa-admin] - 一个多窗口后台模板,流畅、易上手、提高生产力](https://gitee.com/ejlchina-zhxu/okhttps)
|
[[ hasor ] - 轻量级ioc/aop框架,采用"微内核+插件"的设计思想](https://gitee.com/zycgit/hasor)
|
||||||
|
|
||||||
[[vue-next-admin] - 一套为开发者快速开发准备的基于 vue2.x 越看越精彩的后台管理系统一站式平台模板](https://gitee.com/lyt-top/vue-admin-wonderful)
|
[[ sa-admin ] - 一个多窗口后台模板,流畅、易上手、提高生产力](https://gitee.com/ejlchina-zhxu/okhttps)
|
||||||
|
|
||||||
[[小诺快速开发平台] - 基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本](https://xiaonuo.vip/index#pricing)
|
[[ vue-next-admin ] - 一套为开发者快速开发准备的基于 vue2.x 越看越精彩的后台管理系统一站式平台模板](https://gitee.com/lyt-top/vue-next-admin)
|
||||||
|
|
||||||
|
[[ 小诺快速开发平台 ] - 基于SpringBoot2 + AntDesignVue全新快速开发平台,同时拥有三个版本](https://xiaonuo.vip/index#pricing)
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
虚位以待...
|
虚位以待...
|
||||||
|
Loading…
Reference in New Issue
Block a user