做 Rust 后端时,很多团队都会遇到同一类问题:
- 项目能跑,但认证授权方案“拼装感”太强
- 框架一变(Axum、Actix、Rocket...),中间件和鉴权逻辑要重写
- 业务变复杂后,登录、权限、SSO、WebSocket、微服务会话开始互相拉扯
- 代码能交付,但后续升级和维护很痛苦
sa-token-rust 想解决的不是“能不能登录”,而是:
在 Rust 里,把认证授权做成一个可持续演进的工程底座。
先看结论
sa-token-rust 是一个轻量、高性能、可扩展的 Rust 认证授权框架,覆盖 9 大 Web 框架,支持 JWT / OAuth2 / SSO / WebSocket / 在线用户 / 分布式 Session,且在最新演进中完成了多版本框架适配重构与认证链路统一抽象,兼顾“现在能用”和“未来好升”。
这套框架到底解决了什么
1) 从“功能点堆砌”变成“体系化认证授权”
在很多项目里,这些能力通常是分散实现:
- 登录登出一套
- 权限角色一套
- Token 刷新一套
- WebSocket 鉴权又一套
- SSO / OAuth2 再来一套
sa-token-rust 的做法是把它们统一到同一个认证模型下:
SaTokenManager + StpUtil + Context + EventBus + Plugin Adapter
结果是:你不再是在“接 10 个散件”,而是在“用一套完整系统”。
2) 覆盖 9 大 Rust Web 框架,统一接入体验
支持框架:
- Axum
- Actix-web
- Poem
- Rocket
- Warp
- Salvo
- Tide
- Gotham
- Ntex
统一心智:
SaTokenState::builder()初始化SaTokenLayer/SaTokenMiddleware接入- Extractor 读登录态
StpUtil做业务层 API(登录、权限、角色、session 等)
3) 面向真实生产场景,不只“Hello World 登录”
- 认证基础:登录、登出、踢下线、会话管理
- 授权控制:权限/角色校验,支持通配符规则(如
user:*) - 安全能力:Nonce 防重放、Refresh Token 刷新、JWT 多算法
- 协议能力:OAuth2 授权码模式(RFC 6749)
- 组织能力:SSO 单点登录(票据+统一登出)
- 实时能力:WebSocket 鉴权 + 在线用户管理 + 消息推送
- 微服务能力:分布式 Session 跨服务共享
- 可观测能力:事件监听(登录、登出、踢下线等)
功能全景图(直观版)
| 模块 | 解决什么问题 | 典型用途 |
|---|---|---|
| StpUtil API | 统一业务调用入口 | 登录、登出、查权限、查角色 |
| PathAuthConfig | 路径级鉴权规则 | /api/** 保护、/public/** 放行 |
| JWT | 自包含 Token | 网关透传、跨服务鉴权 |
| OAuth2 | 第三方授权 | 开放平台、统一授权中心 |
| SSO | 单点登录 | 多系统一次登录、统一退出 |
| WebSocket Auth | 长连接认证 | IM、实时协作、推送系统 |
| OnlineManager | 在线状态与推送 | 在线人数、定向通知、踢下线 |
| Distributed Session | 微服务会话共享 | 网关 + 多服务共享登录态 |
| EventBus | 认证事件扩展点 | 审计日志、风控、告警 |
从 README 和示例看,它为什么“上手快”
一行依赖、一行导入(插件模式)
- 依赖层可按框架直接使用
sa-token-plugin-* - 导入层可直接
use sa_token_plugin_xxx::* - Builder 里就能把存储、超时、token 名等配齐
stp_util_demo 展示的是真实业务闭环
示例不是只演示 login(),而是完整串起来:
- 登录拿 token
- 校验登录态
- 读取 login_id 和 token_info
- 读有效期
- 操作 session
- 动态加权限、加角色
- 做权限和角色检查
- 登出并验证状态
这基本就是很多中后台项目首周要做的鉴权闭环。
这次版本演进最值得说的三件事
1) 多版本框架适配重构:解决“升级就炸”问题
过去常见痛点
- 插件对框架版本绑定过死
- 用户项目升级大版本时容易出现类型树冲突
- 维护时同一逻辑在多个插件重复散落
本次架构升级思路
- A 组(axum/warp/poem/tide):单 crate + 版本 feature
- B 组(actix/rocket/salvo/ntex/gotham):
-core+-vN+ facade
结果
- 用户迁移路径更清晰
- 插件内部耦合下降,后续扩展更稳
- 大版本支持可以“增量演进”,不是“全量推倒”
过去怎么用(示意)
业务 Cargo.toml 里写死一套框架大版本,插件往往只跟其中一条线对齐;团队想从 Axum 0.7 升到 0.8、或 Actix 3 升到 4 时,经常遇到「插件与主项目类型树/依赖树冲突、只能等插件发新版或自己 fork」。
# 过去:框架与插件强绑定一条线,大版本升级容易“牵一发而动全身”
axum = "0.7"
# 假设当时示例/文档都按 0.7 写,换 0.8 后中间件与 Request 类型要对齐大量细节- 1
- 2
- 3
现在怎么用(示意)
A 组(Axum / Warp / Poem / Tide):仍是 单一sa-token-plugin-* crate,用 框架绑定 feature(如 axum-08)选对大版本,存储再用 memory / redis 等。
sa-token-plugin-axum = { version = "0.1.13", features = ["axum-08", "redis"] }- 1
B 组(Actix / Rocket / Salvo / Ntex / Gotham): 门面 crate + features 选具体大版本(如 v4、v05),共享逻辑在 *-core,版本差异在 *-v*。
sa-token-plugin-actix-web = { version = "0.1.13", features = ["v4", "redis"] }- 1
2) 认证链路统一到 run_auth_flow
把“token 提取 + 鉴权决策 + context 生成”做成统一流程,下沉到核心层;插件层只做框架相关写入和响应。
直接收益:
- 各框架行为更一致
- 减少重复逻辑和分叉 bug
- 后续优化可以一处改动,多处生效
过去怎么用(示意)
每个插件中间件里各自实现「从 Header/Cookie 取 token → 调 SaTokenManager 校验 → 拼 SaTokenContext → 写 extensions / 401」;逻辑相似但复制多,行为容易慢慢不一致。
// 过去:伪代码 — 各框架各写一套,核心层没有统一入口
// let token = self.extract_token_from_request(&req); // 每插件一份
// let auth = self.process_path_and_validate(token).await;
// let ctx = self.build_context(&auth);
// SaTokenContext::set_current(ctx);
// let res = inner.call(req).await;
// SaTokenContext::clear();- 1
- 2
- 3
- 4
- 5
- 6
- 7
现在怎么用(示意)
核心层提供 run_auth_flow,一次走完「提取 token → 路径规则/默认校验 → 生成上下文」;插件只负责把实现了 SaRequest 的请求快照喂进去(如 Axum 的 AxumRequestSnapshot::capture(&request)),以及把 token / login_id 写进框架自己的扩展位,并用 AuthFlowResult::run 把后续 handler 包在正确的作用域里(与第 3 点的 task-local 一致)。
// 现在:与 sa-token-plugin-axum 0.8 中间件同思路的精简示意
let snapshot = AxumRequestSnapshot::capture(&request);
let flow = run_auth_flow(&snapshot, &state.manager, path_config.as_ref()).await;
if flow.should_reject() {
return Ok(unauthorized_response());
}
if let Some(t) = &flow.token {
request.extensions_mut().insert(t.clone());
}
if let Some(id) = &flow.login_id {
request.extensions_mut().insert(id.clone());
}
flow.run(inner.call(request)).await- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
3) 上下文传播安全升级路线:thread-local -> task-local
已明确迁移方案,用于修复异步多线程 runtime 下跨 await 的上下文一致性风险。
这是典型“用户不一定看得见,但线上稳定性强相关”的改进。
这种方向说明项目不只是在“加功能”,也在持续做底层可靠性建设。
过去怎么用(示意)
请求级上下文若主要依赖 thread-local 的 set_current / clear,在 异步、多线程调度 的 runtime 里,任务可能在不同 worker 线程上续跑;跨过 await 后若仍假设“同一线程上的 TLS”,存在上下文丢失或串台的理论风险,中间件里需要非常小心配对。
// 过去:示意 — 依赖线程局部 + 手动配对,跨 await 场景要自证安全
// SaTokenContext::set_current(ctx);
// let response = inner.call(request).await;
// SaTokenContext::clear();- 1
- 2
- 3
- 4
现在怎么用(示意)
路线是 task-local(及 SaTokenContext::scope):与异步任务绑定, 可安全跨 await。业务侧推荐通过框架插件已接好的链路走;核心 API 上,AuthFlowResult::run 内部即使用 SaTokenContext::scope 执行后续 Future。
// 现在:与核心实现一致 — scope 包裹整个后续异步过程
// pub async fn run<F, R>(self, fut: F) -> R
// where F: Future<Output = R> {
// SaTokenContext::scope(self.context, fut).await
// }
// 插件里典型写法就是一行:
flow.run(inner.call(request)).await- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
若你在自定义中间件里需要同样语义,可直接使用 SaTokenContext::scope(ctx, async { ... }).await,避免手写 set_current / clear 的配对逻辑。
场景化:你可以怎么落地
场景 A:中后台管理系统
- 登录态 + 角色权限 + 路由保护
- 宏注解和中间件搭配,减少重复 if/else 校验
场景 B:SaaS 多端登录
- Web / App / 小程序多端并行
- 设备级登录控制 + 踢下线 + 在线状态管理
场景 C:开放平台
- OAuth2 授权码模式
- 客户端注册、scope 控制、token 刷新/撤销
场景 D:企业统一身份
- SSO 统一登录入口
- 多系统票据校验
- 统一登出保证安全闭环
场景 E:微服务架构
- 分布式 Session 跨服务共享
- 网关和业务服务统一鉴权上下文
场景 F:实时系统
- WebSocket 握手认证
- 在线用户追踪与实时消息推送
对团队最有价值的点(管理者视角)
- 降低重复建设:不同项目、不同框架都可复用统一方案
- 降低维护成本:认证授权逻辑收敛,升级更可控
- 提高安全下限:内置 Nonce、Refresh、错误体系、路径鉴权
- 提升交付效率:文档体系完整,示例可直接跑通
对开发者最有价值的点(工程师视角)
- API 心智统一,不容易“每个项目都重学一遍”
- 从最小可用到复杂场景,都是同一套模型
- 插件式接入 + Builder 配置,能快速起步
- 事件监听机制非常适合接审计、风控、告警
行动建议
推荐上手路径(30-60 分钟):
- 选你当前框架的
sa-token-plugin-*跑一个最小 demo - 用
StpUtil完成登录 + 权限 + 角色 + session 闭环 - 再按业务扩展 JWT / OAuth2 / SSO / WebSocket 模块
- 最后接事件监听做审计与风控
这样做,你会很快判断它是否适合你的团队长期作为认证授权底座。
开源地址
- GitHub:https://github.com/sa-tokens/sa-token-rust
- Gitee:https://gitee.com/sa-tokens/sa-token-rust
- AtomGit:https://atomgit.com/sa-tokens/sa-token-rust
鲁公网安备37011202002956号