整体重构 sa-token-sso 模块,将 server 端和 client 端代码拆分
This commit is contained in:
+2
-2
@@ -1,8 +1,8 @@
|
||||
package com.pj.h5;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoOfRedis;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
@@ -27,15 +27,15 @@ public class SsoConfig {
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Bean
|
||||
public void configSso(SaSsoConfig sso) { //SaSsoConfig 已自动构建
|
||||
public void configSso(SaSsoServerConfig ssoServer) { //SaSsoConfig 已自动构建
|
||||
|
||||
// 配置:未登录时返回的View
|
||||
sso.notLoginView = () -> {
|
||||
ssoServer.notLoginView = () -> {
|
||||
return new ModelAndView("sa-login.html");
|
||||
};
|
||||
|
||||
// 配置:登录处理函数
|
||||
sso.doLoginHandle = (name, pwd) -> {
|
||||
ssoServer.doLoginHandle = (name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
@@ -45,7 +45,7 @@ public class SsoConfig {
|
||||
};
|
||||
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
sso.sendHttp = url -> {
|
||||
ssoServer.sendHttp = url -> {
|
||||
try {
|
||||
// 发起 http 请求
|
||||
System.out.println("------ 发起请求:" + url);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pj.sso;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
import org.noear.solon.annotation.Mapping;
|
||||
|
||||
@@ -22,6 +22,6 @@ public class SsoServerController {
|
||||
*/
|
||||
@Mapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.serverDister();
|
||||
return SaSsoServerProcessor.instance.dister();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -10,7 +10,7 @@ sa-token:
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
sso:
|
||||
sso-server:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
@@ -22,7 +22,8 @@ sa-token:
|
||||
# 是否打开模式三
|
||||
isHttp: true
|
||||
# 接口调用秘钥(用于SSO模式三的单点注销功能)
|
||||
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
sign:
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
|
||||
sa-token: #名字可以随意取
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ public class SsoClientController implements Render {
|
||||
@Produces(MimeType.TEXT_HTML_VALUE)
|
||||
@Mapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaSsoManager.getConfig().splicingAuthUrl();
|
||||
String solUrl = SaSsoManager.getConfig().splicingSloUrl();
|
||||
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
|
||||
String solUrl = SaSsoManager.getClientConfig().splicingSloUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ server:
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
sso-client:
|
||||
# SSO-Server端-单点登录授权地址
|
||||
auth-url: http://sso.stp.com:9000/sso/auth
|
||||
# SSO-Server端-单点注销地址
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package com.pj.h5;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
@@ -34,7 +34,7 @@ public class H5Controller implements Render {
|
||||
// 根据ticket进行登录
|
||||
@Mapping("/sso/doLoginByTicket")
|
||||
public SaResult doLoginByTicket(String ticket) {
|
||||
Object loginId = SaSsoProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket");
|
||||
Object loginId = SaSsoClientProcessor.instance.checkTicketByMode2Or3(ticket, "/sso/doLoginByTicket");
|
||||
if(loginId != null) {
|
||||
StpUtil.login(loginId);
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.pj.sso;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
@@ -37,7 +37,7 @@ public class SsoClientController implements Render {
|
||||
*/
|
||||
@Mapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.clientDister();
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 全局异常拦截并转换
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ server:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
sso-client:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html
|
||||
|
||||
+9
-11
@@ -1,10 +1,9 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoTemplate;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Condition;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
@@ -25,7 +24,7 @@ public class SaSsoAutoConfigure {
|
||||
* 获取 SSO 配置Bean
|
||||
* */
|
||||
@Bean
|
||||
public SaSsoConfig getConfig(@Inject(value = "${sa-token.sso}",required = false) SaSsoConfig ssoConfig) {
|
||||
public SaSsoClientConfig getConfig(@Inject(value = "${sa-token.sso-client}",required = false) SaSsoClientConfig ssoConfig) {
|
||||
return ssoConfig;
|
||||
}
|
||||
|
||||
@@ -35,18 +34,17 @@ public class SaSsoAutoConfigure {
|
||||
* @param saSsoConfig 配置对象
|
||||
*/
|
||||
@Bean
|
||||
public void setSaSsoConfig(@Inject(required = false) SaSsoConfig saSsoConfig) {
|
||||
SaSsoManager.setConfig(saSsoConfig);
|
||||
public void setSaSsoConfig(@Inject(required = false) SaSsoClientConfig saSsoConfig) {
|
||||
SaSsoManager.setClientConfig(saSsoConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 Sa-Token-SSO 单点登录模块 Bean
|
||||
*
|
||||
* @param ssoTemplate saSsoTemplate对象
|
||||
* @param ssoClientTemplate ssoClientTemplate对象
|
||||
*/
|
||||
@Bean
|
||||
public void setSaSsoTemplate(@Inject(required = false) SaSsoTemplate ssoTemplate) {
|
||||
SaSsoUtil.ssoTemplate = ssoTemplate;
|
||||
SaSsoProcessor.instance.ssoTemplate = ssoTemplate;
|
||||
public void setSaSsoClientTemplate(@Inject(required = false) SaSsoClientTemplate ssoClientTemplate) {
|
||||
SaSsoClientProcessor.instance.ssoClientTemplate = ssoClientTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-8
@@ -1,7 +1,7 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
@@ -11,6 +11,9 @@ import org.noear.solon.boot.web.MimeType;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
import org.noear.solon.core.handle.Render;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author click33
|
||||
@@ -37,15 +40,21 @@ public class SsoClientController implements Render {
|
||||
*/
|
||||
@Mapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.clientDister();
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 查询我的账号信息
|
||||
@Mapping("/sso/myinfo")
|
||||
public Object myinfo() {
|
||||
Object userinfo = SaSsoUtil.getUserinfo(StpUtil.getLoginId());
|
||||
System.out.println("--------info:" + userinfo);
|
||||
return userinfo;
|
||||
@Mapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("apiType", "userinfo");
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
|
||||
// 发起请求
|
||||
Object resData = SaSsoUtil.getData(map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
}
|
||||
|
||||
// 全局异常拦截并转换
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import com.dtflys.forest.Forest;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
@@ -12,9 +12,9 @@ import org.noear.solon.annotation.Configuration;
|
||||
public class SsoConfig {
|
||||
// 配置SSO相关参数
|
||||
@Bean
|
||||
private void configSso(SaSsoConfig sso) {
|
||||
private void configSso(SaSsoClientConfig ssoClient) {
|
||||
// 配置Http请求处理器
|
||||
sso.sendHttp = url -> {
|
||||
ssoClient.sendHttp = url -> {
|
||||
System.out.println("------ 发起请求:" + url);
|
||||
return Forest.get(url).executeAsString();
|
||||
};
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ server:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
sso-client:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# 使用Http请求校验ticket
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
+11
-10
@@ -1,9 +1,9 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.sign.SaSignUtil;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
@@ -20,7 +20,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
@RestController
|
||||
public class SsoServerController {
|
||||
|
||||
/*
|
||||
/**
|
||||
* SSO-Server端:处理所有SSO相关请求
|
||||
* http://{host}:{port}/sso/auth -- 单点登录授权地址,接受参数:redirect=授权重定向地址
|
||||
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd
|
||||
@@ -29,20 +29,20 @@ public class SsoServerController {
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.serverDister();
|
||||
return SaSsoServerProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoConfig sso) {
|
||||
private void configSso(SaSsoServerConfig ssoServer) {
|
||||
|
||||
// 配置:未登录时返回的View
|
||||
sso.notLoginView = () -> {
|
||||
ssoServer.notLoginView = () -> {
|
||||
return new ModelAndView("sa-login.html");
|
||||
};
|
||||
|
||||
// 配置:登录处理函数
|
||||
sso.doLoginHandle = (name, pwd) -> {
|
||||
ssoServer.doLoginHandle = (name, pwd) -> {
|
||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||
StpUtil.login(10001);
|
||||
@@ -52,11 +52,12 @@ public class SsoServerController {
|
||||
};
|
||||
|
||||
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
|
||||
sso.sendHttp = url -> {
|
||||
ssoServer.sendHttp = url -> {
|
||||
try {
|
||||
// 发起 http 请求
|
||||
System.out.println("------ 发起请求:" + url);
|
||||
return Forest.get(url).executeAsString();
|
||||
String resStr = Forest.get(url).executeAsString();
|
||||
System.out.println("------ 请求结果:" + resStr);
|
||||
return resStr;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ sa-token:
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
sso:
|
||||
sso-server:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
|
||||
+10
-1
@@ -1,5 +1,6 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -13,7 +14,15 @@ public class SaSso1ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSso1ClientApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式一 Client端启动成功");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式一 Client 端启动成功 ----------------------");
|
||||
System.out.println("配置信息:" + SaSsoManager.getClientConfig());
|
||||
System.out.println("测试访问应用端一: http://s1.stp.com:9001");
|
||||
System.out.println("测试访问应用端二: http://s2.stp.com:9001");
|
||||
System.out.println("测试访问应用端三: http://s3.stp.com:9001");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
+5
-6
@@ -1,12 +1,11 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
@@ -18,8 +17,8 @@ public class SsoClientController {
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaSsoManager.getConfig().splicingAuthUrl();
|
||||
String solUrl = SaSsoManager.getConfig().splicingSloUrl();
|
||||
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
|
||||
String solUrl = SaSsoManager.getClientConfig().splicingSloUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
|
||||
+3
-5
@@ -5,11 +5,9 @@ server:
|
||||
# Sa-Token 配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# SSO-Server端-单点登录授权地址
|
||||
auth-url: http://sso.stp.com:9000/sso/auth
|
||||
# SSO-Server端-单点注销地址
|
||||
slo-url: http://sso.stp.com:9000/sso/signout
|
||||
sso-client:
|
||||
# SSO-Server端主机地址
|
||||
server-url: http://sso.stp.com:9000
|
||||
|
||||
# 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
alone-redis:
|
||||
|
||||
+10
-1
@@ -1,5 +1,6 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -8,7 +9,15 @@ public class SaSso2ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSso2ClientApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式二 Client端启动成功");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式二 Client 端启动成功 ----------------------");
|
||||
System.out.println("配置信息:" + SaSsoManager.getClientConfig());
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
+5
-6
@@ -1,14 +1,13 @@
|
||||
package com.pj.h5;
|
||||
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 前后台分离架构下集成SSO所需的代码 (SSO-Client端)
|
||||
* <p>(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)</p>
|
||||
@@ -34,7 +33,7 @@ public class H5Controller {
|
||||
// 根据ticket进行登录
|
||||
@RequestMapping("/sso/doLoginByTicket")
|
||||
public SaResult doLoginByTicket(String ticket) {
|
||||
Object loginId = SaSsoProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket");
|
||||
Object loginId = SaSsoClientProcessor.instance.checkTicketByMode2Or3(ticket, "/sso/doLoginByTicket");
|
||||
if(loginId != null) {
|
||||
StpUtil.login(loginId);
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
|
||||
+4
-5
@@ -1,13 +1,12 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author click33
|
||||
@@ -33,7 +32,7 @@ public class SsoClientController {
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.clientDister();
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
|
||||
+3
-4
@@ -5,12 +5,11 @@ server:
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
sso-client:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 前后端分离时用这个
|
||||
# auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html
|
||||
# 是否打开单点注销接口
|
||||
is-slo: true
|
||||
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
alone-redis:
|
||||
|
||||
+10
-1
@@ -1,5 +1,6 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -8,7 +9,15 @@ public class SaSso3ClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSso3ClientApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式三 Client端启动成功");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------");
|
||||
System.out.println("配置信息:" + SaSsoManager.getClientConfig());
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9001");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9001");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9001");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
+9
-7
@@ -1,8 +1,8 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
@@ -39,16 +39,18 @@ public class SsoClientController {
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoProcessor.instance.clientDister();
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoConfig sso) {
|
||||
private void configSso(SaSsoClientConfig ssoClient) {
|
||||
// 配置Http请求处理器
|
||||
sso.sendHttp = url -> {
|
||||
ssoClient.sendHttp = url -> {
|
||||
System.out.println("------ 发起请求:" + url);
|
||||
return Forest.get(url).executeAsString();
|
||||
String resStr = Forest.get(url).executeAsString();
|
||||
System.out.println("------ 请求结果:" + resStr);
|
||||
return resStr;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+5
-10
@@ -4,18 +4,13 @@ server:
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# SSO-Server端 统一认证地址
|
||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
||||
# sso-client 相关配置
|
||||
sso-client:
|
||||
# sso-server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 使用 Http 请求校验ticket (模式三)
|
||||
is-http: true
|
||||
# SSO-Server端 ticket校验地址
|
||||
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
|
||||
# 单点注销地址
|
||||
slo-url: http://sa-sso-server.com:9000/sso/signout
|
||||
# 查询数据地址
|
||||
get-data-url: http://sa-sso-server.com:9000/sso/getData
|
||||
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
@@ -1,585 +0,0 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 单点登录模块 配置类 Model
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.30.0
|
||||
*/
|
||||
public class SaSsoConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6541180061782004705L;
|
||||
|
||||
|
||||
// ----------------- Server端相关配置
|
||||
|
||||
/**
|
||||
* 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*/
|
||||
public String mode = "";
|
||||
|
||||
/**
|
||||
* Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long ticketTimeout = 60 * 5;
|
||||
|
||||
/**
|
||||
* 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
*/
|
||||
public String allowUrl = "*";
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean isSlo = true;
|
||||
|
||||
/**
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
|
||||
// ----------------- Client端相关配置
|
||||
|
||||
// /**
|
||||
// * 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
// */
|
||||
// public String mode = ""; // 同Server端,不再重复声明
|
||||
|
||||
/**
|
||||
* 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public String client;
|
||||
|
||||
/**
|
||||
* 配置 Server 端单点登录授权地址
|
||||
*/
|
||||
public String authUrl = "/sso/auth";
|
||||
|
||||
// /**
|
||||
// * 是否打开单点注销功能
|
||||
// */
|
||||
// public Boolean isSlo = true; // 同Server端,不再重复声明
|
||||
|
||||
// /**
|
||||
// * 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
// */
|
||||
// public Boolean isHttp = false; // 同Server端,不再重复声明
|
||||
|
||||
// /**
|
||||
// * 接口调用秘钥 (用于SSO模式三单点注销的接口通信身份校验)
|
||||
// */
|
||||
// public String secretkey; // 同Server端,不再重复声明
|
||||
|
||||
/**
|
||||
* 配置 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String checkTicketUrl = "/sso/checkTicket";
|
||||
|
||||
/**
|
||||
* 配置 Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getDataUrl = "/sso/getData";
|
||||
|
||||
/**
|
||||
* 配置 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String userinfoUrl = "/sso/userinfo";
|
||||
|
||||
/**
|
||||
* 配置 Server 端单点注销地址
|
||||
*/
|
||||
public String sloUrl = "/sso/signout";
|
||||
|
||||
/**
|
||||
* 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
*/
|
||||
public String currSsoLogin;
|
||||
|
||||
/**
|
||||
* 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String currSsoLogoutCall;
|
||||
|
||||
/**
|
||||
* 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String serverUrl;
|
||||
|
||||
/**
|
||||
* 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean isCheckSign = true;
|
||||
|
||||
|
||||
/**
|
||||
* 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @param mode /
|
||||
*/
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long getTicketTimeout() {
|
||||
return ticketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ticketTimeout Ticket有效期 (单位: 秒)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setTicketTimeout(long ticketTimeout) {
|
||||
this.ticketTimeout = ticketTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
*/
|
||||
public String getAllowUrl() {
|
||||
return allowUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setAllowUrl(String allowUrl) {
|
||||
this.allowUrl = allowUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销功能
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsSlo(Boolean isSlo) {
|
||||
this.isSlo = isSlo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean getIsHttp() {
|
||||
return isHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setIsHttp(Boolean isHttp) {
|
||||
this.isHttp = isHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public String getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param client 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public SaSsoConfig setClient(String client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端单点登录授权地址
|
||||
*/
|
||||
public String getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authUrl 配置 Server 端单点登录授权地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setAuthUrl(String authUrl) {
|
||||
this.authUrl = authUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String getCheckTicketUrl() {
|
||||
return checkTicketUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param checkTicketUrl 配置 Server 端的 ticket 校验地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setCheckTicketUrl(String checkTicketUrl) {
|
||||
this.checkTicketUrl = checkTicketUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getGetDataUrl() {
|
||||
return getDataUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param getDataUrl 配置 Server 端查询数据 getData 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setGetDataUrl(String getDataUrl) {
|
||||
this.getDataUrl = getDataUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String getUserinfoUrl() {
|
||||
return userinfoUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userinfoUrl 配置 Server 端查询 userinfo 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setUserinfoUrl(String userinfoUrl) {
|
||||
this.userinfoUrl = userinfoUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置 Server 端单点注销地址
|
||||
*/
|
||||
public String getSloUrl() {
|
||||
return sloUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sloUrl 配置 Server 端单点注销地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSloUrl(String sloUrl) {
|
||||
this.sloUrl = sloUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
*/
|
||||
public String getCurrSsoLogin() {
|
||||
return currSsoLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currSsoLogin 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setCurrSsoLogin(String currSsoLogin) {
|
||||
this.currSsoLogin = currSsoLogin;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String getCurrSsoLogoutCall() {
|
||||
return currSsoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currSsoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setCurrSsoLogoutCall(String currSsoLogoutCall) {
|
||||
this.currSsoLogoutCall = currSsoLogoutCall;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String getServerUrl() {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setServerUrl(String serverUrl) {
|
||||
this.serverUrl = serverUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean getIsCheckSign() {
|
||||
return this.isCheckSign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public SaSsoConfig setIsCheckSign(Boolean isCheckSign) {
|
||||
this.isCheckSign = isCheckSign;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoConfig ["
|
||||
+ "mode=" + mode
|
||||
+ ", ticketTimeout=" + ticketTimeout
|
||||
+ ", allowUrl=" + allowUrl
|
||||
+ ", isSlo=" + isSlo
|
||||
+ ", isHttp=" + isHttp
|
||||
+ ", client=" + client
|
||||
+ ", authUrl=" + authUrl
|
||||
+ ", checkTicketUrl=" + checkTicketUrl
|
||||
+ ", getDataUrl=" + getDataUrl
|
||||
+ ", userinfoUrl=" + userinfoUrl
|
||||
+ ", sloUrl=" + sloUrl
|
||||
+ ", currSsoLogin=" + currSsoLogin
|
||||
+ ", currSsoLogoutCall=" + currSsoLogoutCall
|
||||
+ ", serverUrl=" + serverUrl
|
||||
+ ", isCheckSign=" + isCheckSign
|
||||
+ "]";
|
||||
}
|
||||
|
||||
|
||||
// 额外添加的一些函数
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端单点登录授权地址
|
||||
*/
|
||||
public String splicingAuthUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String splicingCheckTicketUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String splicingGetDataUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String splicingUserinfoUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getUserinfoUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端单点注销地址
|
||||
*/
|
||||
public String splicingSloUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 以数组形式写入允许的授权回调地址
|
||||
* @param url 所有集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setAllow(String ...url) {
|
||||
this.allowUrl = SaFoxUtil.arrayJoin(url);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -------------------- SaSsoHandle 所有回调函数 --------------------
|
||||
|
||||
|
||||
/**
|
||||
* SSO-Server端:未登录时返回的View
|
||||
*/
|
||||
public Supplier<Object> notLoginView = () -> {
|
||||
return "当前会话在SSO-Server认证中心尚未登录(当前未配置登录视图)";
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Server端:登录函数
|
||||
*/
|
||||
public BiFunction<String, String, Object> doLoginHandle = (name, pwd) -> {
|
||||
return SaResult.error();
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
* <p> 参数:loginId, back
|
||||
* <p> 返回值:返回给前端的值
|
||||
*/
|
||||
public BiFunction<Object, String, Object> ticketResultHandle = null;
|
||||
|
||||
/**
|
||||
* SSO-Client端:发送Http请求的处理函数
|
||||
*/
|
||||
public Function<String, String> sendHttp = url -> {
|
||||
throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010);
|
||||
};
|
||||
|
||||
|
||||
// -------------------- 废弃方法 --------------------
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @param notLoginView SSO-Server端:未登录时返回的View
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaSsoConfig setNotLoginView(Supplier<Object> notLoginView) {
|
||||
this.notLoginView = notLoginView;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @return 函数 SSO-Server端:未登录时返回的View
|
||||
*/
|
||||
@Deprecated
|
||||
public Supplier<Object> getNotLoginView() {
|
||||
return notLoginView;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @param doLoginHandle SSO-Server端:登录函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaSsoConfig setDoLoginHandle(BiFunction<String, String, Object> doLoginHandle) {
|
||||
this.doLoginHandle = doLoginHandle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @return 函数 SSO-Server端:登录函数
|
||||
*/
|
||||
@Deprecated
|
||||
public BiFunction<String, String, Object> getDoLoginHandle() {
|
||||
return doLoginHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @param ticketResultHandle SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaSsoConfig setTicketResultHandle(BiFunction<Object, String, Object> ticketResultHandle) {
|
||||
this.ticketResultHandle = ticketResultHandle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @return 函数 SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
*/
|
||||
@Deprecated
|
||||
public BiFunction<Object, String, Object> getTicketResultHandle() {
|
||||
return ticketResultHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @param sendHttp SSO-Client端:发送Http请求的处理函数
|
||||
* @return 对象自身
|
||||
*/
|
||||
@Deprecated
|
||||
public SaSsoConfig setSendHttp(Function<String, String> sendHttp) {
|
||||
this.sendHttp = sendHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2> 属性为 public,请直接访问 </h2>
|
||||
* @return 函数 SSO-Client端:发送Http请求的处理函数
|
||||
*/
|
||||
@Deprecated
|
||||
public Function<String, String> getSendHttp() {
|
||||
return sendHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String getSsoLogoutCall() {
|
||||
return currSsoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ssoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoConfig setSsoLogoutCall(String ssoLogoutCall) {
|
||||
this.currSsoLogoutCall = ssoLogoutCall;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package cn.dev33.satoken.sso;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO 模块 总控类
|
||||
@@ -26,29 +27,62 @@ import cn.dev33.satoken.config.SaSsoConfig;
|
||||
public class SaSsoManager {
|
||||
|
||||
/**
|
||||
* Sso 配置 Bean
|
||||
* Sso Server 端 配置 Bean
|
||||
*/
|
||||
private volatile static SaSsoConfig config;
|
||||
public static SaSsoConfig getConfig() {
|
||||
if (config == null) {
|
||||
private volatile static SaSsoServerConfig serverConfig;
|
||||
public static SaSsoServerConfig getServerConfig() {
|
||||
if (serverConfig == null) {
|
||||
synchronized (SaSsoManager.class) {
|
||||
if (config == null) {
|
||||
setConfig(new SaSsoConfig());
|
||||
if (serverConfig == null) {
|
||||
setServerConfig(new SaSsoServerConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
return serverConfig;
|
||||
}
|
||||
public static void setConfig(SaSsoConfig config) {
|
||||
SaSsoManager.config = config;
|
||||
public static void setServerConfig(SaSsoServerConfig serverConfig) {
|
||||
SaSsoManager.serverConfig = serverConfig;
|
||||
// 如果配置了 is-check-sign=false,则打印一条警告日志
|
||||
if ( ! config.getIsCheckSign()) {
|
||||
System.err.println("-----------------------------------------------------------------------------");
|
||||
System.err.println("警告信息:");
|
||||
System.err.println("当前配置项 sa-token.sso.is-check-sign=false 代表跳过 SSO 参数签名校验");
|
||||
System.err.println("此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)");
|
||||
System.err.println("-----------------------------------------------------------------------------");
|
||||
if ( ! serverConfig.getIsCheckSign()) {
|
||||
printNoCheckSignWarningByStartup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sso Client 端 配置 Bean
|
||||
*/
|
||||
private volatile static SaSsoClientConfig clientConfig;
|
||||
public static SaSsoClientConfig getClientConfig() {
|
||||
if (clientConfig == null) {
|
||||
synchronized (SaSsoManager.class) {
|
||||
if (clientConfig == null) {
|
||||
setClientConfig(new SaSsoClientConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
return clientConfig;
|
||||
}
|
||||
public static void setClientConfig(SaSsoClientConfig clientConfig) {
|
||||
SaSsoManager.clientConfig = clientConfig;
|
||||
// 如果配置了 is-check-sign=false,则打印一条警告日志
|
||||
if ( ! clientConfig.getIsCheckSign()) {
|
||||
printNoCheckSignWarningByStartup();
|
||||
}
|
||||
}
|
||||
|
||||
// 在启动时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息
|
||||
public static void printNoCheckSignWarningByStartup() {
|
||||
System.err.println("-----------------------------------------------------------------------------");
|
||||
System.err.println("警告信息:");
|
||||
System.err.println("当前配置项 sa-token.sso.is-check-sign=false 代表跳过 SSO 参数签名校验");
|
||||
System.err.println("此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)");
|
||||
System.err.println("-----------------------------------------------------------------------------");
|
||||
}
|
||||
|
||||
// 在运行时检测到 sa-token.sso.is-check-sign=false 时,输出警告信息
|
||||
public static void printNoCheckSignWarningByRuntime() {
|
||||
System.err.println("警告信息:当前配置项 sa-token.sso.is-check-sign=false 已跳过参数签名校验," +
|
||||
"此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,511 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* SSO 请求处理器
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.32.0
|
||||
*/
|
||||
public class SaSsoProcessor {
|
||||
|
||||
/**
|
||||
* 底层 SaSsoTemplate 对象
|
||||
*/
|
||||
public SaSsoTemplate ssoTemplate = SaSsoUtil.ssoTemplate;
|
||||
|
||||
|
||||
// ----------- SSO-Server 端路由分发 -----------
|
||||
|
||||
/**
|
||||
* 分发 Server 端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object serverDister() {
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
ApiName apiName = ssoTemplate.apiName;
|
||||
|
||||
// ------------------ 路由分发 ------------------
|
||||
|
||||
// ---------- SSO-Server端:授权地址
|
||||
if(req.isPath(apiName.ssoAuth)) {
|
||||
return ssoAuth();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:RestAPI 登录接口
|
||||
if(req.isPath(apiName.ssoDoLogin)) {
|
||||
return ssoDoLogin();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:校验ticket 获取账号id
|
||||
if(req.isPath(apiName.ssoCheckTicket) && cfg.getIsHttp()) {
|
||||
return ssoCheckTicket();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:单点注销
|
||||
if(req.isPath(apiName.ssoSignout)) {
|
||||
return ssoSignout();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:授权地址
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoAuth() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
StpLogic stpLogic = ssoTemplate.getStpLogic();
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// ---------- 此处有两种情况分开处理:
|
||||
// ---- 情况1:在SSO认证中心尚未登录,需要先去登录
|
||||
if( ! stpLogic.isLogin()) {
|
||||
return cfg.notLoginView.get();
|
||||
}
|
||||
// ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式:
|
||||
String mode = req.getParam(paramName.mode, "");
|
||||
|
||||
// 方式1:直接重定向回Client端 (mode=simple)
|
||||
if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
|
||||
String redirect = req.getParam(paramName.redirect);
|
||||
ssoTemplate.checkRedirectUrl(redirect);
|
||||
return res.redirect(redirect);
|
||||
} else {
|
||||
// 方式2:带着ticket参数重定向回Client端 (mode=ticket)
|
||||
String redirectUrl = ssoTemplate.buildRedirectUrl(stpLogic.getLoginId(), req.getParam(paramName.client), req.getParam(paramName.redirect));
|
||||
return res.redirect(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:RestAPI 登录接口
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoDoLogin() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// 处理
|
||||
return cfg.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd));
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:校验ticket 获取账号id [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoCheckTicket() {
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// 1、获取参数
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String client = req.getParam(paramName.client);
|
||||
String ticket = req.getParamNotNull(paramName.ticket);
|
||||
String sloCallback = req.getParam(paramName.ssoLogoutCall);
|
||||
|
||||
// 2、校验签名
|
||||
if(ssoTemplate.getSsoConfig().getIsCheckSign()) {
|
||||
ssoTemplate.getSignTemplate().checkRequest(req,
|
||||
paramName.client, paramName.ticket, paramName.ssoLogoutCall);
|
||||
} else {
|
||||
ssoTemplate.printNoCheckSignWarning();
|
||||
}
|
||||
|
||||
// 3、校验ticket,获取 loginId
|
||||
Object loginId = ssoTemplate.checkTicket(ticket, client);
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
}
|
||||
|
||||
// 4、注册此客户端的单点注销回调URL
|
||||
ssoTemplate.registerSloCallbackUrl(loginId, sloCallback);
|
||||
|
||||
// 5、给 client 端响应结果
|
||||
return SaResult.data(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignout() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// SSO-Server端:单点注销 [用户访问式] (不带loginId参数)
|
||||
if(cfg.getIsSlo() && ! req.hasParam(paramName.loginId)) {
|
||||
return ssoSignoutByUserVisit();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [Client调用式] (带loginId参数 & isHttp=true)
|
||||
if(cfg.getIsHttp() && cfg.getIsSlo() && req.hasParam(paramName.loginId)) {
|
||||
return ssoSignoutByClientHttp();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [用户访问式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignoutByUserVisit() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
Object loginId = ssoTemplate.getStpLogic().getLoginIdDefaultNull();
|
||||
|
||||
// 单点注销
|
||||
if(SaFoxUtil.isNotEmpty(loginId)) {
|
||||
ssoTemplate.ssoLogout(loginId);
|
||||
}
|
||||
|
||||
// 完成
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [Client调用式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignoutByClientHttp() {
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// 获取参数
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String loginId = req.getParam(paramName.loginId);
|
||||
|
||||
// step.1 校验签名
|
||||
if(ssoTemplate.getSsoConfig().getIsCheckSign()) {
|
||||
ssoTemplate.getSignTemplate().checkRequest(req, paramName.loginId);
|
||||
} else {
|
||||
ssoTemplate.printNoCheckSignWarning();
|
||||
}
|
||||
|
||||
// step.2 单点注销
|
||||
ssoTemplate.ssoLogout(loginId);
|
||||
|
||||
// 响应
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
||||
// ----------- SSO-Client 端路由分发 -----------
|
||||
|
||||
/**
|
||||
* 分发 Client 端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object clientDister() {
|
||||
ApiName apiName = ssoTemplate.apiName;
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
|
||||
// ------------------ 路由分发 ------------------
|
||||
|
||||
// ---------- SSO-Client端:登录地址
|
||||
if(req.isPath(apiName.ssoLogin)) {
|
||||
return ssoLogin();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销
|
||||
if(req.isPath(apiName.ssoLogout)) {
|
||||
return ssoLogout();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销的回调 [模式三]
|
||||
if(req.isPath(apiName.ssoLogoutCall) && cfg.getIsSlo() && cfg.getIsHttp()) {
|
||||
return ssoLogoutCall();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:登录地址
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogin() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
StpLogic stpLogic = ssoTemplate.getStpLogic();
|
||||
ApiName apiName = ssoTemplate.apiName;
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// 获取参数
|
||||
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) {
|
||||
// 获取当前项目的 sso 登录地址
|
||||
// 全局配置了就是用全局的,否则使用当前请求的地址
|
||||
String currSsoLoginUrl;
|
||||
if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogin())) {
|
||||
currSsoLoginUrl = cfg.getCurrSsoLogin();
|
||||
} else {
|
||||
currSsoLoginUrl = SaHolder.getRequest().getUrl();
|
||||
}
|
||||
// 构建url
|
||||
String serverAuthUrl = ssoTemplate.buildServerAuthUrl(currSsoLoginUrl, back);
|
||||
return res.redirect(serverAuthUrl);
|
||||
} else {
|
||||
// ------- 1、校验ticket,获取 loginId
|
||||
Object loginId = checkTicket(ticket, apiName.ssoLogin);
|
||||
|
||||
// Be: 如果开发者自定义了处理逻辑
|
||||
if(cfg.ticketResultHandle != null) {
|
||||
return cfg.ticketResultHandle.apply(loginId, back);
|
||||
}
|
||||
|
||||
// ------- 2、如果 loginId 无值,说明 ticket 无效
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoErrorCode.CODE_30004);
|
||||
} else {
|
||||
// 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址
|
||||
stpLogic.login(loginId);
|
||||
return res.redirect(back);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogout() {
|
||||
// 获取对象
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
|
||||
// ---------- SSO-Client端:单点注销 [模式二]
|
||||
if(cfg.getIsSlo() && ! cfg.getIsHttp()) {
|
||||
return ssoLogoutType2();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销 [模式三]
|
||||
if(cfg.getIsSlo() && cfg.getIsHttp()) {
|
||||
return ssoLogoutType3();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销 [模式二]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogoutType2() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoTemplate.getStpLogic();
|
||||
|
||||
// 开始处理
|
||||
if(stpLogic.isLogin()) {
|
||||
stpLogic.logout(stpLogic.getLoginId());
|
||||
}
|
||||
|
||||
// 返回
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销 [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogoutType3() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoTemplate.getStpLogic();
|
||||
|
||||
// 如果未登录,则无需注销
|
||||
if( ! stpLogic.isLogin()) {
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
// 调用 sso-server 认证中心单点注销API
|
||||
String url = ssoTemplate.buildSloUrl(stpLogic.getLoginId());
|
||||
SaResult result = ssoTemplate.request(url);
|
||||
|
||||
// 校验响应状态码
|
||||
if(SaResult.CODE_SUCCESS == result.getCode()) {
|
||||
// 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀
|
||||
if(stpLogic.isLogin()) {
|
||||
stpLogic.logout();
|
||||
}
|
||||
return ssoLogoutBack(req, res);
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30006);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销的回调 [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogoutCall() {
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
StpLogic stpLogic = ssoTemplate.getStpLogic();
|
||||
|
||||
// 获取参数
|
||||
String loginId = req.getParamNotNull(paramName.loginId);
|
||||
|
||||
// 校验参数签名
|
||||
if(ssoTemplate.getSsoConfig().getIsCheckSign()) {
|
||||
ssoTemplate.getSignTemplate().checkRequest(req, paramName.loginId);
|
||||
} else {
|
||||
ssoTemplate.printNoCheckSignWarning();
|
||||
}
|
||||
|
||||
// 注销当前应用端会话
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 响应
|
||||
return SaResult.ok("单点注销回调成功");
|
||||
}
|
||||
|
||||
|
||||
// ----------- 工具方法
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public Object ssoLogoutBack(SaRequest req, SaResponse res) {
|
||||
ParamName paramName = ssoTemplate.paramName;
|
||||
|
||||
/*
|
||||
* 三种情况:
|
||||
* 1. 有back参数,值为SELF -> 回退一级并刷新
|
||||
* 2. 有back参数,值为url -> 跳转到此url地址
|
||||
* 3. 无back参数 -> 返回json数据
|
||||
*/
|
||||
String back = req.getParam(paramName.back);
|
||||
if(SaFoxUtil.isNotEmpty(back)) {
|
||||
if(back.equals(SaSsoConsts.SELF)) {
|
||||
return "<script>if(document.referrer != location.href){ location.replace(document.referrer || '/'); }</script>";
|
||||
}
|
||||
return res.redirect(back);
|
||||
} else {
|
||||
return SaResult.ok("单点注销成功");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId
|
||||
* @param ticket ticket码
|
||||
* @param currUri 当前路由的uri,用于计算单点注销回调地址
|
||||
* @return loginId
|
||||
*/
|
||||
public Object checkTicket(String ticket, String currUri) {
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
ApiName apiName = ssoTemplate.apiName;
|
||||
|
||||
// --------- 两种模式
|
||||
if(cfg.getIsHttp()) {
|
||||
// q1、使用模式三:使用 http 请求从认证中心校验ticket
|
||||
|
||||
// 计算当前 sso-client 的单点注销回调地址
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.getIsSlo()) {
|
||||
// 如果配置了回调地址,就使用配置的值:
|
||||
if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) {
|
||||
ssoLogoutCall = cfg.getCurrSsoLogoutCall();
|
||||
}
|
||||
// 如果提供了当前 uri,则根据此值来计算:
|
||||
else if(SaFoxUtil.isNotEmpty(currUri)) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall);
|
||||
}
|
||||
// 否则视为不注册单点注销回调地址
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
// 构建请求URL
|
||||
String checkUrl = ssoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
|
||||
// 发起请求
|
||||
SaResult result = ssoTemplate.request(checkUrl);
|
||||
|
||||
// 校验
|
||||
if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) {
|
||||
return result.getData();
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005);
|
||||
}
|
||||
} else {
|
||||
// q2、使用模式二:直连Redis校验ticket
|
||||
return ssoTemplate.checkTicket(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------- 全局默认实例 -----------
|
||||
|
||||
/**
|
||||
* 全局默认实例
|
||||
*/
|
||||
public static SaSsoProcessor instance = new SaSsoProcessor();
|
||||
|
||||
}
|
||||
@@ -1,647 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.sign.SaSignTemplate;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO 单点登录模块
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.30.0
|
||||
*/
|
||||
public class SaSsoTemplate {
|
||||
|
||||
// ---------------------- 全局配置 ----------------------
|
||||
|
||||
/**
|
||||
* 所有 API 名称
|
||||
*/
|
||||
public ApiName apiName = new ApiName();
|
||||
|
||||
/**
|
||||
* 所有参数名称
|
||||
*/
|
||||
public ParamName paramName = new ParamName();
|
||||
|
||||
/**
|
||||
* @param paramName 替换 paramName 对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoTemplate setParamName(ParamName paramName) {
|
||||
this.paramName = paramName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName 替换 apiName 对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoTemplate setApiName(ApiName apiName) {
|
||||
this.apiName = apiName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的会话对象
|
||||
* @return /
|
||||
*/
|
||||
public StpLogic getStpLogic() {
|
||||
return StpUtil.stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的配置对象
|
||||
* @return /
|
||||
*/
|
||||
public SaSsoConfig getSsoConfig() {
|
||||
return SaSsoManager.getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的 API 签名对象
|
||||
* @return /
|
||||
*/
|
||||
public SaSignTemplate getSignTemplate() {
|
||||
return SaManager.getSaSignTemplate();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- Ticket 操作 ----------------------
|
||||
|
||||
/**
|
||||
* 保存 Ticket 关联的 loginId
|
||||
* @param ticket ticket码
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void saveTicket(String ticket, Object loginId) {
|
||||
// 保存 ticket -> loginId 的关系
|
||||
long ticketTimeout = SaSsoManager.getConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 Ticket 索引 (id 反查 ticket)
|
||||
* @param ticket ticket码
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void saveTicketIndex(String ticket, Object loginId) {
|
||||
long ticketTimeout = SaSsoManager.getConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketIndexKey(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 = SaSsoManager.getConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 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 关联的 client
|
||||
* @param ticket Ticket码
|
||||
*/
|
||||
public void deleteTicketToClient(String ticket) {
|
||||
if(ticket == null) {
|
||||
return;
|
||||
}
|
||||
SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 ticket 指向的 loginId,如果 ticket 码无效则返回 null
|
||||
* @param ticket Ticket码
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object getLoginId(String ticket) {
|
||||
if(SaFoxUtil.isEmpty(ticket)) {
|
||||
return null;
|
||||
}
|
||||
return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 ticket 指向的 loginId,并转换为指定类型
|
||||
* @param <T> 要转换的类型
|
||||
* @param ticket Ticket码
|
||||
* @param cs 要转换的类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public <T> T getLoginId(String ticket, Class<T> cs) {
|
||||
return SaFoxUtil.getValueByType(getLoginId(ticket), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 指定 loginId 其所属的 ticket 值
|
||||
* @param loginId 账号id
|
||||
* @return Ticket值
|
||||
*/
|
||||
public String getTicketValue(Object loginId) {
|
||||
if(loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return SaManager.getSaTokenDao().get(splicingTicketIndexKey(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));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* 根据 账号id 创建一个 Ticket码
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @return Ticket码
|
||||
*/
|
||||
public String createTicket(Object loginId, String client) {
|
||||
// 创建 Ticket
|
||||
String ticket = randomTicket(loginId);
|
||||
|
||||
// 保存 Ticket
|
||||
saveTicket(ticket, loginId);
|
||||
saveTicketIndex(ticket, loginId);
|
||||
saveTicketToClient(ticket, client);
|
||||
|
||||
// 返回 Ticket
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除
|
||||
* @param ticket Ticket码
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object checkTicket(String ticket) {
|
||||
return checkTicket(ticket, getSsoConfig().getClient());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除
|
||||
* @param ticket Ticket码
|
||||
* @param client client 标识
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object checkTicket(String ticket, String client) {
|
||||
// 读取 loginId
|
||||
String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||
|
||||
if(loginId != null) {
|
||||
|
||||
// 解析出这个 ticket 关联的 Client
|
||||
String ticketClient = getTicketToClient(ticket);
|
||||
|
||||
// 如果指定了 client 标识,则校验一下 client 标识是否一致
|
||||
if(SaFoxUtil.isNotEmpty(client) && SaFoxUtil.notEquals(client, ticketClient)) {
|
||||
throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket)
|
||||
.setCode(SaSsoErrorCode.CODE_30011);
|
||||
}
|
||||
|
||||
// 删除 ticket 信息,使其只有一次性有效
|
||||
deleteTicket(ticket);
|
||||
deleteTicketIndex(loginId);
|
||||
deleteTicketToClient(ticket);
|
||||
}
|
||||
|
||||
//
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机一个 Ticket码
|
||||
* @param loginId 账号id
|
||||
* @return Ticket码
|
||||
*/
|
||||
public String randomTicket(Object loginId) {
|
||||
return SaFoxUtil.getRandomString(64);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return see note
|
||||
*/
|
||||
public String getAllowUrl() {
|
||||
// 默认从配置文件中返回
|
||||
return SaSsoManager.getConfig().getAllowUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验重定向url合法性
|
||||
* @param url 下放ticket的url地址
|
||||
*/
|
||||
public void checkRedirectUrl(String url) {
|
||||
|
||||
// 1、是否是一个有效的url
|
||||
if( ! SaFoxUtil.isUrl(url) ) {
|
||||
throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001);
|
||||
}
|
||||
|
||||
// 2、截取掉?后面的部分
|
||||
int qIndex = url.indexOf("?");
|
||||
if(qIndex != -1) {
|
||||
url = url.substring(0, qIndex);
|
||||
}
|
||||
|
||||
// 3、是否在[允许地址列表]之中
|
||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||
if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) {
|
||||
throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002);
|
||||
}
|
||||
|
||||
// 校验通过 √
|
||||
}
|
||||
|
||||
|
||||
// ------------------- SSO 模式三相关 -------------------
|
||||
|
||||
/**
|
||||
* 为指定账号id注册单点注销回调URL
|
||||
* @param loginId 账号id
|
||||
* @param sloCallbackUrl 单点注销时的回调URL
|
||||
*/
|
||||
public void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) {
|
||||
if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) {
|
||||
return;
|
||||
}
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId);
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new);
|
||||
urlSet.add(sloCallbackUrl);
|
||||
session.set(SaSsoConsts.SLO_CALLBACK_SET_KEY, urlSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号单点注销
|
||||
* @param loginId 指定账号
|
||||
*/
|
||||
public void ssoLogout(Object loginId) {
|
||||
|
||||
// 如果这个账号尚未登录,则无操作
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// step.1 遍历通知 Client 端注销会话
|
||||
SaSsoConfig cfg = SaSsoManager.getConfig();
|
||||
Set<String> urlSet = session.get(SaSsoConsts.SLO_CALLBACK_SET_KEY, HashSet::new);
|
||||
for (String url : urlSet) {
|
||||
url = joinLoginIdAndSign(url, loginId);
|
||||
cfg.sendHttp.apply(url);
|
||||
}
|
||||
|
||||
// step.2 Server端注销
|
||||
getStpLogic().logout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置的 getData 地址,查询数据
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getData(Map<String, Object> paramMap) {
|
||||
String getDataUrl = SaSsoManager.getConfig().splicingGetDataUrl();
|
||||
return getData(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param path 自定义 path
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getData(String path, Map<String, Object> paramMap) {
|
||||
String url = buildCustomPathUrl(path, paramMap);
|
||||
return SaSsoManager.getConfig().sendHttp.apply(url);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- 构建URL ----------------------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 单点登录地址
|
||||
* @param clientLoginUrl Client端登录地址
|
||||
* @param back 回调路径
|
||||
* @return [SSO-Server端-认证地址 ]
|
||||
*/
|
||||
public String buildServerAuthUrl(String clientLoginUrl, String back) {
|
||||
|
||||
// 服务端认证地址
|
||||
String serverUrl = SaSsoManager.getConfig().splicingAuthUrl();
|
||||
|
||||
// 拼接客户端标识
|
||||
String client = SaSsoManager.getConfig().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 + "=" + back) ) {
|
||||
clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back);
|
||||
}
|
||||
|
||||
// 返回
|
||||
return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端向Client下放ticket的地址
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @param redirect Client端提供的重定向地址
|
||||
* @return see note
|
||||
*/
|
||||
public String buildRedirectUrl(Object loginId, String client, String redirect) {
|
||||
|
||||
// 校验 重定向地址 是否合法
|
||||
checkRedirectUrl(redirect);
|
||||
|
||||
// 删掉 旧Ticket
|
||||
deleteTicket(getTicketValue(loginId));
|
||||
|
||||
// 创建 新Ticket
|
||||
String ticket = createTicket(loginId, client);
|
||||
|
||||
// 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket)
|
||||
return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
|
||||
* @param url url
|
||||
* @return 编码过后的url
|
||||
*/
|
||||
public String encodeBackParam(String url) {
|
||||
|
||||
// 获取back参数所在位置
|
||||
int index = url.indexOf("?" + paramName.back + "=");
|
||||
if(index == -1) {
|
||||
index = url.indexOf("&" + paramName.back + "=");
|
||||
if(index == -1) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始编码
|
||||
int length = paramName.back.length() + 2;
|
||||
String back = url.substring(index + length);
|
||||
back = SaFoxUtil.encodeUrl(back);
|
||||
|
||||
// 放回url中
|
||||
url = url.substring(0, index + length) + back;
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:校验ticket的URL
|
||||
* <p> 在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id
|
||||
* @param ticket ticket码
|
||||
* @param ssoLogoutCallUrl 单点注销时的回调URL
|
||||
* @return 构建完毕的URL
|
||||
*/
|
||||
public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
|
||||
|
||||
// s1:先收集应该增加的参数:client、ticket、ssoLogoutCall
|
||||
Map<String, Object> paramMap = new TreeMap<>();
|
||||
|
||||
// 拼接 client 参数
|
||||
String client = getSsoConfig().getClient();
|
||||
if(SaFoxUtil.isNotEmpty(client)) {
|
||||
paramMap.put(paramName.client, client);
|
||||
}
|
||||
|
||||
// 拼接 ticket 参数
|
||||
paramMap.put(paramName.ticket, ticket);
|
||||
|
||||
// 拼接单点注销时的回调 URL
|
||||
if(ssoLogoutCallUrl != null) {
|
||||
paramMap.put(paramName.ssoLogoutCall, ssoLogoutCallUrl);
|
||||
}
|
||||
|
||||
// s2:构建 url 地址
|
||||
String url = SaSsoManager.getConfig().splicingCheckTicketUrl();
|
||||
String paramStr = getSignTemplate().addSignParamsAndJoin(paramMap);
|
||||
String finalUrl = SaFoxUtil.joinParam(url, paramStr);
|
||||
|
||||
// 返回
|
||||
return finalUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:单点注销URL
|
||||
* @param loginId 要注销的账号id
|
||||
* @return 单点注销URL
|
||||
*/
|
||||
public String buildSloUrl(Object loginId) {
|
||||
String url = SaSsoManager.getConfig().splicingSloUrl();
|
||||
return joinLoginIdAndSign(url, loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 getData 地址,带签名等参数
|
||||
* @param paramMap 查询参数
|
||||
* @return /
|
||||
*/
|
||||
public String buildGetDataUrl(Map<String, Object> paramMap) {
|
||||
String getDataUrl = SaSsoManager.getConfig().getGetDataUrl();
|
||||
return buildCustomPathUrl(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param paramMap 请求参数
|
||||
* @return /
|
||||
*/
|
||||
public String buildCustomPathUrl(String path, Map<String, Object> paramMap) {
|
||||
// 如果path不是以 http 开头,那么就拼接上 serverUrl
|
||||
String url = path;
|
||||
if( ! url.startsWith("http") ) {
|
||||
String serverUrl = SaSsoManager.getConfig().getServerUrl();
|
||||
SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012);
|
||||
url = SaFoxUtil.spliceTwoUrl(serverUrl, path);
|
||||
}
|
||||
|
||||
// 添加签名等参数,并序列化
|
||||
return joinParamMapAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 发起请求 -------------------
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
* @param url 请求地址
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public SaResult request(String url) {
|
||||
String body = SaSsoManager.getConfig().sendHttp.apply(url);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(body);
|
||||
return new SaResult(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面
|
||||
* @param url 请求地址
|
||||
* @param paramMap 请求原始参数列表
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public String joinParamMapAndSign(String url, Map<String, Object> paramMap) {
|
||||
// 在参数列表中追加:时间戳、随机字符串、参数签名
|
||||
SaManager.getSaSignTemplate().addSignParams(paramMap);
|
||||
|
||||
// 将参数列表序列化为kv字符串
|
||||
String signParams = SaManager.getSaSignTemplate().joinParams(paramMap);
|
||||
|
||||
// 将kv字符串拼接到url后面
|
||||
return SaFoxUtil.joinParam(url, signParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 url 拼接 loginId 参数,并拼接 sign 等参数
|
||||
* @param url 链接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public String joinLoginIdAndSign(String url, Object loginId) {
|
||||
Map<String, Object> paramMap = new LinkedHashMap<>();
|
||||
paramMap.put(paramName.loginId, loginId);
|
||||
return joinParamMapAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key -------------------
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 账号Id
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketSaveKey(String ticket) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 所属的 client
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketToClientSaveKey(String ticket) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:账号Id 反查 Ticket
|
||||
* @param id 账号id
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketIndexKey(Object id) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id;
|
||||
}
|
||||
|
||||
|
||||
// 在 sa-token.sso.is-check-sign=false 时,输出警告信息
|
||||
public void printNoCheckSignWarning() {
|
||||
System.err.println("警告信息:当前配置项 sa-token.sso.is-check-sign=false 已跳过参数签名校验," +
|
||||
"此模式仅为方便本地调试使用,生产环境下请务必配置为 true (配置项默认为true)");
|
||||
}
|
||||
|
||||
|
||||
// -------- 以下方法已废弃,仅为兼容旧版本而保留 --------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
*/
|
||||
@Deprecated
|
||||
public String buildUserinfoUrl(Object loginId) {
|
||||
String userinfoUrl = SaSsoManager.getConfig().splicingUserinfoUrl();
|
||||
return joinLoginIdAndSign(userinfoUrl, loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @return 账号资料
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getUserinfo(Object loginId) {
|
||||
String url = buildUserinfoUrl(loginId);
|
||||
return request(url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
||||
import cn.dev33.satoken.sso.function.TicketResultHandleFunction;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 单点登录模块 配置类 (Client端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.30.0
|
||||
*/
|
||||
public class SaSsoClientConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6541180061782004705L;
|
||||
|
||||
/**
|
||||
* 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*/
|
||||
public String mode = "";
|
||||
|
||||
/**
|
||||
* 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public String client;
|
||||
|
||||
/**
|
||||
* 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String serverUrl;
|
||||
|
||||
/**
|
||||
* 单独配置 Server 端单点登录授权地址
|
||||
*/
|
||||
public String authUrl = "/sso/auth";
|
||||
|
||||
/**
|
||||
* 单独配置 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String checkTicketUrl = "/sso/checkTicket";
|
||||
|
||||
/**
|
||||
* 单独配置 Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getDataUrl = "/sso/getData";
|
||||
|
||||
/**
|
||||
* 单独配置 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String userinfoUrl = "/sso/userinfo";
|
||||
|
||||
/**
|
||||
* 单独配置 Server 端单点注销地址
|
||||
*/
|
||||
public String sloUrl = "/sso/signout";
|
||||
|
||||
/**
|
||||
* 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
*/
|
||||
public String currSsoLogin;
|
||||
|
||||
/**
|
||||
* 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String currSsoLogoutCall;
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean isSlo = true;
|
||||
|
||||
/**
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean isCheckSign = true;
|
||||
|
||||
|
||||
// get set
|
||||
|
||||
/**
|
||||
* 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @param mode /
|
||||
*/
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销功能
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setIsSlo(Boolean isSlo) {
|
||||
this.isSlo = isSlo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean getIsHttp() {
|
||||
return isHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setIsHttp(Boolean isHttp) {
|
||||
this.isHttp = isHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public String getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param client 当前 Client 名称标识,用于和 ticket 码的互相锁定
|
||||
*/
|
||||
public SaSsoClientConfig setClient(String client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端单点登录授权地址
|
||||
*/
|
||||
public String getAuthUrl() {
|
||||
return authUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authUrl 配置 Server 端单点登录授权地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setAuthUrl(String authUrl) {
|
||||
this.authUrl = authUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String getCheckTicketUrl() {
|
||||
return checkTicketUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param checkTicketUrl 配置 Server 端的 ticket 校验地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setCheckTicketUrl(String checkTicketUrl) {
|
||||
this.checkTicketUrl = checkTicketUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String getGetDataUrl() {
|
||||
return getDataUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param getDataUrl 配置 Server 端查询数据 getData 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setGetDataUrl(String getDataUrl) {
|
||||
this.getDataUrl = getDataUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String getUserinfoUrl() {
|
||||
return userinfoUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userinfoUrl 配置 Server 端查询 userinfo 地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setUserinfoUrl(String userinfoUrl) {
|
||||
this.userinfoUrl = userinfoUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置 Server 端单点注销地址
|
||||
*/
|
||||
public String getSloUrl() {
|
||||
return sloUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sloUrl 配置 Server 端单点注销地址
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setSloUrl(String sloUrl) {
|
||||
this.sloUrl = sloUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
*/
|
||||
public String getCurrSsoLogin() {
|
||||
return currSsoLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currSsoLogin 配置当前 Client 端的登录地址(为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setCurrSsoLogin(String currSsoLogin) {
|
||||
this.currSsoLogin = currSsoLogin;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
*/
|
||||
public String getCurrSsoLogoutCall() {
|
||||
return currSsoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currSsoLogoutCall 配置当前 Client 端的单点注销回调URL (为空时自动获取)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setCurrSsoLogoutCall(String currSsoLogoutCall) {
|
||||
this.currSsoLogoutCall = currSsoLogoutCall;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 配置的 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
*/
|
||||
public String getServerUrl() {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverUrl 配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setServerUrl(String serverUrl) {
|
||||
this.serverUrl = serverUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean getIsCheckSign() {
|
||||
return this.isCheckSign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public SaSsoClientConfig setIsCheckSign(Boolean isCheckSign) {
|
||||
this.isCheckSign = isCheckSign;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoConfig ["
|
||||
+ "mode=" + mode
|
||||
+ ", client=" + client
|
||||
+ ", serverUrl=" + serverUrl
|
||||
+ ", authUrl=" + authUrl
|
||||
+ ", checkTicketUrl=" + checkTicketUrl
|
||||
+ ", getDataUrl=" + getDataUrl
|
||||
+ ", userinfoUrl=" + userinfoUrl
|
||||
+ ", sloUrl=" + sloUrl
|
||||
+ ", currSsoLogin=" + currSsoLogin
|
||||
+ ", currSsoLogoutCall=" + currSsoLogoutCall
|
||||
+ ", isSlo=" + isSlo
|
||||
+ ", isHttp=" + isHttp
|
||||
+ ", isCheckSign=" + isCheckSign
|
||||
+ "]";
|
||||
}
|
||||
|
||||
// 额外添加的一些函数
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端单点登录授权地址
|
||||
*/
|
||||
public String splicingAuthUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getAuthUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端的 ticket 校验地址
|
||||
*/
|
||||
public String splicingCheckTicketUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getCheckTicketUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询数据 getData 地址
|
||||
*/
|
||||
public String splicingGetDataUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getGetDataUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端查询 userinfo 地址
|
||||
*/
|
||||
public String splicingUserinfoUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getUserinfoUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取拼接url:Server 端单点注销地址
|
||||
*/
|
||||
public String splicingSloUrl() {
|
||||
return SaFoxUtil.spliceTwoUrl(getServerUrl(), getSloUrl());
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 所有回调函数 --------------------
|
||||
|
||||
/**
|
||||
* SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
* <p> 参数:loginId, back
|
||||
* <p> 返回值:返回给前端的值
|
||||
*/
|
||||
public TicketResultHandleFunction ticketResultHandle = null;
|
||||
|
||||
/**
|
||||
* SSO-Client端:发送Http请求的处理函数
|
||||
*/
|
||||
public SendHttpFunction sendHttp = url -> {
|
||||
throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010);
|
||||
};
|
||||
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.function.DoLoginHandleFunction;
|
||||
import cn.dev33.satoken.sso.function.NotLoginViewFunction;
|
||||
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 单点登录模块 配置类 (Server端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoServerConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6541180061782004705L;
|
||||
|
||||
|
||||
// ----------------- Server端相关配置
|
||||
|
||||
/**
|
||||
* 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*/
|
||||
public String mode = "";
|
||||
|
||||
/**
|
||||
* Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long ticketTimeout = 60 * 5;
|
||||
|
||||
/**
|
||||
* 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
*/
|
||||
public String allowUrl = "*";
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean isSlo = true;
|
||||
|
||||
/**
|
||||
* 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean isCheckSign = true;
|
||||
|
||||
|
||||
// get set
|
||||
|
||||
/**
|
||||
* 获取 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public String getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 指定当前系统集成 SSO 时使用的模式(约定型配置项,不对代码逻辑产生任何影响)
|
||||
*
|
||||
* @param mode /
|
||||
*/
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Ticket有效期 (单位: 秒)
|
||||
*/
|
||||
public long getTicketTimeout() {
|
||||
return ticketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ticketTimeout Ticket有效期 (单位: 秒)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoServerConfig setTicketTimeout(long ticketTimeout) {
|
||||
this.ticketTimeout = ticketTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
*/
|
||||
public String getAllowUrl() {
|
||||
return allowUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowUrl 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoServerConfig setAllowUrl(String allowUrl) {
|
||||
this.allowUrl = allowUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开单点注销功能
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销功能
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoServerConfig setIsSlo(Boolean isSlo) {
|
||||
this.isSlo = isSlo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
*/
|
||||
public Boolean getIsHttp() {
|
||||
return isHttp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isHttp 是否打开模式三(此值为 true 时将使用 http 请求:校验ticket值、单点注销、获取userinfo)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoServerConfig setIsHttp(Boolean isHttp) {
|
||||
this.isHttp = isHttp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @return isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public Boolean getIsCheckSign() {
|
||||
return this.isCheckSign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*
|
||||
* @param isCheckSign 是否校验参数签名(方便本地调试用的一个配置项,生产环境请务必为true)
|
||||
*/
|
||||
public SaSsoServerConfig setIsCheckSign(Boolean isCheckSign) {
|
||||
this.isCheckSign = isCheckSign;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以数组形式写入允许的授权回调地址
|
||||
* @param url 所有集合
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoServerConfig setAllow(String ...url) {
|
||||
this.allowUrl = SaFoxUtil.arrayJoin(url);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoServerConfig ["
|
||||
+ "mode=" + mode
|
||||
+ ", ticketTimeout=" + ticketTimeout
|
||||
+ ", allowUrl=" + allowUrl
|
||||
+ ", isSlo=" + isSlo
|
||||
+ ", isHttp=" + isHttp
|
||||
+ ", isCheckSign=" + isCheckSign
|
||||
+ "]";
|
||||
}
|
||||
|
||||
|
||||
// -------------------- 所有回调函数 --------------------
|
||||
|
||||
|
||||
/**
|
||||
* SSO-Server端:未登录时返回的View
|
||||
*/
|
||||
public NotLoginViewFunction notLoginView = () -> {
|
||||
return "当前会话在SSO-Server认证中心尚未登录(当前未配置登录视图)";
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Server端:登录函数
|
||||
*/
|
||||
public DoLoginHandleFunction doLoginHandle = (name, pwd) -> {
|
||||
return SaResult.error();
|
||||
};
|
||||
|
||||
/**
|
||||
* SSO-Server端:发送Http请求的处理函数
|
||||
*/
|
||||
public SendHttpFunction sendHttp = url -> {
|
||||
throw new SaSsoException("请配置 Http 请求处理器").setCode(SaSsoErrorCode.CODE_30010);
|
||||
};
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* 函数式接口:SSO-Server端:未登录时返回的View
|
||||
*
|
||||
* <p> 参数:账号、密码 </p>
|
||||
* <p> 返回:登录结果 </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DoLoginHandleFunction extends BiFunction<String, String, Object> {
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 函数式接口:SSO-Server端:未登录时返回的View
|
||||
*
|
||||
* <p> 参数:无 </p>
|
||||
* <p> 返回:未登录时的 View 视图 </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface NotLoginViewFunction extends Supplier<Object> {
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 函数式接口:发送Http请求的处理函数
|
||||
*
|
||||
* <p> 参数:要请求的url </p>
|
||||
* <p> 返回:请求结果 </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SendHttpFunction extends Function<String, String> {
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.function;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* 函数式接口:SSO-Client端:自定义校验Ticket返回值的处理逻辑 (每次从认证中心获取校验Ticket的结果后调用)
|
||||
*
|
||||
* <p> 参数:loginId, back </p>
|
||||
* <p> 返回:返回给前端的值 </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TicketResultHandleFunction extends BiFunction<Object, String, Object> {
|
||||
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
|
||||
/**
|
||||
* Sa-Token SSO Model
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoClientModel {
|
||||
|
||||
/**
|
||||
* 客户端标识
|
||||
*/
|
||||
public String client;
|
||||
|
||||
/**
|
||||
* 单点注销回调url
|
||||
*/
|
||||
public String ssoLogoutCall;
|
||||
|
||||
/**
|
||||
* 此 client 注册信息的时间,13位时间戳
|
||||
*/
|
||||
public Long regTime;
|
||||
|
||||
public SaSsoClientModel() {
|
||||
}
|
||||
|
||||
public SaSsoClientModel(String client, String ssoLogoutCall) {
|
||||
this.client = client;
|
||||
this.ssoLogoutCall = ssoLogoutCall;
|
||||
this.regTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 客户端标识
|
||||
*
|
||||
* @return client 客户端标识
|
||||
*/
|
||||
public String getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 客户端标识
|
||||
*
|
||||
* @param client 客户端标识
|
||||
*/
|
||||
public void setClient(String client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 单点注销回调url
|
||||
*
|
||||
* @return ssoLogoutCall 单点注销回调url
|
||||
*/
|
||||
public String getSsoLogoutCall() {
|
||||
return this.ssoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 单点注销回调url
|
||||
*
|
||||
* @param ssoLogoutCall 单点注销回调url
|
||||
*/
|
||||
public void setSsoLogoutCall(String ssoLogoutCall) {
|
||||
this.ssoLogoutCall = ssoLogoutCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 此 client 注册信息的时间,13位时间戳
|
||||
*
|
||||
* @return regTime 此 client 注册信息的时间,13位时间戳
|
||||
*/
|
||||
public Long getRegTime() {
|
||||
return this.regTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 此 client 注册信息的时间,13位时间戳
|
||||
*
|
||||
* @param regTime 此 client 注册信息的时间,13位时间戳
|
||||
*/
|
||||
public void setRegTime(Long regTime) {
|
||||
this.regTime = regTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SaSsoClientModel{" +
|
||||
"client='" + client + '\'' +
|
||||
", ssoLogoutCall='" + ssoLogoutCall + '\'' +
|
||||
", regTime='" + regTime + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* SSO 请求处理器 (Client端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoClientProcessor {
|
||||
|
||||
/**
|
||||
* 全局默认实例
|
||||
*/
|
||||
public static SaSsoClientProcessor instance = new SaSsoClientProcessor();
|
||||
|
||||
/**
|
||||
* 底层 SaSsoClientTemplate 对象
|
||||
*/
|
||||
public SaSsoClientTemplate ssoClientTemplate = new SaSsoClientTemplate();
|
||||
|
||||
// ----------- SSO-Client 端路由分发 -----------
|
||||
|
||||
/**
|
||||
* 分发 Client 端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object dister() {
|
||||
ApiName apiName = ssoClientTemplate.apiName;
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
|
||||
|
||||
// ------------------ 路由分发 ------------------
|
||||
|
||||
// ---------- SSO-Client端:登录地址
|
||||
if(req.isPath(apiName.ssoLogin)) {
|
||||
return ssoLogin();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销
|
||||
if(req.isPath(apiName.ssoLogout)) {
|
||||
return ssoLogout();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销的回调 [模式三]
|
||||
if(req.isPath(apiName.ssoLogoutCall) && cfg.getIsSlo() && cfg.getIsHttp()) {
|
||||
return ssoLogoutCall();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:登录地址
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogin() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
ApiName apiName = ssoClientTemplate.apiName;
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
|
||||
// 获取参数
|
||||
String back = req.getParam(paramName.back, "/");
|
||||
String ticket = req.getParam(paramName.ticket);
|
||||
|
||||
// 如果当前Client端已经登录,则无需访问SSO认证中心,可以直接返回
|
||||
if(stpLogic.isLogin()) {
|
||||
return res.redirect(back);
|
||||
}
|
||||
/*
|
||||
* 此时有两种情况:
|
||||
* 情况1:ticket无值,说明此请求是Client端访问,需要重定向至SSO认证中心
|
||||
* 情况2:ticket有值,说明此请求从SSO认证中心重定向而来,需要根据ticket进行登录
|
||||
*/
|
||||
if(ticket == null) {
|
||||
// 获取当前项目的 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 {
|
||||
// ------- 1、校验ticket,获取 loginId
|
||||
Object loginId = checkTicketByMode2Or3(ticket, apiName.ssoLogin);
|
||||
|
||||
// Be: 如果开发者自定义了处理逻辑
|
||||
if(cfg.ticketResultHandle != null) {
|
||||
return cfg.ticketResultHandle.apply(loginId, back);
|
||||
}
|
||||
|
||||
// ------- 2、如果 loginId 无值,说明 ticket 无效
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
throw new SaSsoException("无效ticket:" + ticket).setCode(SaSsoErrorCode.CODE_30004);
|
||||
} else {
|
||||
// 3、如果 loginId 有值,说明 ticket 有效,此时进行登录并重定向至back地址
|
||||
stpLogic.login(loginId);
|
||||
return res.redirect(back);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogout() {
|
||||
// 获取对象
|
||||
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
|
||||
|
||||
// ---------- SSO-Client端:单点注销 [模式二]
|
||||
if(cfg.getIsSlo() && ! cfg.getIsHttp()) {
|
||||
return ssoLogoutType2();
|
||||
}
|
||||
|
||||
// ---------- SSO-Client端:单点注销 [模式三]
|
||||
if(cfg.getIsSlo() && cfg.getIsHttp()) {
|
||||
return ssoLogoutType3();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销 [模式二]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogoutType2() {
|
||||
// 获取对象
|
||||
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 ssoLogoutType3() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
|
||||
// 如果未登录,则无需注销
|
||||
if( ! stpLogic.isLogin()) {
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
// 调用 sso-server 认证中心单点注销API
|
||||
String url = ssoClientTemplate.buildSloUrl(stpLogic.getLoginId());
|
||||
SaResult result = ssoClientTemplate.request(url);
|
||||
|
||||
// 校验响应状态码
|
||||
if(SaResult.CODE_SUCCESS == result.getCode()) {
|
||||
// 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀
|
||||
if(stpLogic.isLogin()) {
|
||||
stpLogic.logout();
|
||||
}
|
||||
return ssoLogoutBack(req, res);
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30006);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Client端:单点注销的回调 [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoLogoutCall() {
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
SaSsoClientConfig ssoConfig = ssoClientTemplate.getClientConfig();
|
||||
|
||||
// 获取参数
|
||||
String loginId = req.getParamNotNull(paramName.loginId);
|
||||
|
||||
// 校验参数签名
|
||||
if(ssoConfig.getIsCheckSign()) {
|
||||
ssoClientTemplate.getSignTemplate(ssoConfig.getClient()).checkRequest(req, paramName.loginId);
|
||||
} else {
|
||||
SaSsoManager.printNoCheckSignWarningByRuntime();
|
||||
}
|
||||
|
||||
// 注销当前应用端会话
|
||||
stpLogic.logout(loginId);
|
||||
|
||||
// 响应
|
||||
return SaResult.ok("单点注销回调成功");
|
||||
}
|
||||
|
||||
// 工具方法
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId
|
||||
* @param ticket ticket码
|
||||
* @param currUri 当前路由的uri,用于计算单点注销回调地址
|
||||
* @return loginId
|
||||
*/
|
||||
public Object checkTicketByMode2Or3(String ticket, String currUri) {
|
||||
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
|
||||
ApiName apiName = ssoClientTemplate.apiName;
|
||||
|
||||
// --------- 两种模式
|
||||
if(cfg.getIsHttp()) {
|
||||
// q1、使用模式三:使用 http 请求从认证中心校验ticket
|
||||
|
||||
// 计算当前 sso-client 的单点注销回调地址
|
||||
String ssoLogoutCall = null;
|
||||
if(cfg.getIsSlo()) {
|
||||
// 如果配置了回调地址,就使用配置的值:
|
||||
if(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) {
|
||||
ssoLogoutCall = cfg.getCurrSsoLogoutCall();
|
||||
}
|
||||
// 如果提供了当前 uri,则根据此值来计算:
|
||||
else if(SaFoxUtil.isNotEmpty(currUri)) {
|
||||
ssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall);
|
||||
}
|
||||
// 否则视为不注册单点注销回调地址
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
// 构建请求URL
|
||||
String checkUrl = ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCall);
|
||||
|
||||
// 发起请求
|
||||
SaResult result = ssoClientTemplate.request(checkUrl);
|
||||
|
||||
// 校验
|
||||
if(result.getCode() != null && result.getCode() == SaResult.CODE_SUCCESS) {
|
||||
return result.getData();
|
||||
} else {
|
||||
// 将 sso-server 回应的消息作为异常抛出
|
||||
throw new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005);
|
||||
}
|
||||
} else {
|
||||
// q2、使用模式二:直连Redis校验ticket
|
||||
// return ssoClientTemplate.checkTicket(ticket);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, cfg.getClient());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public Object ssoLogoutBack(SaRequest req, SaResponse res) {
|
||||
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoClientTemplate.paramName);
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* SSO 请求处理器,辅助方法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoProcessorHelper {
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Object ssoLogoutBack(SaRequest req, SaResponse res, ParamName paramName) {
|
||||
|
||||
/*
|
||||
* 三种情况:
|
||||
* 1. 有back参数,值为SELF -> 回退一级并刷新
|
||||
* 2. 有back参数,值为url -> 跳转到此url地址
|
||||
* 3. 无back参数 -> 返回json数据
|
||||
*/
|
||||
String back = req.getParam(paramName.back);
|
||||
if(SaFoxUtil.isNotEmpty(back)) {
|
||||
if(back.equals(SaSsoConsts.SELF)) {
|
||||
return "<script>if(document.referrer != location.href){ location.replace(document.referrer || '/'); }</script>";
|
||||
}
|
||||
return res.redirect(back);
|
||||
} else {
|
||||
return SaResult.ok("单点注销成功");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.processor;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* SSO 请求处理器 (Server端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoServerProcessor {
|
||||
|
||||
/**
|
||||
* 全局默认实例
|
||||
*/
|
||||
public static SaSsoServerProcessor instance = new SaSsoServerProcessor();
|
||||
|
||||
/**
|
||||
* 底层 SaSsoServerTemplate 对象
|
||||
*/
|
||||
public SaSsoServerTemplate ssoServerTemplate = new SaSsoServerTemplate();
|
||||
|
||||
// ----------- SSO-Server 端路由分发 -----------
|
||||
|
||||
/**
|
||||
* 分发 Server 端所有请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object dister() {
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
|
||||
ApiName apiName = ssoServerTemplate.apiName;
|
||||
|
||||
// ------------------ 路由分发 ------------------
|
||||
|
||||
// ---------- SSO-Server端:授权地址
|
||||
if(req.isPath(apiName.ssoAuth)) {
|
||||
return ssoAuth();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:RestAPI 登录接口
|
||||
if(req.isPath(apiName.ssoDoLogin)) {
|
||||
return ssoDoLogin();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:校验ticket 获取账号id
|
||||
if(req.isPath(apiName.ssoCheckTicket) && cfg.getIsHttp()) {
|
||||
return ssoCheckTicket();
|
||||
}
|
||||
|
||||
// ---------- SSO-Server端:单点注销
|
||||
if(req.isPath(apiName.ssoSignout)) {
|
||||
return ssoSignout();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:授权地址
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoAuth() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogic();
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// ---------- 此处有两种情况分开处理:
|
||||
// ---- 情况1:在SSO认证中心尚未登录,需要先去登录
|
||||
if( ! stpLogic.isLogin()) {
|
||||
return cfg.notLoginView.get();
|
||||
}
|
||||
// ---- 情况2:在SSO认证中心已经登录,需要重定向回 Client 端,而这又分为两种方式:
|
||||
String mode = req.getParam(paramName.mode, "");
|
||||
|
||||
// 方式1:直接重定向回Client端 (mode=simple)
|
||||
if(mode.equals(SaSsoConsts.MODE_SIMPLE)) {
|
||||
String redirect = req.getParam(paramName.redirect);
|
||||
ssoServerTemplate.checkRedirectUrl(redirect);
|
||||
return res.redirect(redirect);
|
||||
} else {
|
||||
// 方式2:带着ticket参数重定向回Client端 (mode=ticket)
|
||||
String redirectUrl = ssoServerTemplate.buildRedirectUrl(
|
||||
stpLogic.getLoginId(), req.getParam(paramName.client), req.getParam(paramName.redirect));
|
||||
return res.redirect(redirectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:RestAPI 登录接口
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoDoLogin() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// 处理
|
||||
return cfg.doLoginHandle.apply(req.getParam(paramName.name), req.getParam(paramName.pwd));
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:校验ticket 获取账号id [模式三]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoCheckTicket() {
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// 1、获取参数
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String client = req.getParam(paramName.client);
|
||||
String ticket = req.getParamNotNull(paramName.ticket);
|
||||
String sloCallback = req.getParam(paramName.ssoLogoutCall);
|
||||
|
||||
// 2、校验签名
|
||||
if(ssoServerTemplate.getServerConfig().getIsCheckSign()) {
|
||||
ssoServerTemplate.getSignTemplate(client).checkRequest(req,
|
||||
paramName.client, paramName.ticket, paramName.ssoLogoutCall);
|
||||
} else {
|
||||
SaSsoManager.printNoCheckSignWarningByRuntime();
|
||||
}
|
||||
|
||||
// 3、校验ticket,获取 loginId
|
||||
Object loginId = ssoServerTemplate.checkTicket(ticket, client);
|
||||
if(SaFoxUtil.isEmpty(loginId)) {
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
}
|
||||
|
||||
// 4、注册此客户端的单点注销回调URL
|
||||
ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallback);
|
||||
|
||||
// 5、给 client 端响应结果
|
||||
return SaResult.data(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignout() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// SSO-Server端:单点注销 [用户访问式] (不带loginId参数)
|
||||
if(cfg.getIsSlo() && ! req.hasParam(paramName.loginId)) {
|
||||
return ssoSignoutByUserVisit();
|
||||
}
|
||||
|
||||
// SSO-Server端:单点注销 [Client调用式] (带loginId参数 & isHttp=true)
|
||||
if(cfg.getIsHttp() && cfg.getIsSlo() && req.hasParam(paramName.loginId)) {
|
||||
return ssoSignoutByClientHttp();
|
||||
}
|
||||
|
||||
// 默认返回
|
||||
return SaSsoConsts.NOT_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [用户访问式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignoutByUserVisit() {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
Object loginId = ssoServerTemplate.getStpLogic().getLoginIdDefaultNull();
|
||||
|
||||
// 单点注销
|
||||
if(SaFoxUtil.isNotEmpty(loginId)) {
|
||||
ssoServerTemplate.ssoLogout(loginId);
|
||||
}
|
||||
|
||||
// 完成
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSO-Server端:单点注销 [Client调用式]
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object ssoSignoutByClientHttp() {
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// 获取参数
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String loginId = req.getParam(paramName.loginId);
|
||||
String client = req.getParam(paramName.client);
|
||||
|
||||
// step.1 校验签名
|
||||
if(ssoServerTemplate.getServerConfig().getIsCheckSign()) {
|
||||
ssoServerTemplate.getSignTemplate(client).checkRequest(req, paramName.loginId);
|
||||
} else {
|
||||
SaSsoManager.printNoCheckSignWarningByRuntime();
|
||||
}
|
||||
|
||||
// step.2 单点注销
|
||||
ssoServerTemplate.ssoLogout(loginId);
|
||||
|
||||
// 响应
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public Object ssoLogoutBack(SaRequest req, SaResponse res) {
|
||||
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName);
|
||||
}
|
||||
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.SaManager;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 模板方法类 (Client端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoClientTemplate extends SaSsoTemplate {
|
||||
|
||||
/**
|
||||
* 获取底层使用的SsoClient配置对象
|
||||
* @return /
|
||||
*/
|
||||
public SaSsoClientConfig getClientConfig() {
|
||||
return SaSsoManager.getClientConfig();
|
||||
}
|
||||
|
||||
// ------------------- SSO 模式三相关 -------------------
|
||||
|
||||
/**
|
||||
* 根据配置的 getData 地址,查询数据
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getData(Map<String, Object> paramMap) {
|
||||
String getDataUrl = getClientConfig().splicingGetDataUrl();
|
||||
return getData(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义 path 地址,查询数据 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param path 自定义 path
|
||||
* @param paramMap 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
public Object getData(String path, Map<String, Object> paramMap) {
|
||||
String url = buildCustomPathUrl(path, paramMap);
|
||||
return getClientConfig().sendHttp.apply(url);
|
||||
}
|
||||
|
||||
// ---------------------- 构建URL ----------------------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 单点登录地址
|
||||
* @param clientLoginUrl Client端登录地址
|
||||
* @param back 回调路径
|
||||
* @return [SSO-Server端-认证地址 ]
|
||||
*/
|
||||
public String buildServerAuthUrl(String clientLoginUrl, String back) {
|
||||
SaSsoClientConfig ssoConfig = getClientConfig();
|
||||
|
||||
// 服务端认证地址
|
||||
String serverUrl = ssoConfig.splicingAuthUrl();
|
||||
|
||||
// 拼接客户端标识
|
||||
String client = ssoConfig.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 + "=" + back) ) {
|
||||
clientLoginUrl = SaFoxUtil.joinParam(clientLoginUrl, paramName.back, back);
|
||||
}
|
||||
|
||||
// 返回
|
||||
return SaFoxUtil.joinParam(serverUrl, paramName.redirect, clientLoginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:校验ticket的URL
|
||||
* <p> 在模式三下,Client端拿到Ticket后根据此地址向Server端发送请求,获取账号id
|
||||
* @param ticket ticket码
|
||||
* @param ssoLogoutCallUrl 单点注销时的回调URL
|
||||
* @return 构建完毕的URL
|
||||
*/
|
||||
public String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
|
||||
|
||||
SaSsoClientConfig ssoConfig = getClientConfig();
|
||||
|
||||
// 1、url
|
||||
String url = ssoConfig.splicingCheckTicketUrl();
|
||||
|
||||
// 2、参数:client、ticket、ssoLogoutCall
|
||||
Map<String, Object> paramMap = new TreeMap<>();
|
||||
paramMap.put(paramName.ticket, ticket);
|
||||
paramMap.put(paramName.client, ssoConfig.getClient());
|
||||
paramMap.put(paramName.ssoLogoutCall, ssoLogoutCallUrl);
|
||||
|
||||
// 追加签名参数,并序列化为kv字符串
|
||||
String signParamStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(paramMap);
|
||||
|
||||
// 3、拼接
|
||||
return SaFoxUtil.joinParam(url, signParamStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:单点注销URL
|
||||
* @param loginId 要注销的账号id
|
||||
* @return 单点注销URL
|
||||
*/
|
||||
public String buildSloUrl(Object loginId) {
|
||||
// 获取所需对象
|
||||
SaSsoClientConfig ssoConfig = getClientConfig();
|
||||
String url = ssoConfig.splicingSloUrl();
|
||||
String currClient = ssoConfig.getClient();
|
||||
|
||||
// 组织请求参数
|
||||
Map<String, Object> paramMap = new TreeMap<>();
|
||||
paramMap.put(paramName.loginId, loginId);
|
||||
paramMap.put(paramName.client, currClient);
|
||||
|
||||
// 追加签名参数,并序列化为kv字符串
|
||||
String signParamsStr = getSignTemplate(currClient).addSignParamsAndJoin(paramMap);
|
||||
|
||||
// 拼接到 url 上
|
||||
return SaFoxUtil.joinParam(url, signParamsStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 getData 地址,带签名等参数
|
||||
* @param paramMap 查询参数
|
||||
* @return /
|
||||
*/
|
||||
public String buildGetDataUrl(Map<String, Object> paramMap) {
|
||||
String getDataUrl = getClientConfig().getGetDataUrl();
|
||||
return buildCustomPathUrl(getDataUrl, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server 端自定义 path 地址,带签名等参数 (此方法需要配置 sa-token.sso.server-url 地址)
|
||||
* @param paramMap 请求参数
|
||||
* @return /
|
||||
*/
|
||||
public String buildCustomPathUrl(String path, Map<String, Object> paramMap) {
|
||||
SaSsoClientConfig ssoConfig = getClientConfig();
|
||||
|
||||
// 构建 url
|
||||
// 如果 path 不是以 http 开头,那么就拼接上 serverUrl
|
||||
String url = path;
|
||||
if( ! url.startsWith("http") ) {
|
||||
String serverUrl = ssoConfig.getServerUrl();
|
||||
SaSsoException.notEmpty(serverUrl, "请先配置 sa-token.sso.server-url 地址", SaSsoErrorCode.CODE_30012);
|
||||
url = SaFoxUtil.spliceTwoUrl(serverUrl, path);
|
||||
}
|
||||
|
||||
// 构建参数字符串
|
||||
paramMap.put(paramName.client, ssoConfig.getClient());
|
||||
String signParamsStr = getSignTemplate(ssoConfig.getClient()).addSignParamsAndJoin(paramMap);
|
||||
|
||||
// 拼接
|
||||
return SaFoxUtil.joinParam(url, signParamsStr);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 发起请求 -------------------
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
* @param url 请求地址
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public SaResult request(String url) {
|
||||
String body = getClientConfig().sendHttp.apply(url);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(body);
|
||||
return new SaResult(map);
|
||||
}
|
||||
|
||||
}
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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.SaManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.sso.util.SaSsoConsts;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.model.SaSsoClientModel;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 模板方法类 (Server端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
|
||||
/**
|
||||
* 获取底层使用的SsoServer配置对象
|
||||
* @return /
|
||||
*/
|
||||
public SaSsoServerConfig getServerConfig() {
|
||||
return SaSsoManager.getServerConfig();
|
||||
}
|
||||
|
||||
// ---------------------- Ticket 操作 ----------------------
|
||||
|
||||
/**
|
||||
* 保存 Ticket 关联的 loginId
|
||||
* @param ticket ticket码
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void saveTicket(String ticket, Object loginId) {
|
||||
// 保存 ticket -> loginId 的关系
|
||||
long ticketTimeout = getServerConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketSaveKey(ticket), String.valueOf(loginId), ticketTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 Ticket 索引 (id 反查 ticket)
|
||||
* @param ticket ticket码
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public void saveTicketIndex(String ticket, Object loginId) {
|
||||
long ticketTimeout = getServerConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketIndexKey(loginId), String.valueOf(ticket), ticketTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 Ticket 关联的 client
|
||||
* @param ticket ticket码
|
||||
* @param client 客户端标识
|
||||
*/
|
||||
public void saveTicketToClient(String ticket, String client) {
|
||||
if(SaFoxUtil.isEmpty(client)) {
|
||||
return;
|
||||
}
|
||||
long ticketTimeout = getServerConfig().getTicketTimeout();
|
||||
SaManager.getSaTokenDao().set(splicingTicketToClientSaveKey(ticket), client, ticketTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 Ticket
|
||||
* @param ticket Ticket码
|
||||
*/
|
||||
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 关联的 client
|
||||
* @param ticket Ticket码
|
||||
*/
|
||||
public void deleteTicketToClient(String ticket) {
|
||||
if(ticket == null) {
|
||||
return;
|
||||
}
|
||||
SaManager.getSaTokenDao().delete(splicingTicketToClientSaveKey(ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 ticket 指向的 loginId,如果 ticket 码无效则返回 null
|
||||
* @param ticket Ticket码
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object getLoginId(String ticket) {
|
||||
if(SaFoxUtil.isEmpty(ticket)) {
|
||||
return null;
|
||||
}
|
||||
return SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 ticket 指向的 loginId,并转换为指定类型
|
||||
* @param <T> 要转换的类型
|
||||
* @param ticket Ticket码
|
||||
* @param cs 要转换的类型
|
||||
* @return 账号id
|
||||
*/
|
||||
public <T> T getLoginId(String ticket, Class<T> cs) {
|
||||
return SaFoxUtil.getValueByType(getLoginId(ticket), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询 指定 loginId 其所属的 ticket 值
|
||||
* @param loginId 账号id
|
||||
* @return Ticket值
|
||||
*/
|
||||
public String getTicketValue(Object loginId) {
|
||||
if(loginId == null) {
|
||||
return null;
|
||||
}
|
||||
return SaManager.getSaTokenDao().get(splicingTicketIndexKey(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));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* 根据 账号id 创建一个 Ticket码
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @return Ticket码
|
||||
*/
|
||||
public String createTicket(Object loginId, String client) {
|
||||
// 创建 Ticket
|
||||
String ticket = randomTicket(loginId);
|
||||
|
||||
// 保存 Ticket
|
||||
saveTicket(ticket, loginId);
|
||||
saveTicketIndex(ticket, loginId);
|
||||
saveTicketToClient(ticket, client);
|
||||
|
||||
// 返回 Ticket
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除
|
||||
* @param ticket Ticket码
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object checkTicket(String ticket) {
|
||||
return checkTicket(ticket, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 Ticket 码,获取账号id,如果此ticket是有效的,则立即删除
|
||||
* @param ticket Ticket码
|
||||
* @param client client 标识
|
||||
* @return 账号id
|
||||
*/
|
||||
public Object checkTicket(String ticket, String client) {
|
||||
// 读取 loginId
|
||||
String loginId = SaManager.getSaTokenDao().get(splicingTicketSaveKey(ticket));
|
||||
|
||||
if(loginId != null) {
|
||||
|
||||
// 解析出这个 ticket 关联的 Client
|
||||
String ticketClient = getTicketToClient(ticket);
|
||||
|
||||
// 如果指定了 client 标识,则校验一下 client 标识是否一致
|
||||
if(SaFoxUtil.isNotEmpty(client) && SaFoxUtil.notEquals(client, ticketClient)) {
|
||||
throw new SaSsoException("该 ticket 不属于 client=" + client + ", ticket 值: " + ticket)
|
||||
.setCode(SaSsoErrorCode.CODE_30011);
|
||||
}
|
||||
|
||||
// 删除 ticket 信息,使其只有一次性有效
|
||||
deleteTicket(ticket);
|
||||
deleteTicketIndex(loginId);
|
||||
deleteTicketToClient(ticket);
|
||||
}
|
||||
|
||||
//
|
||||
return loginId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机一个 Ticket码
|
||||
* @param loginId 账号id
|
||||
* @return Ticket码
|
||||
*/
|
||||
public String randomTicket(Object loginId) {
|
||||
return SaFoxUtil.getRandomString(64);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return see note
|
||||
*/
|
||||
public String getAllowUrl() {
|
||||
// 默认从配置文件中返回
|
||||
return getServerConfig().getAllowUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验重定向url合法性
|
||||
* @param url 下放ticket的url地址
|
||||
*/
|
||||
public void checkRedirectUrl(String url) {
|
||||
|
||||
// 1、是否是一个有效的url
|
||||
if( ! SaFoxUtil.isUrl(url) ) {
|
||||
throw new SaSsoException("无效redirect:" + url).setCode(SaSsoErrorCode.CODE_30001);
|
||||
}
|
||||
|
||||
// 2、截取掉?后面的部分
|
||||
int qIndex = url.indexOf("?");
|
||||
if(qIndex != -1) {
|
||||
url = url.substring(0, qIndex);
|
||||
}
|
||||
|
||||
// 3、是否在[允许地址列表]之中
|
||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||
if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) {
|
||||
throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002);
|
||||
}
|
||||
|
||||
// 校验通过 √
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------- SSO 模式三相关 -------------------
|
||||
|
||||
/**
|
||||
* 为指定账号id注册单点注销回调URL
|
||||
* @param loginId 账号id
|
||||
* @param client 指定客户端标识,可为null
|
||||
* @param sloCallbackUrl 单点注销时的回调URL
|
||||
*/
|
||||
public void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) {
|
||||
// 如果提供的参数是空值,则直接返回,不进行任何操作
|
||||
if(SaFoxUtil.isEmpty(loginId) || SaFoxUtil.isEmpty(sloCallbackUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId);
|
||||
|
||||
// 取
|
||||
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
|
||||
|
||||
// 加
|
||||
scmList.add(new SaSsoClientModel(client, sloCallbackUrl));
|
||||
|
||||
// 存
|
||||
session.set(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, scmList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定账号单点注销
|
||||
* @param loginId 指定账号
|
||||
*/
|
||||
public void ssoLogout(Object loginId) {
|
||||
|
||||
// 如果这个账号尚未登录,则无操作
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaSsoServerConfig ssoConfig = getServerConfig();
|
||||
|
||||
// step.1 遍历通知 Client 端注销会话
|
||||
List<SaSsoClientModel> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
|
||||
scmList.forEach(scm -> {
|
||||
// url
|
||||
String sloCallUrl = scm.getSsoLogoutCall();
|
||||
|
||||
// 参数
|
||||
Map<String, Object> paramsMap = new TreeMap<>();
|
||||
paramsMap.put(paramName.client, scm.getClient());
|
||||
paramsMap.put(paramName.loginId, loginId);
|
||||
String signParamsStr = getSignTemplate(scm.getClient()).addSignParamsAndJoin(paramsMap);
|
||||
|
||||
// 拼接
|
||||
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
|
||||
|
||||
// 发起请求
|
||||
ssoConfig.sendHttp.apply(finalUrl);
|
||||
});
|
||||
|
||||
// step.2 Server端注销
|
||||
getStpLogic().logout(loginId);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- 构建URL ----------------------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端向Client下放ticket的地址
|
||||
* @param loginId 账号id
|
||||
* @param client 客户端标识
|
||||
* @param redirect Client端提供的重定向地址
|
||||
* @return see note
|
||||
*/
|
||||
public String buildRedirectUrl(Object loginId, String client, String redirect) {
|
||||
|
||||
// 校验 重定向地址 是否合法
|
||||
checkRedirectUrl(redirect);
|
||||
|
||||
// 删掉 旧Ticket
|
||||
deleteTicket(getTicketValue(loginId));
|
||||
|
||||
// 创建 新Ticket
|
||||
String ticket = createTicket(loginId, client);
|
||||
|
||||
// 构建 授权重定向地址 (Server端 根据此地址向 Client端 下放Ticket)
|
||||
return SaFoxUtil.joinParam(encodeBackParam(redirect), paramName.ticket, ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
|
||||
* @param url url
|
||||
* @return 编码过后的url
|
||||
*/
|
||||
public String encodeBackParam(String url) {
|
||||
|
||||
// 获取back参数所在位置
|
||||
int index = url.indexOf("?" + paramName.back + "=");
|
||||
if(index == -1) {
|
||||
index = url.indexOf("&" + paramName.back + "=");
|
||||
if(index == -1) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始编码
|
||||
int length = paramName.back.length() + 2;
|
||||
String back = url.substring(index + length);
|
||||
back = SaFoxUtil.encodeUrl(back);
|
||||
|
||||
// 放回url中
|
||||
url = url.substring(0, index + length) + back;
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 返回相应key -------------------
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 账号Id
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketSaveKey(String ticket) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:Ticket 查 所属的 client
|
||||
* @param ticket ticket值
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketToClientSaveKey(String ticket) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-client:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接key:账号Id 反查 Ticket
|
||||
* @param id 账号id
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketIndexKey(Object id) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":id-ticket:" + id;
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.SaManager;
|
||||
import cn.dev33.satoken.sign.SaSignTemplate;
|
||||
import cn.dev33.satoken.sso.name.ApiName;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token SSO 模板方法类 (公共端)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.30.0
|
||||
*/
|
||||
public class SaSsoTemplate {
|
||||
|
||||
// ---------------------- 全局配置 ----------------------
|
||||
|
||||
/**
|
||||
* 所有 API 名称
|
||||
*/
|
||||
public ApiName apiName = new ApiName();
|
||||
|
||||
/**
|
||||
* 所有参数名称
|
||||
*/
|
||||
public ParamName paramName = new ParamName();
|
||||
|
||||
/**
|
||||
* @param paramName 替换 paramName 对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoTemplate setParamName(ParamName paramName) {
|
||||
this.paramName = paramName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName 替换 apiName 对象
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoTemplate setApiName(ApiName apiName) {
|
||||
this.apiName = apiName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的会话对象
|
||||
* @return /
|
||||
*/
|
||||
public StpLogic getStpLogic() {
|
||||
return StpUtil.stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的 API 签名对象
|
||||
* @param client 指定客户端标识,填 null 代表获取默认的
|
||||
* @return /
|
||||
*/
|
||||
public SaSignTemplate getSignTemplate(String client) {
|
||||
// 框架默认只返回全局 SaSignTemplate,client 参数留作开发者扩展
|
||||
return SaManager.getSaSignTemplate();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+24
-82
@@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.sso;
|
||||
package cn.dev33.satoken.sso.template;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -27,12 +28,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class SaSsoUtil {
|
||||
|
||||
/**
|
||||
* 底层 SaSsoTemplate 对象
|
||||
*/
|
||||
public static SaSsoTemplate ssoTemplate = new SaSsoTemplate();
|
||||
|
||||
|
||||
// ---------------------- Ticket 操作 ----------------------
|
||||
|
||||
/**
|
||||
@@ -42,7 +37,7 @@ public class SaSsoUtil {
|
||||
* @return Ticket码
|
||||
*/
|
||||
public static String createTicket(Object loginId, String client) {
|
||||
return ssoTemplate.createTicket(loginId, client);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.createTicket(loginId, client);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +45,7 @@ public class SaSsoUtil {
|
||||
* @param ticket Ticket码
|
||||
*/
|
||||
public static void deleteTicket(String ticket) {
|
||||
ssoTemplate.deleteTicket(ticket);
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicket(ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +53,7 @@ public class SaSsoUtil {
|
||||
* @param loginId 账号id
|
||||
*/
|
||||
public static void deleteTicketIndex(Object loginId) {
|
||||
ssoTemplate.deleteTicketIndex(loginId);
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate.deleteTicketIndex(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +62,7 @@ public class SaSsoUtil {
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object getLoginId(String ticket) {
|
||||
return ssoTemplate.getLoginId(ticket);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +73,7 @@ public class SaSsoUtil {
|
||||
* @return 账号id
|
||||
*/
|
||||
public static <T> T getLoginId(String ticket, Class<T> cs) {
|
||||
return ssoTemplate.getLoginId(ticket, cs);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.getLoginId(ticket, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +82,7 @@ public class SaSsoUtil {
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object checkTicket(String ticket) {
|
||||
return ssoTemplate.checkTicket(ticket);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +92,7 @@ public class SaSsoUtil {
|
||||
* @return 账号id
|
||||
*/
|
||||
public static Object checkTicket(String ticket, String client) {
|
||||
return ssoTemplate.checkTicket(ticket, client);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.checkTicket(ticket, client);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +100,7 @@ public class SaSsoUtil {
|
||||
* @return see note
|
||||
*/
|
||||
public static String getAllowUrl() {
|
||||
return ssoTemplate.getAllowUrl();
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.getAllowUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +108,7 @@ public class SaSsoUtil {
|
||||
* @param url 下放ticket的url地址
|
||||
*/
|
||||
public static void checkRedirectUrl(String url) {
|
||||
ssoTemplate.checkRedirectUrl(url);
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate.checkRedirectUrl(url);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,16 +121,17 @@ public class SaSsoUtil {
|
||||
* @return 构建完毕的URL
|
||||
*/
|
||||
public static String buildCheckTicketUrl(String ticket, String ssoLogoutCallUrl) {
|
||||
return ssoTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.buildCheckTicketUrl(ticket, ssoLogoutCallUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为指定账号id注册单点注销回调URL
|
||||
* @param loginId 账号id
|
||||
* @param client 指定客户端标识,可为null
|
||||
* @param sloCallbackUrl 单点注销时的回调URL
|
||||
*/
|
||||
public static void registerSloCallbackUrl(Object loginId, String sloCallbackUrl) {
|
||||
ssoTemplate.registerSloCallbackUrl(loginId, sloCallbackUrl);
|
||||
public static void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) {
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate.registerSloCallbackUrl(loginId, client, sloCallbackUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +140,7 @@ public class SaSsoUtil {
|
||||
* @return 单点注销URL
|
||||
*/
|
||||
public static String buildSloUrl(Object loginId) {
|
||||
return ssoTemplate.buildSloUrl(loginId);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.buildSloUrl(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,7 +148,7 @@ public class SaSsoUtil {
|
||||
* @param loginId 指定账号
|
||||
*/
|
||||
public static void ssoLogout(Object loginId) {
|
||||
ssoTemplate.ssoLogout(loginId);
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate.ssoLogout(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +157,7 @@ public class SaSsoUtil {
|
||||
* @return 查询结果
|
||||
*/
|
||||
public static Object getData(Map<String, Object> paramMap) {
|
||||
return ssoTemplate.getData(paramMap);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.getData(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +167,7 @@ public class SaSsoUtil {
|
||||
* @return 查询结果
|
||||
*/
|
||||
public static Object getData(String path, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.getData(path, paramMap);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.getData(path, paramMap);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +180,7 @@ public class SaSsoUtil {
|
||||
* @return [SSO-Server端-认证地址 ]
|
||||
*/
|
||||
public static String buildServerAuthUrl(String clientLoginUrl, String back) {
|
||||
return ssoTemplate.buildServerAuthUrl(clientLoginUrl, back);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.buildServerAuthUrl(clientLoginUrl, back);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +191,7 @@ public class SaSsoUtil {
|
||||
* @return see note
|
||||
*/
|
||||
public static String buildRedirectUrl(Object loginId, String client, String redirect) {
|
||||
return ssoTemplate.buildRedirectUrl(loginId, client, redirect);
|
||||
return SaSsoServerProcessor.instance.ssoServerTemplate.buildRedirectUrl(loginId, client, redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +200,7 @@ public class SaSsoUtil {
|
||||
* @return /
|
||||
*/
|
||||
public static String buildGetDataUrl(Map<String, Object> paramMap) {
|
||||
return ssoTemplate.buildGetDataUrl(paramMap);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.buildGetDataUrl(paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,62 +209,8 @@ public class SaSsoUtil {
|
||||
* @return /
|
||||
*/
|
||||
public static String buildCustomPathUrl(String path, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.buildCustomPathUrl(path, paramMap);
|
||||
return SaSsoClientProcessor.instance.ssoClientTemplate.buildCustomPathUrl(path, paramMap);
|
||||
}
|
||||
|
||||
|
||||
// ------------------- 发起请求 -------------------
|
||||
|
||||
/**
|
||||
* 发出请求,并返回 SaResult 结果
|
||||
* @param url 请求地址
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public static SaResult request(String url) {
|
||||
return ssoTemplate.request(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 paramMap 追加 sign 等参数,并序列化为kv字符串,拼接到url后面
|
||||
* @param url 请求地址
|
||||
* @param paramMap 请求原始参数列表
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public static String joinParamMapAndSign(String url, Map<String, Object> paramMap) {
|
||||
return ssoTemplate.joinLoginIdAndSign(url, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 url 拼接 loginId 参数,并拼接 sign 等参数
|
||||
* @param url 链接
|
||||
* @param loginId 账号id
|
||||
* @return 加工后的url
|
||||
*/
|
||||
public static String joinLoginIdAndSign(String url, Object loginId) {
|
||||
return ssoTemplate.joinLoginIdAndSign(url, loginId);
|
||||
}
|
||||
|
||||
|
||||
// -------- 以下方法已废弃,仅为兼容旧版本而保留 --------
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
* @return Server端 账号资料查询地址
|
||||
*/
|
||||
@Deprecated
|
||||
public static String buildUserinfoUrl(Object loginId) {
|
||||
return ssoTemplate.buildUserinfoUrl(loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:账号资料
|
||||
* @param loginId 账号id
|
||||
* @return 账号资料
|
||||
*/
|
||||
@Deprecated
|
||||
public static Object getUserinfo(Object loginId) {
|
||||
return ssoTemplate.getUserinfo(loginId);
|
||||
}
|
||||
|
||||
}
|
||||
+5
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.sso;
|
||||
package cn.dev33.satoken.sso.util;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO模块相关常量
|
||||
@@ -24,8 +24,12 @@ package cn.dev33.satoken.sso;
|
||||
public class SaSsoConsts {
|
||||
|
||||
/** Client端单点注销回调URL的Set集合,存储在Session中使用的key */
|
||||
@Deprecated
|
||||
public static final String SLO_CALLBACK_SET_KEY = "SLO_CALLBACK_SET_KEY_";
|
||||
|
||||
/** Client 端 Model 信息的 List 集合,存储在 SaSession 中使用的key */
|
||||
public static final String SSO_CLIENT_MODEL_LIST_KEY_ = "SSO_CLIENT_MODEL_LIST_KEY_";
|
||||
|
||||
/** 表示OK的返回结果 */
|
||||
public static final String OK = "ok";
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ package cn.dev33.satoken.solon.sso;
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoTemplate;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.template.SaSsoTemplate;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Condition;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
|
||||
+37
-17
@@ -15,17 +15,18 @@
|
||||
*/
|
||||
package cn.dev33.satoken.spring.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoClientTemplate;
|
||||
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.SaSsoProcessor;
|
||||
import cn.dev33.satoken.sso.SaSsoTemplate;
|
||||
import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
|
||||
/**
|
||||
* 注入 Sa-Token-SSO 所需要的 Bean
|
||||
* 注入 Sa-Token SSO 所需要的 Bean
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.34.0
|
||||
@@ -34,24 +35,43 @@ import cn.dev33.satoken.sso.SaSsoUtil;
|
||||
public class SaSsoBeanInject {
|
||||
|
||||
/**
|
||||
* 注入 Sa-Token-SSO 配置类
|
||||
* 注入 Sa-Token SSO Server 端 配置类
|
||||
*
|
||||
* @param saSsoConfig 配置对象
|
||||
* @param serverConfig 配置对象
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaOAuth2Config(SaSsoConfig saSsoConfig) {
|
||||
SaSsoManager.setConfig(saSsoConfig);
|
||||
public void setSaSsoServerConfig(SaSsoServerConfig serverConfig) {
|
||||
SaSsoManager.setServerConfig(serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 SSO 模板代码类
|
||||
*
|
||||
* @param ssoTemplate SaSsoTemplate 对象
|
||||
* 注入 Sa-Token SSO Client 端 配置类
|
||||
*
|
||||
* @param clientConfig 配置对象
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaSsoTemplate(SaSsoTemplate ssoTemplate) {
|
||||
SaSsoUtil.ssoTemplate = ssoTemplate;
|
||||
SaSsoProcessor.instance.ssoTemplate = ssoTemplate;
|
||||
public void setSaSsoClientConfig(SaSsoClientConfig clientConfig) {
|
||||
SaSsoManager.setClientConfig(clientConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 SSO 模板代码类 (Server 端)
|
||||
*
|
||||
* @param ssoServerTemplate /
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaSsoServerTemplate(SaSsoServerTemplate ssoServerTemplate) {
|
||||
SaSsoServerProcessor.instance.ssoServerTemplate = ssoServerTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 SSO 模板代码类 (Client 端)
|
||||
*
|
||||
* @param ssoClientTemplate /
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setSaSsoClientTemplate(SaSsoClientTemplate ssoClientTemplate) {
|
||||
SaSsoClientProcessor.instance.ssoClientTemplate = ssoClientTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
-10
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
package cn.dev33.satoken.spring.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.config.SaSsoConfig;
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token-SSO 所需要的 Bean
|
||||
* 注册 Sa-Token SSO 所需要的 Bean
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.34.0
|
||||
@@ -32,13 +32,23 @@ import cn.dev33.satoken.sso.SaSsoManager;
|
||||
public class SaSsoBeanRegister {
|
||||
|
||||
/**
|
||||
* 获取 SSO 配置对象
|
||||
* @return 配置对象
|
||||
* 获取 SSO Server 端 配置对象
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sa-token.sso")
|
||||
public SaSsoConfig getSaSsoConfig() {
|
||||
return new SaSsoConfig();
|
||||
@ConfigurationProperties(prefix = "sa-token.sso-server")
|
||||
public SaSsoServerConfig getSaSsoServerConfig() {
|
||||
return new SaSsoServerConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 SSO Client 端 配置对象
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sa-token.sso-client")
|
||||
public SaSsoClientConfig getSaSsoClientConfig() {
|
||||
return new SaSsoClientConfig();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ package cn.dev33.satoken.integrate.configure.inject;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoTemplate;
|
||||
import cn.dev33.satoken.sso.template.SaSsoTemplate;
|
||||
|
||||
/**
|
||||
* 自定义 Sa-SSO 模板方法
|
||||
|
||||
Reference in New Issue
Block a user