Skip to main content

sa_token_core/token/
mod.rs

1// Author: 金书记
2//
3//! Token 管理模块
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8pub mod generator;
9pub mod validator;
10pub mod jwt;
11pub mod map;
12
13pub use generator::TokenGenerator;
14pub use validator::TokenValidator;
15pub use jwt::{JwtManager, JwtClaims, JwtAlgorithm};
16
17/// Token 值
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct TokenValue(String);
20
21impl TokenValue {
22    pub fn new(value: impl Into<String>) -> Self {
23        Self(value.into())
24    }
25    
26    pub fn as_str(&self) -> &str {
27        &self.0
28    }
29}
30
31impl From<String> for TokenValue {
32    fn from(s: String) -> Self {
33        Self(s)
34    }
35}
36
37impl From<TokenValue> for String {
38    fn from(v: TokenValue) -> Self {
39        v.0
40    }
41}
42
43impl std::fmt::Display for TokenValue {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49/// Token 信息 | Token Information
50/// 
51/// 存储 Token 的完整信息,包括元数据和安全特性
52/// Stores complete token information, including metadata and security features
53/// 
54/// # 字段说明 | Field Description
55/// - `token`: Token 值 | Token value
56/// - `login_id`: 登录用户 ID | Logged-in user ID
57/// - `login_type`: 登录类型(如 "user", "admin")| Login type (e.g., "user", "admin")
58/// - `create_time`: Token 创建时间 | Token creation time
59/// - `last_active_time`: 最后活跃时间 | Last active time
60/// - `expire_time`: 过期时间(None 表示永不过期)| Expiration time (None means never expires)
61/// - `device`: 设备标识 | Device identifier
62/// - `extra_data`: 额外数据 | Extra data
63/// - `nonce`: 防重放攻击的一次性令牌 | One-time token for replay attack prevention
64/// - `refresh_token`: 用于刷新的长期令牌 | Long-term token for refresh
65/// - `refresh_token_expire_time`: Refresh Token 过期时间 | Refresh token expiration time
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TokenInfo {
68    /// Token 值 | Token value
69    pub token: TokenValue,
70    
71    /// 登录 ID | Login ID
72    pub login_id: String,
73    
74    /// 登录类型(user、admin 等)| Login type (user, admin, etc.)
75    pub login_type: String,
76    
77    /// Token 创建时间 | Token creation time
78    pub create_time: DateTime<Utc>,
79    
80    /// Token 最后活跃时间 | Token last active time
81    pub last_active_time: DateTime<Utc>,
82    
83    /// Token 过期时间(None 表示永不过期)| Token expiration time (None means never expires)
84    pub expire_time: Option<DateTime<Utc>>,
85    
86    /// 设备标识 | Device identifier
87    pub device: Option<String>,
88    
89    /// 额外数据 | Extra data
90    pub extra_data: Option<serde_json::Value>,
91    
92    /// Nonce(用于防重放攻击)| Nonce (for replay attack prevention)
93    pub nonce: Option<String>,
94    
95    /// Refresh Token(用于刷新访问令牌)| Refresh Token (for refreshing access token)
96    pub refresh_token: Option<String>,
97    
98    /// Refresh Token 过期时间 | Refresh Token expiration time
99    pub refresh_token_expire_time: Option<DateTime<Utc>>,
100}
101
102impl TokenInfo {
103    pub fn new(token: TokenValue, login_id: impl Into<String>) -> Self {
104        let now = Utc::now();
105        Self {
106            token,
107            login_id: login_id.into(),
108            login_type: "default".to_string(),
109            create_time: now,
110            last_active_time: now,
111            expire_time: None,
112            device: None,
113            extra_data: None,
114            nonce: None,
115            refresh_token: None,
116            refresh_token_expire_time: None,
117        }
118    }
119    
120    pub fn is_expired(&self) -> bool {
121        if let Some(expire_time) = self.expire_time {
122            Utc::now() > expire_time
123        } else {
124            false
125        }
126    }
127    
128    pub fn update_active_time(&mut self) {
129        self.last_active_time = Utc::now();
130    }
131
132    /// 是否因长时间未活跃而冻结(对齐 Java StpLogic.isFreeze)
133    ///
134    /// `active_timeout <= 0` 表示永不冻结;否则比较当前时间与 `last_active_time` 的间隔是否超过阈值。
135    pub fn is_freeze(&self, active_timeout: i64) -> bool {
136        if active_timeout <= 0 {
137            return false;
138        }
139        Utc::now()
140            .signed_duration_since(self.last_active_time)
141            .num_seconds()
142            > active_timeout
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_is_freeze_respects_active_timeout() {
152        let mut info = TokenInfo::new(TokenValue::new("t"), "u");
153        info.last_active_time = Utc::now() - chrono::Duration::seconds(120);
154        assert!(info.is_freeze(60));
155        assert!(!info.is_freeze(-1));
156        assert!(!info.is_freeze(0));
157    }
158}
159
160/// Token 签名
161#[derive(Debug, Clone)]
162pub struct TokenSign {
163    pub value: String,
164    pub device: Option<String>,
165}