sa-token/sa-token-doc/fun/sa-token-context.md
2024-04-11 07:59:33 +08:00

181 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 自定义 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
![sa-token-context](https://oss.dev33.cn/sa-token/doc/sa-token-context.svg 's-w')
SaTokenContext只是一个接口没有工作能力这也就意味着 SaTokenContext 接口的实现是必须的。
那么疑问来了,我们之前在 SpringBoot 中引用 Sa-Token 时为什么可以直接使用呢?
其实原理很简单,`sa-token-spring-boot-starter`集成包中已经内置了`SaTokenContext`的实现:[SaTokenContextForSpring](https://gitee.com/dromara/sa-token/blob/master/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`三者的抽象:
- `Request`:请求对象,携带着一次请求的所有参数数据。参考:[SaRequest.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java)。
- `Response`:响应对象,携带着对客户端一次响应的所有数据。参考:[SaResponse.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaResponse.java)。
- `Storage`:请求上下文对象,提供 [一次请求范围内] 的上下文数据读写。参考:[SaStorage.java](https://gitee.com/dromara/sa-token/blob/master/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaStorage.java)。
因此,在实现 `SaTokenContext` 之前,你必须先实现这三个 Model 接口。
先别着急动手,如果你的 Web 框架是基于 Servlet 规范开发的,那么 Sa-Token 已经为你封装好了三个 Model 接口的实现,你要做的就是引入 `sa-token-servlet`包即可:
<!---------------------------- tabs:start ------------------------------>
<!-------- tab:Maven 方式 -------->
``` xml
<!-- Sa-Token 权限认证ServletAPI 集成包) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-servlet</artifactId>
<version>${sa.top.version}</version>
</dependency>
```
<!-------- tab:Gradle 方式 -------->
``` gradle
// Sa-Token 权限认证ServletAPI 集成包)
implementation 'cn.dev33:sa-token-servlet:${sa.top.version}'
```
<!---------------------------- tabs:end ------------------------------>
如果你的 Web 框架不是基于 Servlet 规范,那么你就需要手动实现这三个 Model 接口,我们可以参考 `sa-token-servlet` 是怎样实现的:
[SaRequestForServlet.java](https://gitee.com/dromara/sa-token/blob/master/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/master/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/master/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/master/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/master/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java)、
[注入Bean](https://gitee.com/dromara/sa-token/blob/master/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java)
### 5、启动项目
启动项目,尝试打印一下 `SaManager.getSaTokenContext()` 对象,如果输出的是你的自定义实现类,那就证明你已经自定义 `SaTokenContext` 成功了,
快来体验一下 Sa-Token 的各种功能吧。