clamber_core/token/
mod.rs

1//! JWT 模块:提供令牌生成、验证和便捷函数;支持自定义密钥与过期时间。
2//! 详见根目录 JWT_README.md 获取更丰富的示例与 API 说明。
3use crate::error::{ClamberError, Result};
4use chrono::{Duration, Utc};
5use hmac::{Hmac, Mac};
6use jwt::{SignWithKey, VerifyWithKey};
7use serde::{Serialize, de::DeserializeOwned};
8use sha2::Sha256;
9use std::collections::BTreeMap;
10
11
12const DEFAULT_JWT_SECRET: &'static str = "secret";
13
14/// JWT配置结构
15#[derive(Debug, Clone)]
16pub struct JwtConfig {
17    /// JWT密钥
18    pub secret: String,
19    /// 过期时间(天数)
20    pub expire_days: i64,
21}
22
23impl Default for JwtConfig {
24    fn default() -> Self {
25        Self {
26            secret: DEFAULT_JWT_SECRET.to_string(),
27            expire_days: 7,
28        }
29    }
30}
31
32impl JwtConfig {
33    /// 创建新的JWT配置
34    pub fn new(secret: impl Into<String>, expire_days: i64) -> Self {
35        Self {
36            secret: secret.into(),
37            expire_days,
38        }
39    }
40
41    /// 根据secret创建配置
42    pub fn with_secret(secret: impl Into<String>) -> Self {
43        Self {
44            secret: secret.into(),
45            expire_days: 7
46        }
47    }
48}
49
50/// JWT管理器
51pub struct JwtManager {
52    config: JwtConfig,
53}
54
55impl JwtManager {
56    /// 创建新的JWT管理器
57    pub fn new(config: JwtConfig) -> Self {
58        Self { config }
59    }
60
61    /// 使用默认配置创建JWT管理器
62    pub fn default() -> Self {
63        Self {
64            config: JwtConfig::default(),
65        }
66    }
67
68    /// 生成JWT token
69    pub fn generate_token<T>(&self, payload: &T) -> Result<String>
70    where
71        T: Serialize,
72    {
73        let expire_time = Utc::now() + Duration::days(self.config.expire_days);
74
75        // 将payload序列化为JSON字符串
76        let payload_json = serde_json::to_string(payload)?;
77
78        let mut claims = BTreeMap::new();
79        claims.insert("payload".to_string(), payload_json);
80        claims.insert("exp".to_string(), expire_time.timestamp().to_string());
81        claims.insert("createAt".to_string(), Utc::now().timestamp().to_string());
82
83        let key: Hmac<Sha256> =
84            Hmac::new_from_slice(self.config.secret.as_bytes()).map_err(|e| {
85                ClamberError::JwtKeyError {
86                    details: e.to_string(),
87                }
88            })?;
89
90        claims
91            .sign_with_key(&key)
92            .map_err(|e| ClamberError::JwtSignError {
93                details: e.to_string(),
94            })
95    }
96
97    /// 验证并解析JWT token
98    pub fn verify_token<T>(&self, token: &str) -> Result<T>
99    where
100        T: DeserializeOwned,
101    {
102        let key: Hmac<Sha256> =
103            Hmac::new_from_slice(self.config.secret.as_bytes()).map_err(|e| {
104                ClamberError::JwtKeyError {
105                    details: e.to_string(),
106                }
107            })?;
108
109        let claims: BTreeMap<String, String> =
110            token
111                .verify_with_key(&key)
112                .map_err(|e| ClamberError::JwtVerifyError {
113                    details: e.to_string(),
114                })?;
115
116        // 检查过期时间
117        if let Some(exp_str) = claims.get("exp") {
118            let exp_timestamp = exp_str.parse::<i64>().map_err(|_| ClamberError::JwtError {
119                message: "无效的过期时间格式".to_string(),
120            })?;
121
122            if exp_timestamp <= Utc::now().timestamp() {
123                return Err(ClamberError::JwtExpiredError);
124            }
125        } else {
126            return Err(ClamberError::JwtMissingFieldError {
127                field: "exp".to_string(),
128            });
129        }
130
131        // 获取payload并反序列化
132        if let Some(payload_str) = claims.get("payload") {
133            serde_json::from_str::<T>(payload_str).map_err(|e| ClamberError::DeserializationError {
134                details: e.to_string(),
135            })
136        } else {
137            Err(ClamberError::JwtMissingFieldError {
138                field: "payload".to_string(),
139            })
140        }
141    }
142
143    /// 检查token是否有效(不解析payload)
144    pub fn is_valid_token(&self, token: &str) -> bool {
145        let key = match Hmac::<Sha256>::new_from_slice(self.config.secret.as_bytes()) {
146            Ok(key) => key,
147            Err(_) => return false,
148        };
149
150        if let Ok(claims) = token.verify_with_key(&key) {
151            let claims: BTreeMap<String, String> = claims;
152            if let Some(exp_str) = claims.get("exp") {
153                if let Ok(exp_timestamp) = exp_str.parse::<i64>() {
154                    return exp_timestamp > Utc::now().timestamp();
155                }
156            }
157        }
158        false
159    }
160}
161
162// 便利函数:使用默认配置
163pub fn generate_token<T>(payload: &T, config: JwtConfig) -> Result<String>
164where
165    T: Serialize,
166{
167    let manager = JwtManager::new(config);
168    manager.generate_token(payload)
169}
170
171pub fn verify_token<T>(token: &str) -> Result<T>
172where
173    T: DeserializeOwned,
174{
175    let manager = JwtManager::default();
176    manager.verify_token(token)
177}
178
179pub fn is_valid_token(token: &str) -> bool {
180    let manager = JwtManager::default();
181    manager.is_valid_token(token)
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use serde::{Deserialize, Serialize};
188
189    #[derive(Debug, Serialize, Deserialize, PartialEq)]
190    struct TestUser {
191        pub id: String,
192        pub name: String,
193        pub role: String,
194    }
195
196    #[test]
197    fn test_jwt_generate_and_verify() {
198        let config = JwtConfig::new("test_secret", 1);
199        let manager = JwtManager::new(config);
200
201        let user = TestUser {
202            id: "123".to_string(),
203            name: "John Doe".to_string(),
204            role: "admin".to_string(),
205        };
206
207        // 生成token
208        let token = manager.generate_token(&user).unwrap();
209        assert!(!token.is_empty());
210
211        // 验证token
212        let decoded_user: TestUser = manager.verify_token(&token).unwrap();
213        assert_eq!(user, decoded_user);
214
215        // 检查token有效性
216        assert!(manager.is_valid_token(&token));
217    }
218
219    #[test]
220    fn test_convenience_functions() {
221        let user = TestUser {
222            id: "456".to_string(),
223            name: "Jane Doe".to_string(),
224            role: "user".to_string(),
225        };
226
227        let token = generate_token(&user, JwtConfig::default()).unwrap();
228        let decoded_user: TestUser = verify_token(&token).unwrap();
229
230        assert_eq!(user, decoded_user);
231        assert!(is_valid_token(&token));
232    }
233
234    #[test]
235    fn test_invalid_token() {
236        let manager = JwtManager::default();
237
238        // 测试无效token
239        assert!(!manager.is_valid_token("invalid_token"));
240
241        // 测试错误密钥
242        let config1 = JwtConfig::new("secret1", 1);
243        let config2 = JwtConfig::new("secret2", 1);
244        let manager1 = JwtManager::new(config1);
245        let manager2 = JwtManager::new(config2);
246
247        let user = TestUser {
248            id: "789".to_string(),
249            name: "Test User".to_string(),
250            role: "test".to_string(),
251        };
252
253        let token = manager1.generate_token(&user).unwrap();
254        assert!(manager2.verify_token::<TestUser>(&token).is_err());
255    }
256}