sa_token_core/
util.rs

1// Author: 金书记
2//
3//! StpUtil - sa-token 便捷工具类
4//! 
5//! 提供类似 Java 版 StpUtil 的静态方法,方便进行认证和权限操作
6//! 
7//! ## 使用示例
8//! 
9//! ```rust,ignore
10//! use sa_token_core::StpUtil;
11//! 
12//! // 初始化全局 Manager(应用启动时调用一次)
13//! StpUtil::init_manager(manager);
14//! 
15//! // 之后就可以直接使用,支持多种 ID 类型
16//! let token = StpUtil::login("user_123").await?;  // 字符串 ID
17//! let token = StpUtil::login(10001).await?;       // 数字 ID (i32)
18//! let token = StpUtil::login(10001_i64).await?;   // 数字 ID (i64)
19//! 
20//! StpUtil::set_permissions(10001, vec!["user:list".to_string()]).await?;
21//! ```
22
23use std::sync::Arc;
24use std::fmt::Display;
25use once_cell::sync::OnceCell;
26use crate::{SaTokenManager, SaTokenResult, SaTokenError};
27use crate::token::{TokenValue, TokenInfo};
28use crate::session::SaSession;
29use crate::context::SaTokenContext;
30use crate::event::{SaTokenEventBus, SaTokenListener};
31
32/// 全局 SaTokenManager 实例
33static GLOBAL_MANAGER: OnceCell<Arc<SaTokenManager>> = OnceCell::new();
34
35/// LoginId trait - 支持任何可以转换为字符串的类型作为登录 ID
36/// 
37/// 自动实现了 String, &str, i32, i64, u32, u64 等常用类型
38pub trait LoginId {
39    fn to_login_id(&self) -> String;
40}
41
42// 为所有实现了 Display 的类型自动实现 LoginId
43impl<T: Display> LoginId for T {
44    fn to_login_id(&self) -> String {
45        self.to_string()
46    }
47}
48
49/// StpUtil - 权限认证工具类
50/// 
51/// 提供便捷的认证和授权操作方法,类似于 Java 版 sa-token 的 StpUtil
52pub struct StpUtil;
53
54impl StpUtil {
55    // ==================== 初始化 ====================
56    
57    /// 初始化全局 SaTokenManager(应用启动时调用一次)
58    /// 
59    /// # 示例
60    /// ```rust,ignore
61    /// let manager = SaTokenConfig::builder()
62    ///     .storage(Arc::new(MemoryStorage::new()))
63    ///     .build();
64    /// StpUtil::init_manager(manager);
65    /// ```
66    pub fn init_manager(manager: SaTokenManager) {
67        GLOBAL_MANAGER.set(Arc::new(manager))
68            .unwrap_or_else(|_| panic!("StpUtil manager already initialized"));
69    }
70    
71    /// 获取全局 Manager
72    fn get_manager() -> &'static Arc<SaTokenManager> {
73        GLOBAL_MANAGER.get()
74            .expect("StpUtil not initialized. Call StpUtil::init_manager() first.")
75    }
76    
77    /// 获取事件总线,用于注册监听器
78    /// 
79    /// # 示例
80    /// ```rust,ignore
81    /// use sa_token_core::{StpUtil, SaTokenListener};
82    /// use async_trait::async_trait;
83    /// 
84    /// struct MyListener;
85    /// 
86    /// #[async_trait]
87    /// impl SaTokenListener for MyListener {
88    ///     async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
89    ///         println!("用户 {} 登录了", login_id);
90    ///     }
91    /// }
92    /// 
93    /// // 注册监听器
94    /// StpUtil::event_bus().register(Arc::new(MyListener)).await;
95    /// ```
96    pub fn event_bus() -> &'static SaTokenEventBus {
97        &Self::get_manager().event_bus
98    }
99    
100    /// 注册事件监听器(便捷方法)
101    /// 
102    /// # 示例
103    /// ```rust,ignore
104    /// StpUtil::register_listener(Arc::new(MyListener)).await;
105    /// ```
106    pub fn register_listener(listener: Arc<dyn SaTokenListener>) {
107        Self::event_bus().register(listener);
108    }
109    
110    // ==================== 登录相关 ====================
111    
112    /// 会话登录
113    /// 
114    /// # 示例
115    /// ```rust,ignore
116    /// // 支持字符串 ID
117    /// let token = StpUtil::login("user_123").await?;
118    /// 
119    /// // 支持数字 ID
120    /// let token = StpUtil::login(10001).await?;
121    /// let token = StpUtil::login(10001_i64).await?;
122    /// ```
123    pub async fn login(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
124        Self::get_manager().login(login_id.to_login_id()).await
125    }
126    
127    /// 会话登录(带 manager 参数的版本,向后兼容)
128    pub async fn login_with_manager(
129        manager: &SaTokenManager,
130        login_id: impl Into<String>,
131    ) -> SaTokenResult<TokenValue> {
132        manager.login(login_id).await
133    }
134    
135    /// 会话登出
136    pub async fn logout(token: &TokenValue) -> SaTokenResult<()> {
137        Self::get_manager().logout(token).await
138    }
139    
140    pub async fn logout_with_manager(
141        manager: &SaTokenManager,
142        token: &TokenValue,
143    ) -> SaTokenResult<()> {
144        manager.logout(token).await
145    }
146    
147    /// 踢人下线(根据登录ID)
148    pub async fn kick_out(login_id: impl LoginId) -> SaTokenResult<()> {
149        Self::get_manager().kick_out(&login_id.to_login_id()).await
150    }
151    
152    pub async fn kick_out_with_manager(
153        manager: &SaTokenManager,
154        login_id: &str,
155    ) -> SaTokenResult<()> {
156        manager.kick_out(login_id).await
157    }
158    
159    /// 强制登出(根据登录ID)
160    pub async fn logout_by_login_id(login_id: impl LoginId) -> SaTokenResult<()> {
161        Self::get_manager().logout_by_login_id(&login_id.to_login_id()).await
162    }
163    
164    /// 根据 token 登出(别名方法,更直观)
165    pub async fn logout_by_token(token: &TokenValue) -> SaTokenResult<()> {
166        Self::logout(token).await
167    }
168    
169    // ==================== 当前会话操作(无参数,从上下文获取)====================
170    
171    /// 获取当前请求的 token(无参数,从上下文获取)
172    /// 
173    /// # 示例
174    /// ```rust,ignore
175    /// // 在请求处理函数中
176    /// let token = StpUtil::get_token_value()?;
177    /// ```
178    pub fn get_token_value() -> SaTokenResult<TokenValue> {
179        let ctx = SaTokenContext::get_current()
180            .ok_or(SaTokenError::NotLogin)?;
181        ctx.token.ok_or(SaTokenError::NotLogin)
182    }
183    
184    /// 当前会话登出(无参数,从上下文获取 token)
185    /// 
186    /// # 示例
187    /// ```rust,ignore
188    /// // 在请求处理函数中
189    /// StpUtil::logout_current().await?;
190    /// ```
191    pub async fn logout_current() -> SaTokenResult<()> {
192        let token = Self::get_token_value()?;
193        Self::logout(&token).await
194    }
195    
196    /// 检查当前会话是否登录(无参数,返回 bool)
197    /// 
198    /// # 示例
199    /// ```rust,ignore
200    /// // 在请求处理函数中
201    /// if StpUtil::is_login_current() {
202    ///     println!("当前用户已登录");
203    /// }
204    /// ```
205    pub fn is_login_current() -> bool {
206        if let Ok(_token) = Self::get_token_value() {
207            // 注意:这里使用同步检查,只检查上下文中是否有 token
208            // 如果需要异步验证,需要使用 is_login(&token).await
209            true
210        } else {
211            false
212        }
213    }
214    
215    /// 检查当前会话登录状态,未登录则抛出异常(无参数)
216    /// 
217    /// # 示例
218    /// ```rust,ignore
219    /// // 在请求处理函数中
220    /// StpUtil::check_login_current()?;
221    /// ```
222    pub fn check_login_current() -> SaTokenResult<()> {
223        Self::get_token_value()?;
224        Ok(())
225    }
226    
227    /// 获取当前会话的 login_id(String 类型,无参数)
228    /// 
229    /// # 示例
230    /// ```rust,ignore
231    /// // 在请求处理函数中
232    /// let login_id = StpUtil::get_login_id_as_string()?;
233    /// ```
234    pub fn get_login_id_as_string() -> SaTokenResult<String> {
235        let ctx = SaTokenContext::get_current()
236            .ok_or(SaTokenError::NotLogin)?;
237        ctx.login_id.ok_or(SaTokenError::NotLogin)
238    }
239    
240    /// 获取当前会话的 login_id(i64 类型,无参数)
241    /// 
242    /// # 示例
243    /// ```rust,ignore
244    /// // 在请求处理函数中
245    /// let user_id = StpUtil::get_login_id_as_long()?;
246    /// ```
247    pub fn get_login_id_as_long() -> SaTokenResult<i64> {
248        let login_id_str = Self::get_login_id_as_string()?;
249        login_id_str.parse::<i64>()
250            .map_err(|_| SaTokenError::LoginIdNotNumber)
251    }
252    
253    /// 获取当前会话的 token 信息(无参数)
254    /// 
255    /// # 示例
256    /// ```rust,ignore
257    /// // 在请求处理函数中
258    /// let token_info = StpUtil::get_token_info_current()?;
259    /// println!("Token 创建时间: {:?}", token_info.create_time);
260    /// ```
261    pub fn get_token_info_current() -> SaTokenResult<Arc<TokenInfo>> {
262        let ctx = SaTokenContext::get_current()
263            .ok_or(SaTokenError::NotLogin)?;
264        ctx.token_info.ok_or(SaTokenError::NotLogin)
265    }
266    
267    // ==================== Token 验证 ====================
268    
269    /// 检查当前 token 是否已登录
270    pub async fn is_login(token: &TokenValue) -> bool {
271        Self::get_manager().is_valid(token).await
272    }
273    
274    /// 根据登录 ID 检查是否已登录
275    /// 
276    /// # 示例
277    /// ```rust,ignore
278    /// let is_logged_in = StpUtil::is_login_by_login_id("user_123").await;
279    /// let is_logged_in = StpUtil::is_login_by_login_id(10001).await;
280    /// ```
281    pub async fn is_login_by_login_id(login_id: impl LoginId) -> bool {
282        match Self::get_token_by_login_id(login_id).await {
283            Ok(token) => Self::is_login(&token).await,
284            Err(_) => false,
285        }
286    }
287    
288    pub async fn is_login_with_manager(
289        manager: &SaTokenManager,
290        token: &TokenValue,
291    ) -> bool {
292        manager.is_valid(token).await
293    }
294    
295    /// 检查当前 token 是否已登录,如果未登录则抛出异常
296    pub async fn check_login(token: &TokenValue) -> SaTokenResult<()> {
297        if !Self::is_login(token).await {
298            return Err(SaTokenError::NotLogin);
299        }
300        Ok(())
301    }
302    
303    /// 获取 token 信息
304    pub async fn get_token_info(token: &TokenValue) -> SaTokenResult<TokenInfo> {
305        Self::get_manager().get_token_info(token).await
306    }
307    
308    /// 获取当前 token 的登录ID
309    pub async fn get_login_id(token: &TokenValue) -> SaTokenResult<String> {
310        let token_info = Self::get_manager().get_token_info(token).await?;
311        Ok(token_info.login_id)
312    }
313    
314    /// 获取当前 token 的登录ID,如果未登录则返回默认值
315    pub async fn get_login_id_or_default(
316        token: &TokenValue,
317        default: impl Into<String>,
318    ) -> String {
319        Self::get_login_id(token)
320            .await
321            .unwrap_or_else(|_| default.into())
322    }
323    
324    /// 根据登录 ID 获取当前用户的 token
325    /// 
326    /// # 示例
327    /// ```rust,ignore
328    /// let token = StpUtil::get_token_by_login_id("user_123").await?;
329    /// let token = StpUtil::get_token_by_login_id(10001).await?;
330    /// ```
331    pub async fn get_token_by_login_id(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
332        let manager = Self::get_manager();
333        let login_id_str = login_id.to_login_id();
334        
335        // 从存储中获取该用户的 token
336        let key = format!("sa:login:token:{}", login_id_str);
337        match manager.storage.get(&key).await {
338            Ok(Some(token_str)) => Ok(TokenValue::new(token_str)),
339            Ok(None) => Err(SaTokenError::NotLogin),
340            Err(e) => Err(SaTokenError::StorageError(e.to_string())),
341        }
342    }
343    
344    /// 根据登录 ID 获取所有在线的 token 列表(支持多设备登录)
345    /// 
346    /// # 示例
347    /// ```rust,ignore
348    /// let tokens = StpUtil::get_all_tokens_by_login_id("user_123").await?;
349    /// ```
350    pub async fn get_all_tokens_by_login_id(login_id: impl LoginId) -> SaTokenResult<Vec<TokenValue>> {
351        let manager = Self::get_manager();
352        let login_id_str = login_id.to_login_id();
353        
354        // 从存储中获取该用户的所有 token
355        let key = format!("sa:login:tokens:{}", login_id_str);
356        match manager.storage.get(&key).await {
357            Ok(Some(tokens_str)) => {
358                let token_strings: Vec<String> = serde_json::from_str(&tokens_str)
359                    .map_err(|e| SaTokenError::SerializationError(e))?;
360                Ok(token_strings.into_iter().map(TokenValue::new).collect())
361            }
362            Ok(None) => Ok(Vec::new()),
363            Err(e) => Err(SaTokenError::StorageError(e.to_string())),
364        }
365    }
366    
367    // ==================== Session 会话 ====================
368    
369    /// 获取当前登录账号的 Session
370    pub async fn get_session(login_id: impl LoginId) -> SaTokenResult<SaSession> {
371        Self::get_manager().get_session(&login_id.to_login_id()).await
372    }
373    
374    /// 保存 Session
375    pub async fn save_session(session: &SaSession) -> SaTokenResult<()> {
376        Self::get_manager().save_session(session).await
377    }
378    
379    /// 删除 Session
380    pub async fn delete_session(login_id: impl LoginId) -> SaTokenResult<()> {
381        Self::get_manager().delete_session(&login_id.to_login_id()).await
382    }
383    
384    /// 在 Session 中设置值
385    pub async fn set_session_value<T: serde::Serialize>(
386        login_id: impl LoginId,
387        key: &str,
388        value: T,
389    ) -> SaTokenResult<()> {
390        let manager = Self::get_manager();
391        let login_id_str = login_id.to_login_id();
392        let mut session = manager.get_session(&login_id_str).await?;
393        session.set(key, value)?;
394        manager.save_session(&session).await
395    }
396    
397    /// 从 Session 中获取值
398    pub async fn get_session_value<T: serde::de::DeserializeOwned>(
399        login_id: impl LoginId,
400        key: &str,
401    ) -> SaTokenResult<Option<T>> {
402        let session = Self::get_manager().get_session(&login_id.to_login_id()).await?;
403        Ok(session.get::<T>(key))
404    }
405    
406    // ==================== Token 相关 ====================
407    
408    /// 创建一个新的 token(但不登录)
409    pub fn create_token(token_value: impl Into<String>) -> TokenValue {
410        TokenValue::new(token_value.into())
411    }
412    
413    /// 检查 token 格式是否有效(仅检查格式,不检查是否存在于存储中)
414    pub fn is_valid_token_format(token: &str) -> bool {
415        !token.is_empty() && token.len() >= 16
416    }
417}
418
419// ==================== 权限管理 ====================
420
421impl StpUtil {
422    /// 为用户添加权限
423    pub async fn set_permissions(
424        login_id: impl LoginId,
425        permissions: Vec<String>,
426    ) -> SaTokenResult<()> {
427        let manager = Self::get_manager();
428        let mut map = manager.user_permissions.write().await;
429        map.insert(login_id.to_login_id(), permissions);
430        Ok(())
431    }
432    
433    /// 为用户添加单个权限
434    pub async fn add_permission(
435        login_id: impl LoginId,
436        permission: impl Into<String>,
437    ) -> SaTokenResult<()> {
438        let manager = Self::get_manager();
439        let mut map = manager.user_permissions.write().await;
440        let login_id_str = login_id.to_login_id();
441        let permissions = map.entry(login_id_str).or_insert_with(Vec::new);
442        let perm = permission.into();
443        if !permissions.contains(&perm) {
444            permissions.push(perm);
445        }
446        Ok(())
447    }
448    
449    /// 移除用户的某个权限
450    pub async fn remove_permission(
451        login_id: impl LoginId,
452        permission: &str,
453    ) -> SaTokenResult<()> {
454        let manager = Self::get_manager();
455        let mut map = manager.user_permissions.write().await;
456        if let Some(permissions) = map.get_mut(&login_id.to_login_id()) {
457            permissions.retain(|p| p != permission);
458        }
459        Ok(())
460    }
461    
462    /// 清除用户的所有权限
463    pub async fn clear_permissions(login_id: impl LoginId) -> SaTokenResult<()> {
464        let manager = Self::get_manager();
465        let mut map = manager.user_permissions.write().await;
466        map.remove(&login_id.to_login_id());
467        Ok(())
468    }
469    
470    /// 获取用户的所有权限
471    pub async fn get_permissions(login_id: impl LoginId) -> Vec<String> {
472        let manager = Self::get_manager();
473        let map = manager.user_permissions.read().await;
474        map.get(&login_id.to_login_id()).cloned().unwrap_or_default()
475    }
476    
477    /// 检查用户是否拥有指定权限
478    pub async fn has_permission(
479        login_id: impl LoginId,
480        permission: &str,
481    ) -> bool {
482        let manager = Self::get_manager();
483        let map = manager.user_permissions.read().await;
484        if let Some(permissions) = map.get(&login_id.to_login_id()) {
485            // 精确匹配
486            if permissions.contains(&permission.to_string()) {
487                return true;
488            }
489            
490            // 通配符匹配(例如 admin:* 匹配 admin:read)
491            for perm in permissions {
492                if perm.ends_with(":*") {
493                    let prefix = &perm[..perm.len() - 2];
494                    if permission.starts_with(prefix) {
495                        return true;
496                    }
497                }
498            }
499        }
500        false
501    }
502    
503    /// 检查用户是否拥有所有指定权限(AND 逻辑)
504    pub async fn has_all_permissions(
505        login_id: impl LoginId,
506        permissions: &[&str],
507    ) -> bool {
508        let login_id_str = login_id.to_login_id();
509        for permission in permissions {
510            if !Self::has_permission(&login_id_str, permission).await {
511                return false;
512            }
513        }
514        true
515    }
516    
517    /// 检查用户是否拥有所有指定权限(别名,AND 逻辑)
518    pub async fn has_permissions_and(
519        login_id: impl LoginId,
520        permissions: &[&str],
521    ) -> bool {
522        Self::has_all_permissions(login_id, permissions).await
523    }
524    
525    /// 检查用户是否拥有任一指定权限(OR 逻辑)
526    pub async fn has_any_permission(
527        login_id: impl LoginId,
528        permissions: &[&str],
529    ) -> bool {
530        let login_id_str = login_id.to_login_id();
531        for permission in permissions {
532            if Self::has_permission(&login_id_str, permission).await {
533                return true;
534            }
535        }
536        false
537    }
538    
539    /// 检查用户是否拥有任一指定权限(别名,OR 逻辑)
540    pub async fn has_permissions_or(
541        login_id: impl LoginId,
542        permissions: &[&str],
543    ) -> bool {
544        Self::has_any_permission(login_id, permissions).await
545    }
546    
547    /// 检查权限,如果没有则抛出异常
548    pub async fn check_permission(
549        login_id: impl LoginId,
550        permission: &str,
551    ) -> SaTokenResult<()> {
552        if !Self::has_permission(login_id, permission).await {
553            return Err(SaTokenError::PermissionDeniedDetail(permission.to_string()));
554        }
555        Ok(())
556    }
557}
558
559// ==================== 角色管理 ====================
560
561impl StpUtil {
562    /// 为用户设置角色
563    pub async fn set_roles(
564        login_id: impl LoginId,
565        roles: Vec<String>,
566    ) -> SaTokenResult<()> {
567        let manager = Self::get_manager();
568        let mut map = manager.user_roles.write().await;
569        map.insert(login_id.to_login_id(), roles);
570        Ok(())
571    }
572    
573    /// 为用户添加单个角色
574    pub async fn add_role(
575        login_id: impl LoginId,
576        role: impl Into<String>,
577    ) -> SaTokenResult<()> {
578        let manager = Self::get_manager();
579        let mut map = manager.user_roles.write().await;
580        let login_id_str = login_id.to_login_id();
581        let roles = map.entry(login_id_str).or_insert_with(Vec::new);
582        let r = role.into();
583        if !roles.contains(&r) {
584            roles.push(r);
585        }
586        Ok(())
587    }
588    
589    /// 移除用户的某个角色
590    pub async fn remove_role(
591        login_id: impl LoginId,
592        role: &str,
593    ) -> SaTokenResult<()> {
594        let manager = Self::get_manager();
595        let mut map = manager.user_roles.write().await;
596        if let Some(roles) = map.get_mut(&login_id.to_login_id()) {
597            roles.retain(|r| r != role);
598        }
599        Ok(())
600    }
601    
602    /// 清除用户的所有角色
603    pub async fn clear_roles(login_id: impl LoginId) -> SaTokenResult<()> {
604        let manager = Self::get_manager();
605        let mut map = manager.user_roles.write().await;
606        map.remove(&login_id.to_login_id());
607        Ok(())
608    }
609    
610    /// 获取用户的所有角色
611    pub async fn get_roles(login_id: impl LoginId) -> Vec<String> {
612        let manager = Self::get_manager();
613        let map = manager.user_roles.read().await;
614        map.get(&login_id.to_login_id()).cloned().unwrap_or_default()
615    }
616    
617    /// 检查用户是否拥有指定角色
618    pub async fn has_role(
619        login_id: impl LoginId,
620        role: &str,
621    ) -> bool {
622        let manager = Self::get_manager();
623        let map = manager.user_roles.read().await;
624        if let Some(roles) = map.get(&login_id.to_login_id()) {
625            roles.contains(&role.to_string())
626        } else {
627            false
628        }
629    }
630    
631    /// 检查用户是否拥有所有指定角色(AND 逻辑)
632    pub async fn has_all_roles(
633        login_id: impl LoginId,
634        roles: &[&str],
635    ) -> bool {
636        let login_id_str = login_id.to_login_id();
637        for role in roles {
638            if !Self::has_role(&login_id_str, role).await {
639                return false;
640            }
641        }
642        true
643    }
644    
645    /// 检查用户是否拥有所有指定角色(别名,AND 逻辑)
646    pub async fn has_roles_and(
647        login_id: impl LoginId,
648        roles: &[&str],
649    ) -> bool {
650        Self::has_all_roles(login_id, roles).await
651    }
652    
653    /// 检查用户是否拥有任一指定角色(OR 逻辑)
654    pub async fn has_any_role(
655        login_id: impl LoginId,
656        roles: &[&str],
657    ) -> bool {
658        let login_id_str = login_id.to_login_id();
659        for role in roles {
660            if Self::has_role(&login_id_str, role).await {
661                return true;
662            }
663        }
664        false
665    }
666    
667    /// 检查用户是否拥有任一指定角色(别名,OR 逻辑)
668    pub async fn has_roles_or(
669        login_id: impl LoginId,
670        roles: &[&str],
671    ) -> bool {
672        Self::has_any_role(login_id, roles).await
673    }
674    
675    /// 检查角色,如果没有则抛出异常
676    pub async fn check_role(
677        login_id: impl LoginId,
678        role: &str,
679    ) -> SaTokenResult<()> {
680        if !Self::has_role(login_id, role).await {
681            return Err(SaTokenError::RoleDenied(role.to_string()));
682        }
683        Ok(())
684    }
685}
686
687// ==================== 扩展工具方法 ====================
688
689impl StpUtil {
690    /// 批量踢人下线
691    pub async fn kick_out_batch<T: LoginId>(
692        login_ids: &[T],
693    ) -> SaTokenResult<Vec<Result<(), SaTokenError>>> {
694        let manager = Self::get_manager();
695        let mut results = Vec::new();
696        for login_id in login_ids {
697            results.push(manager.kick_out(&login_id.to_login_id()).await);
698        }
699        Ok(results)
700    }
701    
702    /// 获取 token 剩余有效时间(秒)
703    pub async fn get_token_timeout(token: &TokenValue) -> SaTokenResult<Option<i64>> {
704        let manager = Self::get_manager();
705        let token_info = manager.get_token_info(token).await?;
706        
707        if let Some(expire_time) = token_info.expire_time {
708            let now = chrono::Utc::now();
709            let duration = expire_time.signed_duration_since(now);
710            Ok(Some(duration.num_seconds()))
711        } else {
712            Ok(None) // 永久有效
713        }
714    }
715    
716    /// 续期 token(重置过期时间)
717    pub async fn renew_timeout(
718        token: &TokenValue,
719        timeout_seconds: i64,
720    ) -> SaTokenResult<()> {
721        let manager = Self::get_manager();
722        let mut token_info = manager.get_token_info(token).await?;
723        
724        // 设置新的过期时间
725        let new_expire_time = chrono::Utc::now() + chrono::Duration::seconds(timeout_seconds);
726        token_info.expire_time = Some(new_expire_time);
727        
728        // 保存更新后的 token 信息
729        let key = format!("sa:token:{}", token.as_str());
730        let value = serde_json::to_string(&token_info)
731            .map_err(|e| SaTokenError::SerializationError(e))?;
732        
733        let timeout = std::time::Duration::from_secs(timeout_seconds as u64);
734        manager.storage.set(&key, &value, Some(timeout)).await
735            .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
736        
737        Ok(())
738    }
739}
740
741#[cfg(test)]
742mod tests {
743    use super::*;
744    
745    #[test]
746    fn test_token_format_validation() {
747        assert!(StpUtil::is_valid_token_format("1234567890abcdef"));
748        assert!(!StpUtil::is_valid_token_format(""));
749        assert!(!StpUtil::is_valid_token_format("short"));
750    }
751    
752    #[test]
753    fn test_create_token() {
754        let token = StpUtil::create_token("test-token-123");
755        assert_eq!(token.as_str(), "test-token-123");
756    }
757}