Skip to main content

sa_token_plugin_salvo_v079/
layer.rs

1//! Salvo **`Handler`** implementing the shared auth pipeline / 实现统一鉴权流水线的 Salvo **`Handler`**。
2use salvo::{Depot, FlowCtrl, Handler, Request, Response};
3use salvo::http::StatusCode;
4use sa_token_core::router::PathAuthConfig;
5use sa_token_plugin_salvo_core::{run_auth_flow, SaTokenState};
6
7use crate::adapter::SalvoCapturedRequest;
8
9/// Optional **`PathAuthConfig`**: if set, **`should_reject`** → 401 without `call_next`.
10/// 可选 **`PathAuthConfig`**:命中拒绝条件时直接 **401**,不调用后续。
11///
12/// When `path_config` is `None`, successful login data is written to **`Depot`** (`sa_token`, `sa_login_id`).
13/// `path_config` 为 `None` 时,将 token / login_id 写入 **`Depot`**(`sa_token`、`sa_login_id`)。
14#[derive(Clone)]
15pub struct SaTokenLayer {
16    state: SaTokenState,
17    path_config: Option<PathAuthConfig>,
18}
19
20impl SaTokenLayer {
21    /// No path rules; depot + context only after validation. | 无路径规则,仅校验后写 Depot + 上下文。
22    pub fn new(state: SaTokenState) -> Self {
23        Self {
24            state,
25            path_config: None,
26        }
27    }
28
29    /// Enable Ant-style include/exclude + optional login-id validator (**`PathAuthConfig`**).
30    /// 启用路径包含/排除与可选登录 id 校验(**`PathAuthConfig`**)。
31    pub fn with_path_auth(state: SaTokenState, config: PathAuthConfig) -> Self {
32        Self {
33            state,
34            path_config: Some(config),
35        }
36    }
37}
38
39#[salvo::async_trait]
40impl Handler for SaTokenLayer {
41    async fn handle(
42        &self,
43        req: &mut Request,
44        depot: &mut Depot,
45        res: &mut Response,
46        ctrl: &mut FlowCtrl,
47    ) {
48        // Snapshot before `.await`: avoid borrowing `req` across `run_auth_flow`.
49        // 在 `.await` 前快照:避免跨 `run_auth_flow` 仍借用 `req`。
50        let adapter =
51            SalvoCapturedRequest::capture(req, self.state.manager.config.token_name.as_str());
52        let flow = run_auth_flow(
53            &adapter,
54            &self.state.manager,
55            self.path_config.as_ref(),
56        )
57        .await;
58
59        if flow.should_reject() {
60            res.status_code(StatusCode::UNAUTHORIZED);
61            return;
62        }
63
64        if self.path_config.is_none() {
65            if let Some(ref t) = flow.token {
66                depot.insert("sa_token", t.clone());
67            }
68            if let Some(ref id) = flow.login_id {
69                depot.insert("sa_login_id", id.clone());
70            }
71        }
72
73        flow.run(ctrl.call_next(req, depot, res)).await;
74    }
75}
76
77/// Extract token using snapshot + router helper (compatible with middleware code paths).
78/// 快照 + router 助手提取 token(与其它中间件代码路径对齐)。
79pub fn extract_token_from_request(req: &Request, token_name: &str) -> Option<String> {
80    let cap = SalvoCapturedRequest::capture(req, token_name);
81    sa_token_core::router::extract_token(&cap, token_name)
82}