Skip to main content

sa_token_core/
util.rs

1// Author: 金书记
2//
3//! StpUtil — static façade over the process-wide `SaTokenManager`.
4//! StpUtil —— 进程内全局 `SaTokenManager` 的静态门面。
5//!
6//! ```rust,ignore
7//! use sa_token_core::StpUtil;
8//!
9//! StpUtil::try_init_manager(manager)?;
10//! let token = StpUtil::login("user_123").await?;
11//! ```
12
13use std::borrow::Cow;
14use std::sync::Arc;
15use std::sync::OnceLock;
16
17use crate::context::SaTokenContext;
18use crate::event::{SaTokenEventBus, SaTokenListener};
19use crate::keys::LOGIN_TYPE_DEFAULT;
20use crate::session::SaSession;
21use crate::token::{TokenInfo, TokenValue};
22use crate::{SaTokenError, SaTokenManager, SaTokenResult};
23
24/// 全局 SaTokenManager 实例(标准库 OnceLock,Rust 1.70+)
25static GLOBAL_MANAGER: OnceLock<Arc<SaTokenManager>> = OnceLock::new();
26
27/// LoginId trait — 登录 ID 零拷贝优先。
28/// LoginId trait — prefer zero-copy login ids.
29pub trait LoginId {
30    /// 借用或拥有登录 ID;`&str` / `String` 走 Borrowed。
31    /// Borrow or own the login id; `&str` / `String` use Borrowed.
32    fn as_login_id(&self) -> Cow<'_, str>;
33
34    /// 兼容旧 API:总是得到 owned `String`。
35    /// Legacy helper: always returns an owned `String`.
36    fn to_login_id(&self) -> String {
37        self.as_login_id().into_owned()
38    }
39}
40
41impl LoginId for str {
42    fn as_login_id(&self) -> Cow<'_, str> {
43        Cow::Borrowed(self)
44    }
45}
46
47impl LoginId for String {
48    fn as_login_id(&self) -> Cow<'_, str> {
49        Cow::Borrowed(self.as_str())
50    }
51}
52
53impl LoginId for &String {
54    fn as_login_id(&self) -> Cow<'_, str> {
55        Cow::Borrowed(self.as_str())
56    }
57}
58
59impl LoginId for &str {
60    fn as_login_id(&self) -> Cow<'_, str> {
61        Cow::Borrowed(*self)
62    }
63}
64
65macro_rules! impl_login_id_display {
66    ($($t:ty),*) => {$(
67        impl LoginId for $t {
68            fn as_login_id(&self) -> Cow<'_, str> {
69                Cow::Owned(self.to_string())
70            }
71        }
72    )*};
73}
74impl_login_id_display!(i32, i64, u32, u64, i16, u16, isize, usize);
75
76/// Static helpers for login, logout, and authorization.
77/// 登录、登出与鉴权的静态辅助方法。
78pub struct StpUtil;
79
80impl StpUtil {
81    // ==================== 初始化 ====================
82
83    /// 尝试初始化全局 Manager(应用启动调用一次)
84    /// Try to initialize the global manager (call once at startup).
85    ///
86    /// 重复调用返回 `AlreadyInitialized`,**不 panic**。
87    /// Duplicate calls return `AlreadyInitialized` without panicking.
88    pub fn try_init_manager(manager: SaTokenManager) -> SaTokenResult<()> {
89        GLOBAL_MANAGER
90            .set(Arc::new(manager))
91            .map_err(|_| SaTokenError::AlreadyInitialized)
92    }
93
94    /// 初始化全局 Manager(兼容旧 API;重复仍 panic)
95    /// Initialize global manager (legacy; still panics on duplicate).
96    ///
97    /// # 示例
98    /// ```rust,ignore
99    /// let manager = SaTokenConfig::builder()
100    ///     .storage(Arc::new(MemoryStorage::new()))
101    ///     .build();
102    /// StpUtil::init_manager(manager);
103    /// ```
104    #[deprecated(note = "use try_init_manager() which returns Result instead of panicking")]
105    #[allow(clippy::panic)]
106    pub fn init_manager(manager: SaTokenManager) {
107        Self::try_init_manager(manager).unwrap_or_else(|e| {
108            panic!("{e}");
109        });
110    }
111
112    /// 尝试获取全局 Manager
113    /// Try to get the global manager
114    pub fn try_get_manager() -> SaTokenResult<&'static Arc<SaTokenManager>> {
115        GLOBAL_MANAGER.get().ok_or(SaTokenError::NotInitialized)
116    }
117
118    /// 获取全局 Manager(未初始化时 panic;仅内部兼容,优先 `try_get_manager`)
119    /// Get the global manager (panics if missing; prefer `try_get_manager`).
120    #[track_caller]
121    #[allow(dead_code, clippy::panic)]
122    pub(crate) fn get_manager() -> &'static Arc<SaTokenManager> {
123        Self::try_get_manager().unwrap_or_else(|e| {
124            panic!("{e}. Call StpUtil::try_init_manager() first.");
125        })
126    }
127
128    /// 尝试获取全局配置(Manager 初始化前返回 None)
129    ///
130    /// Try to get global config; returns `None` before Manager initialization.
131    pub(crate) fn try_get_config() -> Option<&'static crate::config::SaTokenConfig> {
132        GLOBAL_MANAGER.get().map(|m| m.config.as_ref())
133    }
134
135    /// 解析「当前请求所属账号体系」(修 B2-23)。
136    ///
137    /// 顺序:请求上下文中的 token 元信息 → `default`。
138    /// 无请求上下文时回落 `default`,保持旧行为。`Cow` 让常见分支零分配。
139    ///
140    /// Resolves the login type of the current request: token metadata in the
141    /// request context first, then `default`. Falls back to `default` without a
142    /// request context, preserving old behaviour. `Cow` keeps the common branch
143    /// allocation-free.
144    #[inline]
145    fn resolve_login_type() -> Cow<'static, str> {
146        match SaTokenContext::current_login_type() {
147            Some(login_type) => Cow::Owned(login_type),
148            None => Cow::Borrowed(LOGIN_TYPE_DEFAULT),
149        }
150    }
151
152    /// 获取事件总线,用于注册监听器
153    ///
154    /// # 示例
155    /// ```rust,ignore
156    /// use sa_token_core::{StpUtil, SaTokenListener};
157    /// use async_trait::async_trait;
158    ///
159    /// struct MyListener;
160    ///
161    /// #[async_trait]
162    /// impl SaTokenListener for MyListener {
163    ///     async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
164    ///         println!("用户 {} 登录了", login_id);
165    ///     }
166    /// }
167    ///
168    /// // 注册监听器
169    /// StpUtil::event_bus().register(Arc::new(MyListener));
170    /// ```
171    /// 尝试获取事件总线;未初始化返回 `None`(不 panic)。
172    /// Try to get the event bus; `None` before init (no panic).
173    pub fn event_bus() -> Option<&'static SaTokenEventBus> {
174        GLOBAL_MANAGER.get().map(|m| &m.event_bus)
175    }
176
177    /// 注册事件监听器(便捷方法);未初始化时静默跳过。
178    /// Register a listener; no-op when the manager is not initialized.
179    ///
180    /// # 示例
181    /// ```rust,ignore
182    /// StpUtil::register_listener(Arc::new(MyListener));
183    /// ```
184    pub fn register_listener(listener: Arc<dyn SaTokenListener>) {
185        if let Some(bus) = Self::event_bus() {
186            bus.register(listener);
187        }
188    }
189
190    // ==================== 登录相关 ====================
191
192    /// 会话登录
193    ///
194    /// # 示例
195    /// ```rust,ignore
196    /// // 支持字符串 ID
197    /// let token = StpUtil::login("user_123").await?;
198    ///
199    /// // 支持数字 ID
200    /// let token = StpUtil::login(10001).await?;
201    /// let token = StpUtil::login(10001_i64).await?;
202    /// ```
203    pub async fn login(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
204        Self::try_get_manager()?.login(login_id.to_login_id()).await
205    }
206
207    /// `login_with_type` — login with type | `login_with_type`
208    pub async fn login_with_type(
209        login_id: impl LoginId,
210        login_type: impl Into<String>,
211    ) -> SaTokenResult<TokenValue> {
212        Self::try_get_manager()?
213            .login_with_options(
214                login_id.to_login_id(),
215                Some(login_type.into()),
216                None,
217                None,
218                None,
219                None,
220            )
221            .await
222    }
223
224    /// 登录并设置额外数据 | Login with extra data
225    ///
226    /// # 参数 | Arguments
227    /// * `login_id` - 登录ID | Login ID
228    /// * `extra_data` - 额外数据 | Extra data
229    pub async fn login_with_extra(
230        login_id: impl LoginId,
231        extra_data: serde_json::Value,
232    ) -> SaTokenResult<TokenValue> {
233        Self::try_get_manager()?
234            .login_with_options(
235                login_id.to_login_id(),
236                None, // login_type
237                None, // device
238                Some(extra_data),
239                None, // nonce
240                None, // expire_time
241            )
242            .await
243    }
244
245    /// 会话登录(带 manager 参数的版本,向后兼容)
246    pub async fn login_with_manager(
247        manager: &SaTokenManager,
248        login_id: impl Into<String>,
249    ) -> SaTokenResult<TokenValue> {
250        manager.login(login_id).await
251    }
252
253    /// 会话登出
254    pub async fn logout(token: &TokenValue) -> SaTokenResult<()> {
255        tracing::debug!("开始执行 logout,token: {}", token);
256        let result = Self::try_get_manager()?.logout(token).await;
257        match &result {
258            Ok(_) => tracing::debug!("logout 执行成功,token: {}", token),
259            Err(e) => tracing::debug!("logout 执行失败,token: {}, 错误: {}", token, e),
260        }
261        result
262    }
263
264    /// `logout_with_manager` — logout with manager | `logout_with_manager`
265    pub async fn logout_with_manager(
266        manager: &SaTokenManager,
267        token: &TokenValue,
268    ) -> SaTokenResult<()> {
269        manager.logout(token).await
270    }
271
272    /// Opt-in write of the token cookie (no-op unless `is_write_cookie` is true).
273    /// 可选写入 token Cookie(未开启 `is_write_cookie` 时为空操作)。
274    pub fn write_token_cookie<R: sa_token_adapter::context::SaResponse>(
275        res: &mut R,
276        token: &TokenValue,
277    ) -> SaTokenResult<()> {
278        let manager = Self::try_get_manager()?;
279        crate::token_io::write_token_cookie(res, token, &manager.config);
280        Ok(())
281    }
282
283    /// Clear the token cookie (same opt-in guard as write).
284    /// 清除 token Cookie(与写入同一开关)。
285    pub fn delete_token_cookie<R: sa_token_adapter::context::SaResponse>(
286        res: &mut R,
287    ) -> SaTokenResult<()> {
288        let manager = Self::try_get_manager()?;
289        crate::token_io::delete_token_cookie(res, &manager.config);
290        Ok(())
291    }
292
293    /// Set per-token idle timeout. No-op unless `dynamic_active_timeout` is enabled.
294    /// 设置单 token 闲置超时。未打开 `dynamic_active_timeout` 时返回 ConfigError。
295    pub async fn update_active_timeout(token: &TokenValue, seconds: i64) -> SaTokenResult<()> {
296        Self::try_get_manager()?
297            .update_active_timeout(token, seconds)
298            .await
299    }
300
301    /// 踢人下线(使用当前请求 login_type,无上下文则 default)
302    /// Kick out (uses current request login_type; falls back to default).
303    pub async fn kick_out(login_id: impl LoginId) -> SaTokenResult<()> {
304        let login_type = Self::resolve_login_type();
305        Self::kick_out_with_type(login_type.as_ref(), login_id).await
306    }
307
308    /// `kick_out_with_type` — kick out with type | `kick_out_with_type`
309    pub async fn kick_out_with_type(login_type: &str, login_id: impl LoginId) -> SaTokenResult<()> {
310        Self::try_get_manager()?
311            .kick_out(login_type, &login_id.to_login_id())
312            .await
313    }
314
315    /// `kick_out_with_manager` — kick out with manager | `kick_out_with_manager`
316    pub async fn kick_out_with_manager(
317        manager: &SaTokenManager,
318        login_type: &str,
319        login_id: impl LoginId,
320    ) -> SaTokenResult<()> {
321        manager.kick_out(login_type, &login_id.to_login_id()).await
322    }
323
324    /// 强制登出(使用当前请求 login_type,无上下文则 default)
325    /// Force logout by login_id (uses current request login_type; falls back to default).
326    pub async fn logout_by_login_id(login_id: impl LoginId) -> SaTokenResult<()> {
327        let login_type = Self::resolve_login_type();
328        Self::logout_by_login_id_with_type(login_type.as_ref(), login_id).await
329    }
330
331    /// `logout_by_login_id_with_type` — logout by login id with type | `logout_by_login_id_with_type`
332    pub async fn logout_by_login_id_with_type(
333        login_type: &str,
334        login_id: impl LoginId,
335    ) -> SaTokenResult<()> {
336        Self::try_get_manager()?
337            .logout_by_login_id(login_type, &login_id.to_login_id())
338            .await
339    }
340
341    /// 根据 token 登出(别名方法,更直观)
342    pub async fn logout_by_token(token: &TokenValue) -> SaTokenResult<()> {
343        Self::logout(token).await
344    }
345
346    // ==================== 当前会话操作(无参数,从上下文获取)====================
347
348    /// 获取当前请求的 token(无参数,从上下文获取)
349    ///
350    /// # 示例
351    /// ```rust,ignore
352    /// // 在请求处理函数中
353    /// let token = StpUtil::get_token_value()?;
354    /// ```
355    pub fn get_token_value() -> SaTokenResult<TokenValue> {
356        let ctx = SaTokenContext::try_current().ok_or(SaTokenError::NotLogin)?;
357        ctx.token().ok_or(SaTokenError::NotLogin)
358    }
359
360    /// 当前会话登出(无参数,从上下文获取 token)
361    ///
362    /// # 示例
363    /// ```rust,ignore
364    /// // 在请求处理函数中
365    /// StpUtil::logout_current().await?;
366    /// ```
367    pub async fn logout_current() -> SaTokenResult<()> {
368        let token = Self::get_token_value()?;
369        tracing::debug!("成功获取 token: {}", token);
370
371        let result = Self::logout(&token).await;
372        match &result {
373            Ok(_) => tracing::debug!("logout_current 执行成功,token: {}", token),
374            Err(e) => tracing::debug!("logout_current 执行失败,token: {}, 错误: {}", token, e),
375        }
376        result
377    }
378
379    /// 检查当前会话是否登录(同步弱校验:仅看上下文是否有 token 字符串,不查存储)
380    /// Sync weak check: whether context has a token string (does not hit storage).
381    ///
382    /// 踢出/过期后若中间件未刷新上下文,仍可能为 true。强保证用 [`check_login_current_async`]。
383    /// May still be true after kick/expire if middleware did not refresh context.
384    pub fn is_login_current() -> bool {
385        Self::get_token_value().is_ok()
386    }
387
388    /// 检查当前会话登录状态(同步弱校验),未登录则抛出异常
389    /// Sync weak check; returns error when context has no token.
390    pub fn check_login_current() -> SaTokenResult<()> {
391        Self::get_token_value()?;
392        Ok(())
393    }
394
395    /// 异步强校验:上下文有 token 且 storage 仍有效
396    /// Async strong check: context has a token AND storage still considers it valid.
397    pub async fn check_login_current_async() -> SaTokenResult<()> {
398        let token = Self::get_token_value()?;
399        if !Self::try_get_manager()?.is_valid(&token).await {
400            return Err(SaTokenError::NotLogin);
401        }
402        Ok(())
403    }
404
405    /// 异步强校验是否登录
406    /// Async strong login check returning bool.
407    pub async fn is_login_current_async() -> bool {
408        Self::check_login_current_async().await.is_ok()
409    }
410
411    /// 获取当前会话的 login_id(String 类型,无参数)
412    ///
413    /// # 示例
414    /// ```rust,ignore
415    /// // 在请求处理函数中
416    /// let login_id = StpUtil::get_login_id_as_string().await?;
417    /// ```
418    pub async fn get_login_id_as_string() -> SaTokenResult<String> {
419        if let Some(ctx) = SaTokenContext::get_current() {
420            if let Some(switch_id) = ctx.switch_login_id() {
421                return Ok(switch_id);
422            }
423        }
424        let token = Self::get_token_value()?;
425        Self::get_login_id(&token).await
426    }
427
428    /// 获取当前会话的 login_id(i64 类型,无参数)
429    ///
430    /// # 示例
431    /// ```rust,ignore
432    /// // 在请求处理函数中
433    /// let user_id = StpUtil::get_login_id_as_long().await?;
434    /// ```
435    pub async fn get_login_id_as_long() -> SaTokenResult<i64> {
436        let login_id_str = Self::get_login_id_as_string().await?;
437        login_id_str
438            .parse::<i64>()
439            .map_err(|_| SaTokenError::LoginIdNotNumber)
440    }
441
442    /// 获取当前会话的 token 信息(无参数)
443    ///
444    /// # 示例
445    /// ```rust,ignore
446    /// // 在请求处理函数中
447    /// let token_info = StpUtil::get_token_info_current()?;
448    /// println!("Token 创建时间: {:?}", token_info.create_time);
449    /// ```
450    pub fn get_token_info_current() -> SaTokenResult<Arc<TokenInfo>> {
451        let ctx = SaTokenContext::try_current().ok_or(SaTokenError::NotLogin)?;
452        ctx.token_info().ok_or(SaTokenError::NotLogin)
453    }
454
455    // ==================== Token 验证 ====================
456
457    /// 检查当前 token 是否已登录
458    pub async fn is_login(token: &TokenValue) -> bool {
459        let Ok(manager) = Self::try_get_manager() else {
460            return false;
461        };
462        manager.is_valid(token).await
463    }
464
465    /// 根据登录 ID 检查是否已登录
466    ///
467    /// # 示例
468    /// ```rust,ignore
469    /// let is_logged_in = StpUtil::is_login_by_login_id("user_123").await;
470    /// let is_logged_in = StpUtil::is_login_by_login_id(10001).await;
471    /// ```
472    pub async fn is_login_by_login_id(login_id: impl LoginId) -> bool {
473        match Self::get_token_by_login_id(login_id).await {
474            Ok(token) => Self::is_login(&token).await,
475            Err(_) => false,
476        }
477    }
478
479    /// `is_login_with_manager` — is login with manager | `is_login_with_manager`
480    pub async fn is_login_with_manager(manager: &SaTokenManager, token: &TokenValue) -> bool {
481        manager.is_valid(token).await
482    }
483
484    /// 检查当前 token 是否已登录,如果未登录则抛出异常
485    pub async fn check_login(token: &TokenValue) -> SaTokenResult<()> {
486        if !Self::is_login(token).await {
487            return Err(SaTokenError::NotLogin);
488        }
489        Ok(())
490    }
491
492    /// 获取 token 信息
493    pub async fn get_token_info(token: &TokenValue) -> SaTokenResult<TokenInfo> {
494        Self::try_get_manager()?.get_token_info(token).await
495    }
496
497    /// 获取当前 token 的登录ID
498    pub async fn get_login_id(token: &TokenValue) -> SaTokenResult<String> {
499        let token_info = Self::try_get_manager()?.get_token_info(token).await?;
500        Ok(token_info.login_id.to_string())
501    }
502
503    /// 获取当前 token 的登录ID,如果未登录则返回默认值
504    pub async fn get_login_id_or_default(token: &TokenValue, default: impl Into<String>) -> String {
505        Self::get_login_id(token)
506            .await
507            .unwrap_or_else(|_| default.into())
508    }
509
510    /// 根据登录 ID 获取当前用户的 token
511    ///
512    /// # 示例
513    /// ```rust,ignore
514    /// let token = StpUtil::get_token_by_login_id("user_123").await?;
515    /// let token = StpUtil::get_token_by_login_id(10001).await?;
516    /// ```
517    pub async fn get_token_by_login_id(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
518        let login_type = Self::resolve_login_type();
519        Self::get_token_by_login_id_with_type(login_type.as_ref(), login_id).await
520    }
521
522    /// 指定 login_type 获取当前账号的 login:token 映射。
523    ///
524    /// 委托 Manager,禁止 StpUtil 直连 TokenRepo。
525    pub async fn get_token_by_login_id_with_type(
526        login_type: &str,
527        login_id: impl LoginId,
528    ) -> SaTokenResult<TokenValue> {
529        Self::try_get_manager()?
530            .get_token_by_login_id(login_type, &login_id.to_login_id())
531            .await
532    }
533
534    /// `get_all_tokens_by_login_id` — get all tokens by login id | `get_all_tokens_by_login_id`
535    pub async fn get_all_tokens_by_login_id(
536        login_id: impl LoginId,
537    ) -> SaTokenResult<Vec<TokenValue>> {
538        let login_type = Self::resolve_login_type();
539        Self::get_all_tokens_by_login_id_with_type(login_type.as_ref(), login_id).await
540    }
541
542    /// 指定 login_type 获取全部在线 token。
543    ///
544    /// 委托 Manager(内部走 TokenRepo list 原语)。
545    pub async fn get_all_tokens_by_login_id_with_type(
546        login_type: &str,
547        login_id: impl LoginId,
548    ) -> SaTokenResult<Vec<TokenValue>> {
549        Self::try_get_manager()?
550            .get_all_tokens_by_login_id(login_type, &login_id.to_login_id())
551            .await
552    }
553
554    // ==================== Session 会话 ====================
555
556    /// `get_session_with_type` — get session with type | `get_session_with_type`
557    pub async fn get_session_with_type(
558        login_type: &str,
559        login_id: impl LoginId,
560    ) -> SaTokenResult<SaSession> {
561        Self::try_get_manager()?
562            .get_session_with_type(login_type, &login_id.to_login_id())
563            .await
564    }
565
566    /// `delete_session_with_type` — delete session with type | `delete_session_with_type`
567    pub async fn delete_session_with_type(
568        login_type: &str,
569        login_id: impl LoginId,
570    ) -> SaTokenResult<()> {
571        Self::try_get_manager()?
572            .delete_session_with_type(login_type, &login_id.to_login_id())
573            .await
574    }
575
576    /// 获取当前登录账号的 Session(使用当前请求 login_type)
577    /// Get session for login_id (uses current request login_type).
578    pub async fn get_session(login_id: impl LoginId) -> SaTokenResult<SaSession> {
579        let login_type = Self::resolve_login_type();
580        Self::get_session_with_type(login_type.as_ref(), login_id).await
581    }
582
583    /// 保存 Session
584    pub async fn save_session(session: &SaSession) -> SaTokenResult<()> {
585        Self::try_get_manager()?.save_session(session).await
586    }
587
588    /// 删除 Session(使用当前请求 login_type)
589    /// Delete session (uses current request login_type).
590    pub async fn delete_session(login_id: impl LoginId) -> SaTokenResult<()> {
591        let login_type = Self::resolve_login_type();
592        Self::delete_session_with_type(login_type.as_ref(), login_id).await
593    }
594
595    /// 在 Session 中设置值(使用当前请求 login_type)
596    /// Set a value in session (uses current request login_type).
597    pub async fn set_session_value<T: serde::Serialize>(
598        login_id: impl LoginId,
599        key: &str,
600        value: T,
601    ) -> SaTokenResult<()> {
602        let login_type = Self::resolve_login_type();
603        let manager = Self::try_get_manager()?;
604        let login_id_str = login_id.to_login_id();
605        let mut session = manager
606            .get_session_with_type(login_type.as_ref(), &login_id_str)
607            .await?;
608        session.set(key, value)?;
609        manager.save_session(&session).await
610    }
611
612    /// 从 Session 中获取值(使用当前请求 login_type)
613    /// Get a value from session (uses current request login_type).
614    pub async fn get_session_value<T: serde::de::DeserializeOwned>(
615        login_id: impl LoginId,
616        key: &str,
617    ) -> SaTokenResult<Option<T>> {
618        let login_type = Self::resolve_login_type();
619        let session = Self::get_session_with_type(login_type.as_ref(), login_id).await?;
620        Ok(session.get::<T>(key))
621    }
622
623    // ==================== Token 相关 ====================
624
625    /// 创建一个新的 token(但不登录)
626    pub fn create_token(token_value: impl Into<String>) -> TokenValue {
627        TokenValue::new(token_value.into())
628    }
629
630    /// 检查 token 格式是否有效(仅检查格式,不检查是否存在于存储中)
631    pub fn is_valid_token_format(token: &str) -> bool {
632        !token.is_empty() && token.len() >= 16
633    }
634}
635
636impl std::fmt::Debug for StpUtil {
637    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
638        f.write_str("StpUtil { .. }")
639    }
640}
641
642// ==================== 权限管理 ====================
643
644impl StpUtil {
645    // ---------- 权限:写入 | Permissions: writes ----------
646
647    /// 覆盖权限列表(指定账号体系)| Overwrite permissions for a login type
648    pub async fn set_permissions_with_type(
649        login_type: &str,
650        login_id: impl LoginId,
651        permissions: Vec<String>,
652    ) -> SaTokenResult<()> {
653        Self::try_get_manager()?
654            .set_permissions_with_type(login_type, &login_id.to_login_id(), permissions)
655            .await
656    }
657
658    /// 覆盖权限列表 | Overwrite permissions
659    pub async fn set_permissions(
660        login_id: impl LoginId,
661        permissions: Vec<String>,
662    ) -> SaTokenResult<()> {
663        let login_type = Self::resolve_login_type();
664        Self::set_permissions_with_type(&login_type, login_id, permissions).await
665    }
666
667    /// 追加单个权限(指定账号体系)| Append one permission for a login type
668    pub async fn add_permission_with_type(
669        login_type: &str,
670        login_id: impl LoginId,
671        permission: impl Into<String>,
672    ) -> SaTokenResult<()> {
673        Self::try_get_manager()?
674            .add_permission_with_type(login_type, &login_id.to_login_id(), permission.into())
675            .await
676    }
677
678    /// 追加单个权限 | Append one permission
679    pub async fn add_permission(
680        login_id: impl LoginId,
681        permission: impl Into<String>,
682    ) -> SaTokenResult<()> {
683        let login_type = Self::resolve_login_type();
684        Self::add_permission_with_type(&login_type, login_id, permission).await
685    }
686
687    /// 移除单个权限(指定账号体系)| Remove one permission for a login type
688    pub async fn remove_permission_with_type(
689        login_type: &str,
690        login_id: impl LoginId,
691        permission: &str,
692    ) -> SaTokenResult<()> {
693        Self::try_get_manager()?
694            .remove_permission_with_type(login_type, &login_id.to_login_id(), permission)
695            .await
696    }
697
698    /// 移除单个权限 | Remove one permission
699    pub async fn remove_permission(login_id: impl LoginId, permission: &str) -> SaTokenResult<()> {
700        let login_type = Self::resolve_login_type();
701        Self::remove_permission_with_type(&login_type, login_id, permission).await
702    }
703
704    /// 清空权限(指定账号体系)| Clear permissions for a login type
705    pub async fn clear_permissions_with_type(
706        login_type: &str,
707        login_id: impl LoginId,
708    ) -> SaTokenResult<()> {
709        Self::try_get_manager()?
710            .clear_permissions_with_type(login_type, &login_id.to_login_id())
711            .await
712    }
713
714    /// 清空权限 | Clear permissions
715    pub async fn clear_permissions(login_id: impl LoginId) -> SaTokenResult<()> {
716        let login_type = Self::resolve_login_type();
717        Self::clear_permissions_with_type(&login_type, login_id).await
718    }
719
720    // ---------- 权限:读取 | Permissions: reads ----------
721
722    /// 获取权限列表(指定账号体系,错误上抛)
723    /// Permission list for a login type, propagating errors.
724    pub async fn try_get_permissions_with_type(
725        login_type: &str,
726        login_id: impl LoginId,
727    ) -> SaTokenResult<Vec<String>> {
728        Self::try_get_manager()?
729            .get_permissions_with_type(login_type, &login_id.to_login_id())
730            .await
731    }
732
733    /// 获取权限列表(错误上抛,修 B2-39)
734    /// Unlike `get_permissions`, this propagates storage failures.
735    pub async fn try_get_permissions(login_id: impl LoginId) -> SaTokenResult<Vec<String>> {
736        let login_type = Self::resolve_login_type();
737        Self::try_get_permissions_with_type(&login_type, login_id).await
738    }
739
740    /// 获取权限列表(失败返回空表 + 告警日志,修 B2-39)
741    /// Keeps the old signature; failures now log a warning instead of being silent.
742    pub async fn get_permissions(login_id: impl LoginId) -> Vec<String> {
743        let login_id = login_id.to_login_id();
744        match Self::try_get_permissions(&login_id).await {
745            Ok(list) => list,
746            Err(e) => {
747                tracing::warn!(
748                    login_id = %login_id,
749                    error = %e,
750                    "failed to load permissions, treating as empty"
751                );
752                Vec::new()
753            }
754        }
755    }
756
757    // ---------- 权限:校验 | Permissions: checks ----------
758
759    /// 单个权限校验(指定账号体系)| Single permission check for a login type
760    pub async fn has_permission_with_type(
761        login_type: &str,
762        login_id: impl LoginId,
763        permission: &str,
764    ) -> bool {
765        let Ok(manager) = Self::try_get_manager() else {
766            return false;
767        };
768        manager
769            .authz_service()
770            .has_permission(login_type, &login_id.to_login_id(), permission)
771            .await
772            .unwrap_or(false)
773    }
774
775    /// 单个权限校验 | Single permission check
776    pub async fn has_permission(login_id: impl LoginId, permission: &str) -> bool {
777        let login_type = Self::resolve_login_type();
778        Self::has_permission_with_type(&login_type, login_id, permission).await
779    }
780
781    /// 批量权限校验(AND,指定账号体系)| Batch AND check for a login type
782    pub async fn has_all_permissions_with_type(
783        login_type: &str,
784        login_id: impl LoginId,
785        permissions: &[&str],
786    ) -> bool {
787        let Ok(manager) = Self::try_get_manager() else {
788            return false;
789        };
790        manager
791            .authz_service()
792            .has_all_permissions(login_type, &login_id.to_login_id(), permissions)
793            .await
794            .unwrap_or(false)
795    }
796
797    /// 批量权限校验(AND)| Batch AND check
798    pub async fn has_all_permissions(login_id: impl LoginId, permissions: &[&str]) -> bool {
799        let login_type = Self::resolve_login_type();
800        Self::has_all_permissions_with_type(&login_type, login_id, permissions).await
801    }
802
803    /// [`has_all_permissions`](Self::has_all_permissions) 的别名 | Alias
804    pub async fn has_permissions_and(login_id: impl LoginId, permissions: &[&str]) -> bool {
805        Self::has_all_permissions(login_id, permissions).await
806    }
807
808    /// 批量权限校验(OR,指定账号体系)| Batch OR check for a login type
809    pub async fn has_any_permission_with_type(
810        login_type: &str,
811        login_id: impl LoginId,
812        permissions: &[&str],
813    ) -> bool {
814        let Ok(manager) = Self::try_get_manager() else {
815            return false;
816        };
817        manager
818            .authz_service()
819            .has_any_permission(login_type, &login_id.to_login_id(), permissions)
820            .await
821            .unwrap_or(false)
822    }
823
824    /// 批量权限校验(OR)| Batch OR check
825    pub async fn has_any_permission(login_id: impl LoginId, permissions: &[&str]) -> bool {
826        let login_type = Self::resolve_login_type();
827        Self::has_any_permission_with_type(&login_type, login_id, permissions).await
828    }
829
830    /// [`has_any_permission`](Self::has_any_permission) 的别名 | Alias
831    pub async fn has_permissions_or(login_id: impl LoginId, permissions: &[&str]) -> bool {
832        Self::has_any_permission(login_id, permissions).await
833    }
834
835    /// 权限校验(失败返回 `Err`,指定账号体系)
836    /// Permission check returning `Err` on denial, for a login type.
837    pub async fn check_permission_with_type(
838        login_type: &str,
839        login_id: impl LoginId,
840        permission: &str,
841    ) -> SaTokenResult<()> {
842        Self::try_get_manager()?
843            .authz_service()
844            .check_permission(login_type, &login_id.to_login_id(), permission)
845            .await
846    }
847
848    /// 权限校验(失败返回 `Err`)| Permission check returning `Err` on denial
849    pub async fn check_permission(login_id: impl LoginId, permission: &str) -> SaTokenResult<()> {
850        let login_type = Self::resolve_login_type();
851        Self::check_permission_with_type(&login_type, login_id, permission).await
852    }
853
854    /// 批量权限校验(AND,失败返回 `Err`,B2-36 新增)
855    /// Batch AND check returning `Err` on denial.
856    pub async fn check_all_permissions(
857        login_id: impl LoginId,
858        permissions: &[&str],
859    ) -> SaTokenResult<()> {
860        let login_type = Self::resolve_login_type();
861        Self::try_get_manager()?
862            .authz_service()
863            .check_all_permissions(&login_type, &login_id.to_login_id(), permissions)
864            .await
865    }
866
867    /// 批量权限校验(OR,失败返回 `Err`,B2-36 新增)
868    /// Batch OR check returning `Err` on denial.
869    pub async fn check_any_permission(
870        login_id: impl LoginId,
871        permissions: &[&str],
872    ) -> SaTokenResult<()> {
873        let login_type = Self::resolve_login_type();
874        Self::try_get_manager()?
875            .authz_service()
876            .check_any_permission(&login_type, &login_id.to_login_id(), permissions)
877            .await
878    }
879}
880
881// ==================== 角色管理 ====================
882
883impl StpUtil {
884    // ---------- 角色:写入 | Roles: writes ----------
885
886    /// 覆盖角色列表(指定账号体系)| Overwrite roles for a login type
887    pub async fn set_roles_with_type(
888        login_type: &str,
889        login_id: impl LoginId,
890        roles: Vec<String>,
891    ) -> SaTokenResult<()> {
892        Self::try_get_manager()?
893            .set_roles_with_type(login_type, &login_id.to_login_id(), roles)
894            .await
895    }
896
897    /// 覆盖角色列表 | Overwrite roles
898    pub async fn set_roles(login_id: impl LoginId, roles: Vec<String>) -> SaTokenResult<()> {
899        let login_type = Self::resolve_login_type();
900        Self::set_roles_with_type(&login_type, login_id, roles).await
901    }
902
903    /// 追加单个角色(指定账号体系)| Append one role for a login type
904    pub async fn add_role_with_type(
905        login_type: &str,
906        login_id: impl LoginId,
907        role: impl Into<String>,
908    ) -> SaTokenResult<()> {
909        Self::try_get_manager()?
910            .add_role_with_type(login_type, &login_id.to_login_id(), role.into())
911            .await
912    }
913
914    /// 追加单个角色 | Append one role
915    pub async fn add_role(login_id: impl LoginId, role: impl Into<String>) -> SaTokenResult<()> {
916        let login_type = Self::resolve_login_type();
917        Self::add_role_with_type(&login_type, login_id, role).await
918    }
919
920    /// 移除单个角色(指定账号体系)| Remove one role for a login type
921    pub async fn remove_role_with_type(
922        login_type: &str,
923        login_id: impl LoginId,
924        role: &str,
925    ) -> SaTokenResult<()> {
926        Self::try_get_manager()?
927            .remove_role_with_type(login_type, &login_id.to_login_id(), role)
928            .await
929    }
930
931    /// 移除单个角色 | Remove one role
932    pub async fn remove_role(login_id: impl LoginId, role: &str) -> SaTokenResult<()> {
933        let login_type = Self::resolve_login_type();
934        Self::remove_role_with_type(&login_type, login_id, role).await
935    }
936
937    /// 清空角色(指定账号体系)| Clear roles for a login type
938    pub async fn clear_roles_with_type(
939        login_type: &str,
940        login_id: impl LoginId,
941    ) -> SaTokenResult<()> {
942        Self::try_get_manager()?
943            .clear_roles_with_type(login_type, &login_id.to_login_id())
944            .await
945    }
946
947    /// 清空角色 | Clear roles
948    pub async fn clear_roles(login_id: impl LoginId) -> SaTokenResult<()> {
949        let login_type = Self::resolve_login_type();
950        Self::clear_roles_with_type(&login_type, login_id).await
951    }
952
953    // ---------- 角色:读取 | Roles: reads ----------
954
955    /// 获取角色列表(指定账号体系,错误上抛)
956    /// Role list for a login type, propagating errors.
957    pub async fn try_get_roles_with_type(
958        login_type: &str,
959        login_id: impl LoginId,
960    ) -> SaTokenResult<Vec<String>> {
961        Self::try_get_manager()?
962            .get_roles_with_type(login_type, &login_id.to_login_id())
963            .await
964    }
965
966    /// 获取角色列表(错误上抛,修 B2-39)| Role list propagating errors
967    pub async fn try_get_roles(login_id: impl LoginId) -> SaTokenResult<Vec<String>> {
968        let login_type = Self::resolve_login_type();
969        Self::try_get_roles_with_type(&login_type, login_id).await
970    }
971
972    /// 获取角色列表(失败返回空表 + 告警日志)| Role list, empty on failure with a warning
973    pub async fn get_roles(login_id: impl LoginId) -> Vec<String> {
974        let login_id = login_id.to_login_id();
975        match Self::try_get_roles(&login_id).await {
976            Ok(list) => list,
977            Err(e) => {
978                tracing::warn!(
979                    login_id = %login_id,
980                    error = %e,
981                    "failed to load roles, treating as empty"
982                );
983                Vec::new()
984            }
985        }
986    }
987
988    // ---------- 角色:校验 | Roles: checks ----------
989
990    /// 单个角色校验(指定账号体系)| Single role check for a login type
991    pub async fn has_role_with_type(login_type: &str, login_id: impl LoginId, role: &str) -> bool {
992        let Ok(manager) = Self::try_get_manager() else {
993            return false;
994        };
995        manager
996            .authz_service()
997            .has_role(login_type, &login_id.to_login_id(), role)
998            .await
999            .unwrap_or(false)
1000    }
1001
1002    /// 单个角色校验 | Single role check
1003    pub async fn has_role(login_id: impl LoginId, role: &str) -> bool {
1004        let login_type = Self::resolve_login_type();
1005        Self::has_role_with_type(&login_type, login_id, role).await
1006    }
1007
1008    /// 批量角色校验(AND,指定账号体系)| Batch AND role check for a login type
1009    pub async fn has_all_roles_with_type(
1010        login_type: &str,
1011        login_id: impl LoginId,
1012        roles: &[&str],
1013    ) -> bool {
1014        let Ok(manager) = Self::try_get_manager() else {
1015            return false;
1016        };
1017        manager
1018            .authz_service()
1019            .has_all_roles(login_type, &login_id.to_login_id(), roles)
1020            .await
1021            .unwrap_or(false)
1022    }
1023
1024    /// 批量角色校验(AND)| Batch AND role check
1025    pub async fn has_all_roles(login_id: impl LoginId, roles: &[&str]) -> bool {
1026        let login_type = Self::resolve_login_type();
1027        Self::has_all_roles_with_type(&login_type, login_id, roles).await
1028    }
1029
1030    /// [`has_all_roles`](Self::has_all_roles) 的别名 | Alias
1031    pub async fn has_roles_and(login_id: impl LoginId, roles: &[&str]) -> bool {
1032        Self::has_all_roles(login_id, roles).await
1033    }
1034
1035    /// 批量角色校验(OR,指定账号体系)| Batch OR role check for a login type
1036    pub async fn has_any_role_with_type(
1037        login_type: &str,
1038        login_id: impl LoginId,
1039        roles: &[&str],
1040    ) -> bool {
1041        let Ok(manager) = Self::try_get_manager() else {
1042            return false;
1043        };
1044        manager
1045            .authz_service()
1046            .has_any_role(login_type, &login_id.to_login_id(), roles)
1047            .await
1048            .unwrap_or(false)
1049    }
1050
1051    /// 批量角色校验(OR)| Batch OR role check
1052    pub async fn has_any_role(login_id: impl LoginId, roles: &[&str]) -> bool {
1053        let login_type = Self::resolve_login_type();
1054        Self::has_any_role_with_type(&login_type, login_id, roles).await
1055    }
1056
1057    /// [`has_any_role`](Self::has_any_role) 的别名 | Alias
1058    pub async fn has_roles_or(login_id: impl LoginId, roles: &[&str]) -> bool {
1059        Self::has_any_role(login_id, roles).await
1060    }
1061
1062    /// 角色校验(失败返回 `Err`,指定账号体系)
1063    /// Role check returning `Err` on denial, for a login type.
1064    pub async fn check_role_with_type(
1065        login_type: &str,
1066        login_id: impl LoginId,
1067        role: &str,
1068    ) -> SaTokenResult<()> {
1069        Self::try_get_manager()?
1070            .authz_service()
1071            .check_role(login_type, &login_id.to_login_id(), role)
1072            .await
1073    }
1074
1075    /// 角色校验(失败返回 `Err`)| Role check returning `Err` on denial
1076    pub async fn check_role(login_id: impl LoginId, role: &str) -> SaTokenResult<()> {
1077        let login_type = Self::resolve_login_type();
1078        Self::check_role_with_type(&login_type, login_id, role).await
1079    }
1080
1081    /// 批量角色校验(AND,失败返回 `Err`,B2-36 新增)
1082    /// Batch AND role check returning `Err`.
1083    pub async fn check_all_roles(login_id: impl LoginId, roles: &[&str]) -> SaTokenResult<()> {
1084        let login_type = Self::resolve_login_type();
1085        Self::try_get_manager()?
1086            .authz_service()
1087            .check_all_roles(&login_type, &login_id.to_login_id(), roles)
1088            .await
1089    }
1090
1091    /// 批量角色校验(OR,失败返回 `Err`,B2-36 新增)
1092    /// Batch OR role check returning `Err`.
1093    pub async fn check_any_role(login_id: impl LoginId, roles: &[&str]) -> SaTokenResult<()> {
1094        let login_type = Self::resolve_login_type();
1095        Self::try_get_manager()?
1096            .authz_service()
1097            .check_any_role(&login_type, &login_id.to_login_id(), roles)
1098            .await
1099    }
1100}
1101
1102// ==================== 封禁(disable) ====================
1103
1104impl StpUtil {
1105    /// 封禁账号(默认服务 login;使用当前请求 login_type)
1106    /// Disable account (default service; uses current request login_type).
1107    pub async fn disable(login_id: impl LoginId, time: i64) -> SaTokenResult<()> {
1108        let login_type = Self::resolve_login_type();
1109        Self::try_get_manager()?
1110            .disable_with_type(login_type.as_ref(), &login_id.to_login_id(), time)
1111            .await
1112    }
1113
1114    /// 指定 login_type 封禁
1115    /// Disable with explicit login_type.
1116    pub async fn disable_with_type(
1117        login_type: &str,
1118        login_id: impl LoginId,
1119        time: i64,
1120    ) -> SaTokenResult<()> {
1121        Self::try_get_manager()?
1122            .disable_with_type(login_type, &login_id.to_login_id(), time)
1123            .await
1124    }
1125
1126    /// 封禁账号指定服务与等级(使用当前请求 login_type)
1127    /// Disable with service/level (uses current request login_type).
1128    pub async fn disable_level(
1129        login_id: impl LoginId,
1130        service: &str,
1131        level: i32,
1132        time: i64,
1133    ) -> SaTokenResult<()> {
1134        let login_type = Self::resolve_login_type();
1135        Self::try_get_manager()?
1136            .disable_level_with_type(
1137                login_type.as_ref(),
1138                &login_id.to_login_id(),
1139                service,
1140                level,
1141                time,
1142            )
1143            .await
1144    }
1145
1146    /// 校验封禁(默认服务 login、最低等级)
1147    pub async fn check_disable(login_id: impl LoginId) -> SaTokenResult<()> {
1148        Self::check_disable_level(
1149            login_id,
1150            crate::disable::DEFAULT_DISABLE_SERVICE,
1151            crate::disable::MIN_DISABLE_LEVEL,
1152        )
1153        .await
1154    }
1155
1156    /// 校验指定服务的封禁
1157    pub async fn check_disable_service(login_id: impl LoginId, service: &str) -> SaTokenResult<()> {
1158        Self::check_disable_level(login_id, service, crate::disable::MIN_DISABLE_LEVEL).await
1159    }
1160
1161    /// 校验多个服务的封禁(使用当前请求 login_type)
1162    pub async fn check_disable_services(
1163        login_id: impl LoginId,
1164        services: &[&str],
1165    ) -> SaTokenResult<()> {
1166        let login_type = Self::resolve_login_type();
1167        Self::try_get_manager()?
1168            .check_disable_services_with_type(
1169                login_type.as_ref(),
1170                &login_id.to_login_id(),
1171                services,
1172                crate::disable::MIN_DISABLE_LEVEL,
1173            )
1174            .await
1175    }
1176
1177    /// 校验封禁等级(使用当前请求 login_type)
1178    pub async fn check_disable_level(
1179        login_id: impl LoginId,
1180        service: &str,
1181        level: i32,
1182    ) -> SaTokenResult<()> {
1183        let login_type = Self::resolve_login_type();
1184        Self::try_get_manager()?
1185            .check_disable_level_with_type(
1186                login_type.as_ref(),
1187                &login_id.to_login_id(),
1188                service,
1189                level,
1190            )
1191            .await
1192    }
1193
1194    /// 获取封禁等级(使用当前请求 login_type)
1195    /// Get disable level (uses current request login_type).
1196    pub async fn get_disable_level(login_id: impl LoginId, service: &str) -> SaTokenResult<i32> {
1197        let login_type = Self::resolve_login_type();
1198        Self::get_disable_level_with_type(login_type.as_ref(), login_id, service).await
1199    }
1200
1201    /// 指定 login_type 获取封禁等级
1202    /// Get disable level with explicit login_type.
1203    pub async fn get_disable_level_with_type(
1204        login_type: &str,
1205        login_id: impl LoginId,
1206        service: &str,
1207    ) -> SaTokenResult<i32> {
1208        Self::try_get_manager()?
1209            .get_disable_level_with_type(login_type, &login_id.to_login_id(), service)
1210            .await
1211    }
1212
1213    /// 解封(使用当前请求 login_type)
1214    pub async fn untie_disable(login_id: impl LoginId, service: &str) -> SaTokenResult<()> {
1215        let login_type = Self::resolve_login_type();
1216        Self::try_get_manager()?
1217            .untie_disable_with_type(login_type.as_ref(), &login_id.to_login_id(), service)
1218            .await
1219    }
1220}
1221
1222// ==================== 二级认证(safe) ====================
1223
1224impl StpUtil {
1225    /// 为当前 token 开启二级认证
1226    pub async fn open_safe(service: &str, safe_time: i64) -> SaTokenResult<()> {
1227        let token = Self::get_token_value()?;
1228        Self::try_get_manager()?
1229            .open_safe(&token, service, safe_time)
1230            .await
1231    }
1232
1233    /// 当前 token 是否已通过二级认证
1234    pub async fn is_safe(service: &str) -> SaTokenResult<bool> {
1235        let token = Self::get_token_value()?;
1236        Self::try_get_manager()?.is_safe(&token, service).await
1237    }
1238
1239    /// 校验当前 token 的二级认证
1240    pub async fn check_safe(service: &str) -> SaTokenResult<()> {
1241        Self::check_login_current()?;
1242        let token = Self::get_token_value()?;
1243        Self::try_get_manager()?.check_safe(&token, service).await
1244    }
1245
1246    /// 关闭当前 token 的二级认证
1247    pub async fn close_safe(service: &str) -> SaTokenResult<()> {
1248        let token = Self::get_token_value()?;
1249        Self::try_get_manager()?.close_safe(&token, service).await
1250    }
1251}
1252
1253// ==================== 身份临时切换(B3 核心修复)| Identity Switch (B3 core fix) ====================
1254
1255impl StpUtil {
1256    /// 临时切换为指定 login_id(写入请求上下文,task-local 与 thread-local 单轨就地突变)
1257    ///
1258    /// Temporarily switch to the specified login_id (in-place mutation across task-local and thread-local).
1259    pub fn switch_to(login_id: impl LoginId) {
1260        let target = login_id.to_login_id();
1261        SaTokenContext::with_current_mut(|inner| {
1262            inner.switch_login_id = Some(target);
1263        });
1264    }
1265
1266    /// 结束临时身份切换(清除 `switch_login_id`,恢复真实身份)
1267    ///
1268    /// End identity switch (clears `switch_login_id`, restoring real identity).
1269    pub fn end_switch() {
1270        SaTokenContext::with_current_mut(|inner| {
1271            inner.switch_login_id = None;
1272        });
1273    }
1274
1275    /// 是否处于临时身份切换中
1276    ///
1277    /// Whether currently inside an identity switch.
1278    pub fn is_switch() -> bool {
1279        SaTokenContext::get_current()
1280            .and_then(|c| c.switch_login_id())
1281            .is_some()
1282    }
1283
1284    /// 获取临时切换的 login_id(审计日志用)
1285    ///
1286    /// Get the switched login_id (for audit logs).
1287    pub fn get_switch_login_id() -> Option<String> {
1288        SaTokenContext::get_current().and_then(|c| c.switch_login_id())
1289    }
1290}
1291
1292// ==================== 扩展工具方法 ====================
1293
1294impl StpUtil {
1295    /// 批量踢人下线(使用当前请求 login_type)
1296    /// Batch kick-out (uses current request login_type).
1297    pub async fn kick_out_batch<T: LoginId>(
1298        login_ids: &[T],
1299    ) -> SaTokenResult<Vec<Result<(), SaTokenError>>> {
1300        let manager = Self::try_get_manager()?;
1301        let login_type = Self::resolve_login_type();
1302        let mut results = Vec::new();
1303        for login_id in login_ids {
1304            results.push(
1305                manager
1306                    .kick_out(login_type.as_ref(), &login_id.to_login_id())
1307                    .await,
1308            );
1309        }
1310        Ok(results)
1311    }
1312
1313    /// 获取 token 剩余有效时间(秒)
1314    pub async fn get_token_timeout(token: &TokenValue) -> SaTokenResult<Option<i64>> {
1315        let manager = Self::try_get_manager()?;
1316        let token_info = manager.get_token_info(token).await?;
1317
1318        if let Some(expire_time) = token_info.expire_time {
1319            let now = chrono::Utc::now();
1320            let duration = expire_time.signed_duration_since(now);
1321            Ok(Some(duration.num_seconds()))
1322        } else {
1323            Ok(None) // 永久有效
1324        }
1325    }
1326
1327    /// 续期 token(重置过期时间)。
1328    ///
1329    /// 委托 Manager → AuthService,避免 StpUtil 直写 storage 与 B1 续签策略分叉。
1330    pub async fn renew_timeout(token: &TokenValue, timeout_seconds: i64) -> SaTokenResult<()> {
1331        Self::try_get_manager()?
1332            .renew_timeout(token, timeout_seconds)
1333            .await
1334    }
1335
1336    // ==================== 额外数据操作 | Extra Data Operations ====================
1337
1338    /// 设置 Token 的额外数据。
1339    ///
1340    /// 委托 Manager::update_extra_data,禁止 StpUtil 直连 TokenRepo。
1341    pub async fn set_extra_data(
1342        token: &TokenValue,
1343        extra_data: serde_json::Value,
1344    ) -> SaTokenResult<()> {
1345        Self::try_get_manager()?
1346            .update_extra_data(token, extra_data)
1347            .await
1348    }
1349
1350    /// 获取 Token 的额外数据 | Get extra data from token
1351    ///
1352    /// # 参数 | Arguments
1353    /// * `token` - Token值 | Token value
1354    pub async fn get_extra_data(token: &TokenValue) -> SaTokenResult<Option<serde_json::Value>> {
1355        let manager = Self::try_get_manager()?;
1356        let token_info = manager.get_token_info(token).await?;
1357        Ok(token_info.extra_data)
1358    }
1359
1360    // ==================== 终端信息 ====================
1361
1362    /// List terminals for the account | 列出账号终端
1363    pub async fn get_terminal_list(
1364        login_id: &str,
1365        device_type: Option<&str>,
1366    ) -> SaTokenResult<Vec<crate::session::SaTerminalInfo>> {
1367        let login_type = Self::resolve_login_type();
1368        Self::try_get_manager()?
1369            .get_terminal_list(login_type.as_ref(), login_id, device_type)
1370            .await
1371    }
1372
1373    /// `get_token_value_list_by_login_id` — get token value list by login id | `get_token_value_list_by_login_id`
1374    pub async fn get_token_value_list_by_login_id(
1375        login_id: &str,
1376        device_type: Option<&str>,
1377    ) -> SaTokenResult<Vec<String>> {
1378        let login_type = Self::resolve_login_type();
1379        Self::try_get_manager()?
1380            .get_token_value_list_by_login_id(login_type.as_ref(), login_id, device_type)
1381            .await
1382    }
1383
1384    /// Terminal info for a token | 按 Token 查终端信息
1385    pub async fn get_terminal_info_by_token(
1386        token: &TokenValue,
1387    ) -> SaTokenResult<Option<crate::session::SaTerminalInfo>> {
1388        Self::try_get_manager()?
1389            .get_terminal_info_by_token(token)
1390            .await
1391    }
1392
1393    /// Require the current token's device type to equal `expected` (exact match).
1394    /// 要求当前 token 的设备类型等于 `expected`(精确匹配,区分大小写)。
1395    pub async fn check_current_terminal(expected: &str) -> SaTokenResult<()> {
1396        Self::check_login_current_async().await?;
1397        let token = Self::get_token_value()?;
1398        let term = Self::get_terminal_info_by_token(&token).await?;
1399        let actual = term.map(|t| t.device_type).unwrap_or_default();
1400        if actual != expected {
1401            return Err(SaTokenError::TerminalDenied {
1402                expected: expected.to_string(),
1403                actual,
1404            });
1405        }
1406        Ok(())
1407    }
1408
1409    // ==================== 多账号体系 ====================
1410
1411    /// 创建绑定 login_type 的廉价 Clone 门面(无全局注册表)
1412    /// Create a cheap Clone facade for login_type (no global registry).
1413    pub fn stp_logic(login_type: &str) -> SaTokenResult<crate::stp_logic::SaLogic> {
1414        Ok(crate::stp_logic::SaLogic::new(
1415            login_type,
1416            Self::try_get_manager()?.as_ref().clone(),
1417        ))
1418    }
1419
1420    /// 已废弃:SaLogic 为可克隆门面,无需注册
1421    /// Deprecated: SaLogic is a cloneable facade; nothing to register.
1422    #[deprecated(note = "SaLogic is a cloneable facade; use SaLogic::new / StpUtil::stp_logic")]
1423    pub fn put_stp_logic(_logic: crate::stp_logic::SaLogic) {}
1424
1425    /// 已废弃:SaLogic 为可克隆门面,无需移除
1426    /// Deprecated: SaLogic is a cloneable facade; nothing to remove.
1427    #[deprecated(note = "SaLogic is a cloneable facade; nothing to remove")]
1428    pub fn remove_stp_logic(_login_type: &str) {}
1429
1430    // ==================== Token Session ====================
1431
1432    /// 获取 token-session
1433    pub async fn get_token_session(token: &TokenValue) -> SaTokenResult<SaSession> {
1434        Self::try_get_manager()?.get_token_session(token).await
1435    }
1436
1437    /// 获取当前请求的 token-session
1438    pub async fn get_token_session_current() -> SaTokenResult<SaSession> {
1439        let token = Self::get_token_value()?;
1440        Self::get_token_session(&token).await
1441    }
1442
1443    /// 保存 token-session
1444    pub async fn save_token_session(token: &TokenValue, session: &SaSession) -> SaTokenResult<()> {
1445        Self::try_get_manager()?
1446            .save_token_session(token, session)
1447            .await
1448    }
1449
1450    /// 删除 token-session
1451    pub async fn delete_token_session(token: &TokenValue) -> SaTokenResult<()> {
1452        Self::try_get_manager()?.delete_token_session(token).await
1453    }
1454
1455    /// 按 token 踢人下线
1456    pub async fn kick_out_by_token(token: &TokenValue) -> SaTokenResult<()> {
1457        Self::try_get_manager()?.kick_out_by_token(token).await
1458    }
1459
1460    // ==================== 授权快照 | Grant Scope ====================
1461
1462    /// 在一段异步逻辑内启用「授权快照」:期间同一账号的权限/角色只读一次。
1463    /// Enables a per-scope authorization snapshot inside an async block.
1464    pub async fn with_grant_scope<F, T>(future: F) -> T
1465    where
1466        F: Future<Output = T>,
1467    {
1468        crate::context::GrantScope::run(crate::context::GrantScope::new(), future).await
1469    }
1470
1471    /// 组合校验:权限集合 ∪ 角色集合中任一命中即通过(供 `#[sa_check_or]` 宏使用)。
1472    /// Combined check: passes when any of the permissions or roles matches.
1473    pub async fn check_permission_or_role(
1474        login_id: impl LoginId,
1475        permissions: &[&str],
1476        roles: &[&str],
1477    ) -> SaTokenResult<()> {
1478        let login_type = Self::resolve_login_type();
1479        let login_id = login_id.to_login_id();
1480        let authz = Self::try_get_manager()?.authz_service();
1481
1482        if !permissions.is_empty()
1483            && authz
1484                .has_any_permission(&login_type, &login_id, permissions)
1485                .await?
1486        {
1487            return Ok(());
1488        }
1489        if !roles.is_empty() && authz.has_any_role(&login_type, &login_id, roles).await? {
1490            return Ok(());
1491        }
1492
1493        Err(SaTokenError::PermissionDeniedDetail(format!(
1494            "none of permissions [{}] or roles [{}] matched",
1495            permissions.join(", "),
1496            roles.join(", ")
1497        )))
1498    }
1499
1500    // ==================== 链式调用 | Chain Call ====================
1501
1502    /// 创建 Token 构建器,用于链式调用 | Create token builder for chain calls
1503    ///
1504    /// # 示例 | Example
1505    /// ```rust,ignore
1506    /// use serde_json::json;
1507    ///
1508    /// // 链式调用示例
1509    /// let token = StpUtil::builder("user_123")
1510    ///     .extra_data(json!({"ip": "192.168.1.1"}))
1511    ///     .device("pc")
1512    ///     .login_type("admin")
1513    ///     .login()
1514    ///     .await?;
1515    /// ```
1516    pub fn builder(login_id: impl LoginId) -> TokenBuilder {
1517        TokenBuilder::new(login_id.to_login_id())
1518    }
1519
1520    // ---------- request sign | 请求签名 ----------
1521
1522    /// Build a signer from config (`sign_secret_key`). Errors if the secret is missing.
1523    /// 用配置中的 `sign_secret_key` 构造签名器;密钥缺失则报错。
1524    pub fn request_sign() -> SaTokenResult<crate::sign::RequestSign> {
1525        let manager = Self::try_get_manager()?;
1526        let secret = manager
1527            .config
1528            .sign_secret_key
1529            .clone()
1530            .filter(|s| !s.is_empty())
1531            .ok_or_else(|| SaTokenError::ConfigError("sign_secret_key is not configured".into()))?;
1532        Ok(
1533            crate::sign::RequestSign::new(secret, manager.config.sign_window_secs)
1534                .with_dao(manager.dao().clone()),
1535        )
1536    }
1537
1538    /// Create signed params (`timestamp` + `nonce` + `sign`).
1539    /// 创建已签名参数(`timestamp` + `nonce` + `sign`)。
1540    pub async fn sign_params(
1541        params: std::collections::BTreeMap<String, String>,
1542    ) -> SaTokenResult<std::collections::BTreeMap<String, String>> {
1543        Self::request_sign()?.create_signed(params)
1544    }
1545
1546    /// Verify request signature from the `sign` field.
1547    /// 校验请求中 `sign` 字段的签名。
1548    pub async fn check_sign(
1549        params: &std::collections::BTreeMap<String, String>,
1550    ) -> SaTokenResult<()> {
1551        let sign = params
1552            .get("sign")
1553            .cloned()
1554            .ok_or(SaTokenError::SignInvalid)?;
1555        Self::request_sign()?.verify_params(params, &sign).await
1556    }
1557
1558    // ---------- same-token(委托已有模块,避免第二套 API)----------
1559
1560    /// Get current Same-Token (create if missing).
1561    /// 获取当前 Same-Token(不存在则创建)。
1562    pub async fn get_same_token() -> SaTokenResult<String> {
1563        crate::same_token::get_token().await
1564    }
1565
1566    /// Refresh Same-Token.
1567    /// 刷新 Same-Token。
1568    pub async fn refresh_same_token() -> SaTokenResult<String> {
1569        crate::same_token::refresh_token().await
1570    }
1571
1572    /// Check a Same-Token value.
1573    /// 校验 Same-Token 值。
1574    pub async fn check_same_token(token: &str) -> SaTokenResult<()> {
1575        crate::same_token::check_token(token).await
1576    }
1577
1578    // ---------- temp token ----------
1579
1580    /// Create a short-lived temp token in the default namespace.
1581    /// 在默认命名空间创建短时临时令牌。
1582    pub async fn create_temp_token(
1583        value: impl Into<String>,
1584        timeout_secs: i64,
1585    ) -> SaTokenResult<String> {
1586        crate::temp_token::create_default(value, timeout_secs).await
1587    }
1588
1589    /// Parse a temp token from the default namespace.
1590    /// 解析默认命名空间中的临时令牌。
1591    pub async fn parse_temp_token(
1592        token: &str,
1593    ) -> SaTokenResult<crate::temp_token::TempTokenRecord> {
1594        crate::temp_token::parse_default(token).await
1595    }
1596
1597    /// Delete a temp token from the default namespace.
1598    /// 删除默认命名空间中的临时令牌。
1599    pub async fn delete_temp_token(token: &str) -> SaTokenResult<()> {
1600        crate::temp_token::delete_default(token).await
1601    }
1602}
1603
1604/// Token 构建器 - 支持链式调用 | Token Builder - Supports chain calls
1605pub struct TokenBuilder {
1606    login_id: String,
1607    extra_data: Option<serde_json::Value>,
1608    device: Option<String>,
1609    login_type: Option<String>,
1610    nonce: Option<String>,
1611    expire_time: Option<chrono::DateTime<chrono::Utc>>,
1612}
1613
1614impl std::fmt::Debug for TokenBuilder {
1615    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1616        f.write_str("TokenBuilder { .. }")
1617    }
1618}
1619
1620impl TokenBuilder {
1621    /// 创建新的 Token 构建器 | Create new token builder
1622    pub fn new(login_id: String) -> Self {
1623        Self {
1624            login_id,
1625            extra_data: None,
1626            device: None,
1627            login_type: None,
1628            nonce: None,
1629            expire_time: None,
1630        }
1631    }
1632
1633    /// 设置额外数据 | Set extra data
1634    pub fn extra_data(mut self, data: serde_json::Value) -> Self {
1635        self.extra_data = Some(data);
1636        self
1637    }
1638
1639    /// 设置设备信息 | Set device info
1640    pub fn device(mut self, device: impl Into<String>) -> Self {
1641        self.device = Some(device.into());
1642        self
1643    }
1644
1645    /// 设置登录类型 | Set login type
1646    pub fn login_type(mut self, login_type: impl Into<String>) -> Self {
1647        self.login_type = Some(login_type.into());
1648        self
1649    }
1650
1651    /// 设置 nonce(需开启 enable_nonce)
1652    /// Set nonce (requires enable_nonce)
1653    pub fn nonce(mut self, nonce: impl Into<String>) -> Self {
1654        self.nonce = Some(nonce.into());
1655        self
1656    }
1657
1658    /// 设置绝对过期时间 / Set absolute expiration
1659    pub fn expire_at(mut self, expire_time: chrono::DateTime<chrono::Utc>) -> Self {
1660        self.expire_time = Some(expire_time);
1661        self
1662    }
1663
1664    /// 设置 Unix 秒级绝对过期时间 / Set absolute expiration from Unix seconds
1665    pub fn expire_at_unix(mut self, unix_seconds: i64) -> Self {
1666        self.expire_time = chrono::DateTime::from_timestamp(unix_seconds, 0);
1667        self
1668    }
1669
1670    /// Alias of [`Self::expire_at`].
1671    /// [`Self::expire_at`] 的别名。
1672    #[deprecated(note = "use expire_at()")]
1673    pub fn expire_time(self, expire_time: chrono::DateTime<chrono::Utc>) -> Self {
1674        self.expire_at(expire_time)
1675    }
1676
1677    /// 执行登录:字段在登录前注入 LoginRequest 等价路径(login_with_options)。
1678    ///
1679    /// 如果不提供 login_id 参数,则使用构建器中的 login_id。
1680    /// 一次性带齐可选字段,由 AuthService 阶段写入保证索引/终端/映射一致。
1681    pub async fn login<T: LoginId>(self, login_id: Option<T>) -> SaTokenResult<TokenValue> {
1682        let manager = StpUtil::try_get_manager()?;
1683        let final_login_id = match login_id {
1684            Some(id) => id.to_login_id(),
1685            None => self.login_id,
1686        };
1687        manager
1688            .login_with_options(
1689                final_login_id,
1690                self.login_type,
1691                self.device,
1692                self.extra_data,
1693                self.nonce,
1694                self.expire_time,
1695            )
1696            .await
1697    }
1698}
1699
1700#[cfg(test)]
1701mod tests {
1702    use super::*;
1703
1704    #[test]
1705    fn test_token_format_validation() {
1706        assert!(StpUtil::is_valid_token_format("1234567890abcdef"));
1707        assert!(!StpUtil::is_valid_token_format(""));
1708        assert!(!StpUtil::is_valid_token_format("short"));
1709    }
1710
1711    #[test]
1712    fn test_create_token() {
1713        let token = StpUtil::create_token("test-token-123");
1714        assert_eq!(token.as_str(), "test-token-123");
1715    }
1716}