From bea2592dc9cddbe659cfb8e6d60bea66a71f211a Mon Sep 17 00:00:00 2001
From: click33 <2393584716@qq.com>
Date: Fri, 23 Jul 2021 02:13:44 +0800
Subject: [PATCH] =?UTF-8?q?=E6=99=9A=E4=B8=8A=E5=8D=95=E7=82=B9=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../satoken/context/model/SaRequest.java | 2 +-
.../java/com/pj/satoken/MySaTokenAction.java | 18 ---
sa-token-doc/doc/_sidebar.md | 1 +
sa-token-doc/doc/sso/sso-cd.md | 127 ++++++++++++++++++
sa-token-doc/doc/sso/sso-type2.md | 58 ++++----
5 files changed, 158 insertions(+), 48 deletions(-)
delete mode 100644 sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java
create mode 100644 sa-token-doc/doc/sso/sso-cd.md
diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java
index 66ac2ec0..5d88dd4c 100644
--- a/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java
+++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java
@@ -92,7 +92,7 @@ public interface SaRequest {
}
/**
- * 返回当前请求的url,例:http://xxx.com/?id=127
+ * 返回当前请求的url,例:http://xxx.com/
* @return see note
*/
public String getUrl();
diff --git a/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java b/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java
deleted file mode 100644
index bd7d5790..00000000
--- a/sa-token-demo/sa-token-demo-springboot/src/main/java/com/pj/satoken/MySaTokenAction.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.pj.satoken;
-
-import org.springframework.stereotype.Component;
-
-import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
-import cn.dev33.satoken.util.SaFoxUtil;
-
-/**
- * 继承Sa-Token行为Bean默认实现, 重写部分逻辑
- */
-@Component
-public class MySaTokenAction extends SaTokenActionDefaultImpl {
- // 重写token生成策略
- @Override
- public String createToken(Object loginId, String loginType) {
- return SaFoxUtil.getRandomString(60); // 随机60位字符串
- }
-}
diff --git a/sa-token-doc/doc/_sidebar.md b/sa-token-doc/doc/_sidebar.md
index ce51a69b..edc77037 100644
--- a/sa-token-doc/doc/_sidebar.md
+++ b/sa-token-doc/doc/_sidebar.md
@@ -36,6 +36,7 @@
- [SSO模式一 共享Cookie同步会话](/sso/sso-type1)
- [SSO模式二 URL重定向传播会话](/sso/sso-type2)
- [SSO模式三 Http请求获取会话](/sso/sso-type3)
+ - [SSO整合-常见问题总结](/sso/sso-cd)
- **OAuth2.0**
- [OAuth2.0简述](/oauth2/readme)
diff --git a/sa-token-doc/doc/sso/sso-cd.md b/sa-token-doc/doc/sso/sso-cd.md
new file mode 100644
index 00000000..dc6cc763
--- /dev/null
+++ b/sa-token-doc/doc/sso/sso-cd.md
@@ -0,0 +1,127 @@
+# Sa-Token-SSO整合-常见问题总结
+
+---
+
+
+### 一、何时引导用户去登录?
+
+以下解决方案三选一:
+
+##### 1.1、前端按钮跳转
+前端页面准备一个**`[登录]`**按钮,当用户点击按钮时,跳转到登录接口
+``` js
+登录
+```
+
+##### 1.2、后端拦截重定向
+在后端注册全局过滤器(或拦截器),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口
+``` java
+/**
+ * Sa-Token 配置类
+ */
+@Configuration
+public class SaTokenConfigure implements WebMvcConfigurer {
+ /** 注册 [Sa-Token全局过滤器] */
+ @Bean
+ public SaServletFilter getSaServletFilter() {
+ return new SaServletFilter()
+ .addInclude("/**")
+ .addExclude("/sso/*", "/favicon.ico")
+ .setAuth(r -> {
+ if(StpUtil.isLogin() == false) {
+ String back = SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), SpringMVCUtil.getRequest().getQueryString());
+ SaHolder.getResponse().redirect("/sso/login?back=" + SaFoxUtil.encodeUrl(back));
+ SaRouter.back();
+ }
+ })
+ ;
+ }
+}
+```
+
+##### 1.3、后端拦截 + 前端跳转
+首先,后端仍需要提供拦截,但是不直接引导用户重定向,而是返回未登录的提示信息
+``` java
+/**
+ * Sa-Token 配置类
+ */
+@Configuration
+public class SaTokenConfigure implements WebMvcConfigurer {
+ /** 注册 [Sa-Token全局过滤器] */
+ @Bean
+ public SaServletFilter getSaServletFilter() {
+ return new SaServletFilter()
+ .addInclude("/**")
+ .addExclude("/sso/*", "/favicon.ico")
+ .setAuth(r -> {
+ if(StpUtil.isLogin() == false) {
+ // 与前端约定好,code=401时代表会话未登录
+ SaRouter.back(SaResult.ok().setCode(401));
+ }
+ })
+ ;
+ }
+}
+```
+
+前端接受到返回结果 `code=401` 时,开始跳转至登录接口
+``` js
+if(res.code == 401) {
+ location.href = '/sso/login?back=' + encodeURIComponent(location.href);
+}
+```
+
+这种方案比较适合以 Ajax 访问的 RestAPI 接口重定向
+
+
+
+
+### 二、定制化开发
+
+##### 2.1、如何自定义登录视图?
+- 方式一:在demo示例中直接更改页面代码
+- 方式二:在配置中配置登录视图地址
+
+``` java
+cfg.sso
+// 配置:未登录时返回的View
+.setNotLoginView(() -> {
+ return new ModelAndView("xxx.html");
+})
+```
+
+##### 2.2、如何自定义登录API的接口?
+根据需求点选择解决方案:
+
+**2.2.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数?**
+``` java
+// 在任意代码处获取前端提交的参数
+String xxx = SaHolder.getRequest().getParam("xxx");
+```
+
+**2.2.2、想完全自定义一个接口来接受前端登录请求?**
+``` java
+// 直接定义一个拦截路由为 `/sso/doLogin` 的接口即可
+@RequestMapping("/sso/doLogin")
+public SaResult ss(String name, String pwd) {
+ System.out.println("------ 请求进入了自定义的API接口 ---------- ");
+ if("sa".equals(name) && "123456".equals(pwd)) {
+ StpUtil.login(10001);
+ return SaResult.ok("登录成功!");
+ }
+ return SaResult.error("登录失败!");
+}
+```
+
+**2.2.3、不想使用`/sso/doLogin`这个接口,想自定义一个API地址?**
+
+答:直接在前端更改点击按钮时 Ajax 的请求地址即可
+
+
+### 三、常见疑问
+
+##### 问:在模式一与模式二中,Client端 必须通过 Alone-Redis 插件来访问Redis吗?
+
+答:不必须,只是推荐,权限缓存与业务缓存分离后会减少SSO-Redis的访问压力,且可以避免多个Client端的缓存读写冲突
+
+
diff --git a/sa-token-doc/doc/sso/sso-type2.md b/sa-token-doc/doc/sso/sso-type2.md
index 5b1dd70e..eaa6eae9 100644
--- a/sa-token-doc/doc/sso/sso-type2.md
+++ b/sa-token-doc/doc/sso/sso-type2.md
@@ -27,12 +27,21 @@
下面我们按照步骤依次完成上述过程
+### 1、准备工作
+首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`,添加以下IP映射,方便我们进行测试:
+``` url
+127.0.0.1 sa-sso-server.com
+127.0.0.1 sa-sso-client1.com
+127.0.0.1 sa-sso-client2.com
+127.0.0.1 sa-sso-client3.com
+```
-### 1、搭建SSO-Server认证中心
+
+### 2、搭建SSO-Server认证中心
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-server/`,如遇到难点可结合源码进行测试学习
-##### 1.1、创建SSO-Server端项目
+##### 2.1、创建SSO-Server端项目
创建SpringBoot项目 `sa-token-demo-sso-server`(不会的同学自行百度或参考仓库示例),添加pom依赖:
``` xml
@@ -55,7 +64,7 @@
```
-##### 1.2、创建SSO-Server端认证接口
+##### 2.2、创建SSO-Server端认证接口
``` java
/**
* Sa-Token-SSO Server端 Controller
@@ -96,7 +105,7 @@ public class SsoServerController {
```
注意:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数,可通过`SaHolder.getRequest().getParam("xxx")`来获取
-##### 1.4、application.yml配置
+##### 2.3、application.yml配置
``` yml
# 端口
server:
@@ -123,9 +132,9 @@ spring:
# Redis服务器连接密码(默认为空)
password:
```
-注意点:`allow-url`为了方便测试配置为*,线上生产环境一定要配置为详细URL地址 (详见下方“配置域名校验”)
+注意点:`allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (详见下方“配置域名校验”)
-##### 1.4、创建SSO-Server端启动类
+##### 2.4、创建SSO-Server端启动类
``` java
@SpringBootApplication
public class SaSsoServerApplication {
@@ -137,11 +146,11 @@ public class SaSsoServerApplication {
```
-### 2、搭建SSO-Client应用端
+### 3、搭建SSO-Client应用端
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-client/`,如遇到难点可结合源码进行测试学习
-##### 2.1、创建SSO-Client端项目
+##### 3.1、创建SSO-Client端项目
创建一个SpringBoot项目 `sa-token-demo-sso-client`,添加pom依赖:
``` xml
@@ -171,7 +180,7 @@ public class SaSsoServerApplication {
```
-##### 1.2、创建SSO-Client端认证接口
+##### 3.2、创建SSO-Client端认证接口
``` java
/**
@@ -199,7 +208,7 @@ public class SsoClientController {
}
```
-##### 1.3、配置SSO认证中心地址
+##### 3.3、配置SSO认证中心地址
你需要在 `application.yml` 配置如下信息:
``` yml
# 端口
@@ -228,7 +237,7 @@ sa-token:
```
注意点:`sa-token.alone-redis` 的配置需要和SSO-Server端连接同一个Redis(database也要一样)
-##### 1.4、写启动类
+##### 3.4、写启动类
``` java
@SpringBootApplication
public class SaSsoClientApplication {
@@ -241,18 +250,8 @@ public class SaSsoClientApplication {
启动项目
-### 3、测试访问
+### 4、测试访问
-##### 3.1 修改host文件
-首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`,添加以下IP映射,方便我们进行测试:
-``` url
-127.0.0.1 sa-sso-server.com
-127.0.0.1 sa-sso-client1.com
-127.0.0.1 sa-sso-client2.com
-127.0.0.1 sa-sso-client3.com
-```
-
-##### 3.2 启动项目并访问
(1) 依次启动SSO-Server与SSO-Client端,然后从浏览器访问:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)

@@ -292,7 +291,7 @@ public class SaSsoClientApplication {

-### 4、运行官方仓库
+### 5、运行官方仓库
以上示例,虽然完整的复现了单点登录的过程,但是页面还是有些简陋,我们可以运行一下官方仓库的示例,里面有制作好的登录页面
@@ -303,9 +302,9 @@ public class SaSsoClientApplication {
默认测试密码:`sa / 123456`,其余流程保持不变
-### 5、配置域名校验
+### 6、配置域名校验
-##### 5.1、Ticket劫持攻击
+##### 6.1、Ticket劫持攻击
在以上的SSO-Server端示例中,配置项 `sa-token.sso.allow-url=*` 意为配置所有允许的Client端授权地址,不在此配置项中的URL将无法单点登录成功
以上示例为了方便测试被配置为*,但是,在生产环境中,此配置项绝对不能配置为 * ,否则会有被ticket劫持的风险
@@ -320,7 +319,7 @@ public class SaSsoClientApplication {
可以看到,代表着用户身份的ticket码也显现到了URL之中,借此漏洞,攻击者完全可以构建一个URL将小红的ticket码自动提交到攻击者自己的服务器,伪造小红身份登录网站
-##### 5.2、防范方法
+##### 6.2、防范方法
造成此漏洞的直接原因就是SSO-Server认证中心没有对 `redirect地址` 进行任何的限制,防范的方法也很简单,就是对`redirect参数`进行校验,如果其不在指定的URL列表中时,拒绝下放ticket
@@ -330,7 +329,7 @@ public class SaSsoClientApplication {
域名没有通过校验,拒绝授权!
-##### 5.3、配置安全性参考表
+##### 6.3、配置安全性参考表
| 配置方式 | 举例 | 安全性 | 建议 |
| :-------- | :-------- | :-------- | :-------- |
@@ -339,13 +338,14 @@ public class SaSsoClientApplication {
| 配置到详细地址| `http://sa-sso-client1.com:9001/sso/login` | 高 | 可以在生产环境下使用 |
-##### 5.4、疑问:为什么不直接回传Token,而是先回传ticket,再用ticket去查询对应的账号id?
+##### 6.4、疑问:为什么不直接回传Token,而是先回传ticket,再用ticket去查询对应的账号id?
Token作为长时间有效的会话凭证,在任何时候都不应该直接在暴露URL之中(虽然Token直接的暴露本身不会造成安全漏洞,但会为很多漏洞提供可乘之机)
因此Sa-Token-SSO选择先回传ticket,再由ticket获取账号id,且ticket一次性用完即废,提高安全性
-### 6、跨Redis的单点登录
+
+### 7、跨Redis的单点登录
以上流程解决了跨域模式下的单点登录,但是后端仍然采用了共享Redis来同步会话,如果我们的架构设计中Client端与Server端无法共享Redis,又该怎么完成单点登录?
这就要采用模式三了,且往下看:[Http请求获取会话](/sso/sso-type3)