clamber_core/token/
mod.rs

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