From a0715b8aeaa6e48d127790a3f62f0c860d1004aa Mon Sep 17 00:00:00 2001
From: click33 <2393584716@qq.com>
Date: Wed, 15 Sep 2021 22:15:50 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=96=B0=E5=A2=9E=E8=87=AA?=
=?UTF-8?q?=E5=AE=9A=E4=B9=89=20SaTokenContext=20=E6=8C=87=E5=8D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sa-token-doc/doc/_sidebar.md | 1 +
sa-token-doc/doc/fun/sa-token-context.md | 165 ++++++++++++++++++
sa-token-doc/doc/more/common-questions.md | 1 +
sa-token-doc/doc/start/download.md | 2 +
.../spring/SaTokenContextForSpring.java | 2 +-
5 files changed, 170 insertions(+), 1 deletion(-)
create mode 100644 sa-token-doc/doc/fun/sa-token-context.md
diff --git a/sa-token-doc/doc/_sidebar.md b/sa-token-doc/doc/_sidebar.md
index a3676457..c9b29562 100644
--- a/sa-token-doc/doc/_sidebar.md
+++ b/sa-token-doc/doc/_sidebar.md
@@ -80,6 +80,7 @@
- [参考:把权限放在缓存里](/fun/jur-cache)
- [解决跨域问题](/fun/cors-filter)
- [技术选型:SSO 与 OAuth2 对比](/fun/sso-vs-oauth2)
+ - [自定义 SaTokenContext 指南](/fun/sa-token-context)
- [框架源码所有技术栈](/fun/tech-stack)
- [为Sa-Token贡献代码](/fun/git-pr)
- [Sa-Token框架掌握度--在线考试](/fun/sa-token-test)
diff --git a/sa-token-doc/doc/fun/sa-token-context.md b/sa-token-doc/doc/fun/sa-token-context.md
new file mode 100644
index 00000000..c87e629c
--- /dev/null
+++ b/sa-token-doc/doc/fun/sa-token-context.md
@@ -0,0 +1,165 @@
+# 自定义 SaTokenContext 指南
+
+目前 Sa-Token 仅对 SpringBoot、SpringMVC、WebFlux、Solon 等部分 Web 框架制作了 Starter 集成包,
+如果我们使用的都 Web 框架不在上述列表之中,则需要自定义 SaTokenContext 接口的实现完成整合工作。
+
+---
+
+### 1、SaTokenContext是什么,为什么要实现 SaTokenContext 接口?
+
+在鉴权中,必不可少的步骤就是从 `HttpServletRequest` 中读取 Token,然而并不是所有框架都具有 HttpServletRequest 对象,例如在 WebFlux 中,只有 `ServerHttpRequest`,
+在一些其它Web框架中,可能连 `Request` 的概念都没有。
+
+那么,Sa-Token 如何只用一套代码就对接到所有 Web 框架呢?
+
+解决这个问题的关键就在于 `SaTokenContext` 接口,此接口的作用是屏蔽掉不同 Web 框架之间的差异,提供统一的调用API:
+
+
+
+
+SaTokenContext只是一个接口,没有工作能力,这也就意味着 SaTokenContext 接口的实现是必须的。
+那么疑问来了,我们之前在 SpringBoot 中引用 Sa-Token 时为什么可以直接使用呢?
+
+其实原理很简单,`sa-token-spring-boot-starter`集成包中已经内置了`SaTokenContext`的实现:[SaTokenContextForSpring](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java),
+并且根据 Spring 的自动注入特性,在项目启动时注入到 Sa-Token 中,做到“开箱即用”。
+
+那么如果我们使用不是 Spring 框架,是不是就必须得手动实现 `SaTokenContext` 接口?答案是肯定的,脱离Spring 环境后,我们就不能再使用`sa-token-spring-boot-starter`集成包了,
+此时我们只能引入 `sa-token-core` 核心包,然后手动实现 `SaTokenContext` 接口。
+
+不过不用怕,这个工作很简单,只要跟着下面的文档一步步来,你就可以将 Sa-Token 对接到任意Web框架中。
+
+
+### 2、实现 Model 接口
+我们先来观察一下 `SaTokenContext` 接口的签名:
+``` java
+/**
+ * Sa-Token 上下文处理器
+ */
+public interface SaTokenContext {
+
+ /**
+ * 获取当前请求的 [Request] 对象
+ */
+ public SaRequest getRequest();
+
+ /**
+ * 获取当前请求的 [Response] 对象
+ */
+ public SaResponse getResponse();
+
+ /**
+ * 获取当前请求的 [存储器] 对象
+ */
+ public SaStorage getStorage();
+
+ /**
+ * 校验指定路由匹配符是否可以匹配成功指定路径
+ */
+ public boolean matchPath(String pattern, String path);
+
+}
+```
+
+你可能对 `SaRequest` 比较疑惑,这个对象是干什么用的?正如每个 Web 框架都有 Request 概念的抽象,Sa-Token 也封装了 `Request`、`Response`、`Storage`三者的抽象,
+因此在实现 `SaTokenContext` 之前,你必须先实现这三个 Model 接口。
+
+先别着急动手,如果你的 Web 框架是基于 Servlet 规范开发的,那么 Sa-Token 已经为你封装好了三个 Model 接口的实现,你要做的就是引入 `sa-token-servlet`包即可:
+
+``` xml
+
+
+ cn.dev33
+ sa-token-servlet
+ ${sa.top.version}
+
+```
+
+如果你的 Web 框架不是基于 Servlet 规范,那么你就需要手动实现这三个 Model 接口,我们可以参考 `sa-token-servlet` 是怎样实现的:
+[SaRequestForServlet.java](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java)、
+[SaResponseForServlet.java](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaResponseForServlet.java)、
+[SaStorageForServlet.java](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaStorageForServlet.java)。
+
+
+### 3、实现 SaTokenContext 接口
+
+接下来我们奔入主题,提供 `SaTokenContext` 接口的实现,同样我们可以参考 Spring 集成包是怎样实现的:
+
+``` java
+/**
+ * Sa-Token 上下文处理器 [ SpringMVC版本实现 ]
+ */
+public class SaTokenContextForSpring implements SaTokenContext {
+
+ /**
+ * 获取当前请求的Request对象
+ */
+ @Override
+ public SaRequest getRequest() {
+ return new SaRequestForServlet(SpringMVCUtil.getRequest());
+ }
+
+ /**
+ * 获取当前请求的Response对象
+ */
+ @Override
+ public SaResponse getResponse() {
+ return new SaResponseForServlet(SpringMVCUtil.getResponse());
+ }
+
+ /**
+ * 获取当前请求的 [存储器] 对象
+ */
+ @Override
+ public SaStorage getStorage() {
+ return new SaStorageForServlet(SpringMVCUtil.getRequest());
+ }
+
+ /**
+ * 校验指定路由匹配符是否可以匹配成功指定路径
+ */
+ @Override
+ public boolean matchPath(String pattern, String path) {
+ return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
+ }
+
+}
+```
+
+详细参考:
+[SaTokenContextForSpring.java](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java)
+
+
+### 4、将自定义实现注入到 Sa-Token 框架中
+
+有了 `SaTokenContext` 接口的实现,我们还需要将这个实现类注入到 Sa-Token 之中,伪代码参考如下:
+``` java
+/**
+ * 程序启动类
+ */
+public class Application {
+
+ public static void main(String[] args) {
+ // 框架启动
+ XxxApplication.run(xxx);
+
+ // 将自定义的 SaTokenContext 实现类注入到框架中
+ SaTokenContext saTokenContext = new SaTokenContextForXxx();
+ SaManager.setSaTokenContext(saTokenContext);
+ }
+
+}
+```
+
+如果你使用的框架带有自动注入特性,那就更简单了,参考 Spring 集成包的 Bean 注入流程:
+[注册Bean](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanRegister.java)、
+[注入Bean](https://gitee.com/dromara/sa-token/blob/dev/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java)
+
+
+### 5、启动项目
+
+启动项目,尝试打印一下 `SaManager.getSaTokenContext()` 对象,如果输出的是你的自定义实现类,那就证明你已经自定义 `SaTokenContext` 成功了,
+快来体验一下 Sa-Token 的各种功能吧。
+
+
+
+
diff --git a/sa-token-doc/doc/more/common-questions.md b/sa-token-doc/doc/more/common-questions.md
index 8dda9f98..d5a493ba 100644
--- a/sa-token-doc/doc/more/common-questions.md
+++ b/sa-token-doc/doc/more/common-questions.md
@@ -30,6 +30,7 @@
- 如果是 WebFlux 环境就引入 `sa-token-reactor-spring-boot-starter` 依赖,参考:[在WebFlux环境集成](/start/webflux-example)
- 引入错误的依赖会导致`SaTokenContext`初始化失败,抛出上述异常
- 如果你还无法分辨你是哪个环境,就看你的 pom.xml 依赖,如果引入了`spring-boot-starter-web`就是SpringMVC环境,如果引入了 `spring-boot-starter-webflux` 就是WebFlux环境。……什么?你说你两个都引入了?那你的项目能启动成功吗?
+ - 你说你两个包都没引入?那你为什么不引入一个呢?
3. 如果是 WebFlux 环境而且正确引入了依赖,依然报错,请检查是否注册了全局过滤器,在 WebFlux 下这一步是必须的。
4. 如果以上步骤排除无误后依然报错,请直接提 issues 或者加入QQ群求助。
diff --git a/sa-token-doc/doc/start/download.md b/sa-token-doc/doc/start/download.md
index 0a6afd2e..84fbf663 100644
--- a/sa-token-doc/doc/start/download.md
+++ b/sa-token-doc/doc/start/download.md
@@ -39,6 +39,7 @@
${sa.top.version}
```
+引入此依赖需要自定义 SaTokenContext 实现,参考:[自定义 SaTokenContext 指南](/fun/sa-token-context)
注:如果你的项目既没有使用 SpringMVC、WebFlux,也不是基于 ServletAPI 规范,那么可以引入core核心包
@@ -50,6 +51,7 @@
${sa.top.version}
```
+引入此依赖需要自定义 SaTokenContext 实现,参考:[自定义 SaTokenContext 指南](/fun/sa-token-context)
diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java
index 6f6f1fe2..685ccc5c 100644
--- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java
+++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java
@@ -9,7 +9,7 @@ import cn.dev33.satoken.servlet.model.SaResponseForServlet;
import cn.dev33.satoken.servlet.model.SaStorageForServlet;
/**
- * sa-token 对Cookie的相关操作 接口实现类
+ * Sa-Token 上下文处理器 [ SpringMVC版本实现 ]
*
* @author kong
*