Skip to main content

sa_token_core/token/
jwt.rs

1// Author: 金书记
2//
3//! JWT (JSON Web Token) Module | JWT (JSON Web Token) 模块
4//!
5//! Provides complete JWT functionality including generation, validation, and parsing.
6//! 提供完整的 JWT 功能,包括生成、验证和解析。
7//!
8//! ## Features | 功能特性
9//!
10//! - Multiple algorithms support (HS256, HS384, HS512, RS256, etc.)
11//!   支持多种算法(HS256, HS384, HS512, RS256 等)
12//! - Custom claims support | 支持自定义声明
13//! - Expiration time validation | 过期时间验证
14//! - Token refresh | Token 刷新
15//!
16//! ## Usage Example | 使用示例
17//!
18//! ```rust,ignore
19//! use sa_token_core::token::jwt::{JwtManager, JwtClaims};
20//!
21//! // Create JWT manager | 创建 JWT 管理器
22//! let jwt_manager = JwtManager::new("your-secret-key");
23//!
24//! // Generate JWT token | 生成 JWT token
25//! let mut claims = JwtClaims::new("user_123");
26//! claims.set_expiration(3600); // 1 hour | 1小时
27//! let token = jwt_manager.generate(&claims)?;
28//!
29//! // Validate and parse JWT token | 验证并解析 JWT token
30//! let decoded_claims = jwt_manager.validate(&token)?;
31//! println!("User ID: {}", decoded_claims.login_id);
32//! ```
33
34use chrono::{DateTime, Duration, Utc};
35use jsonwebtoken::{
36    decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
37};
38use serde::{Deserialize, Serialize};
39use serde_json::Value;
40use std::collections::HashMap;
41
42use crate::error::{SaTokenError, SaTokenResult};
43
44/// JWT Algorithm | JWT 算法
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[derive(Default)]
47pub enum JwtAlgorithm {
48    /// HMAC using SHA-256 | 使用 SHA-256 的 HMAC
49    #[default]
50    HS256,
51    /// HMAC using SHA-384 | 使用 SHA-384 的 HMAC
52    HS384,
53    /// HMAC using SHA-512 | 使用 SHA-512 的 HMAC
54    HS512,
55    /// RSA using SHA-256 | 使用 SHA-256 的 RSA
56    RS256,
57    /// RSA using SHA-384 | 使用 SHA-384 的 RSA
58    RS384,
59    /// RSA using SHA-512 | 使用 SHA-512 的 RSA
60    RS512,
61    /// ECDSA using SHA-256 | 使用 SHA-256 的 ECDSA
62    ES256,
63    /// ECDSA using SHA-384 | 使用 SHA-384 的 ECDSA
64    ES384,
65}
66
67
68impl From<JwtAlgorithm> for Algorithm {
69    fn from(alg: JwtAlgorithm) -> Self {
70        match alg {
71            JwtAlgorithm::HS256 => Algorithm::HS256,
72            JwtAlgorithm::HS384 => Algorithm::HS384,
73            JwtAlgorithm::HS512 => Algorithm::HS512,
74            JwtAlgorithm::RS256 => Algorithm::RS256,
75            JwtAlgorithm::RS384 => Algorithm::RS384,
76            JwtAlgorithm::RS512 => Algorithm::RS512,
77            JwtAlgorithm::ES256 => Algorithm::ES256,
78            JwtAlgorithm::ES384 => Algorithm::ES384,
79        }
80    }
81}
82
83/// JWT Claims | JWT 声明
84///
85/// Standard JWT claims with sa-token extensions
86/// 标准 JWT 声明及 sa-token 扩展
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct JwtClaims {
89    /// Subject (user identifier) | 主题(用户标识符)
90    #[serde(rename = "sub")]
91    pub login_id: String,
92
93    /// Issuer | 签发者
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub iss: Option<String>,
96
97    /// Audience | 受众
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub aud: Option<String>,
100
101    /// Expiration time (Unix timestamp) | 过期时间(Unix 时间戳)
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub exp: Option<i64>,
104
105    /// Not before time (Unix timestamp) | 生效时间(Unix 时间戳)
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub nbf: Option<i64>,
108
109    /// Issued at time (Unix timestamp) | 签发时间(Unix 时间戳)
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub iat: Option<i64>,
112
113    /// JWT ID (unique identifier) | JWT ID(唯一标识符)
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub jti: Option<String>,
116
117    // Sa-token extensions | Sa-token 扩展字段
118
119    /// Login type (user, admin, etc.) | 登录类型(用户、管理员等)
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub login_type: Option<String>,
122
123    /// Device identifier | 设备标识
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub device: Option<String>,
126
127    /// Custom data | 自定义数据
128    #[serde(default)]
129    #[serde(skip_serializing_if = "HashMap::is_empty")]
130    pub extra: HashMap<String, Value>,
131}
132
133impl JwtClaims {
134    /// Create new JWT claims | 创建新的 JWT 声明
135    ///
136    /// # Arguments | 参数
137    ///
138    /// * `login_id` - User identifier | 用户标识符
139    pub fn new(login_id: impl Into<String>) -> Self {
140        let now = Utc::now().timestamp();
141        Self {
142            login_id: login_id.into(),
143            iss: None,
144            aud: None,
145            exp: None,
146            nbf: None,
147            iat: Some(now),
148            jti: None,
149            login_type: Some("default".to_string()),
150            device: None,
151            extra: HashMap::new(),
152        }
153    }
154
155    /// Set expiration time in seconds from now | 设置从现在开始的过期时间(秒)
156    ///
157    /// # Arguments | 参数
158    ///
159    /// * `seconds` - Seconds until expiration | 到期秒数
160    pub fn set_expiration(&mut self, seconds: i64) -> &mut Self {
161        let exp_time = Utc::now() + Duration::seconds(seconds);
162        self.exp = Some(exp_time.timestamp());
163        self
164    }
165
166    /// Set expiration at specific time | 设置具体的过期时间
167    pub fn set_expiration_at(&mut self, datetime: DateTime<Utc>) -> &mut Self {
168        self.exp = Some(datetime.timestamp());
169        self
170    }
171
172    /// Set issuer | 设置签发者
173    pub fn set_issuer(&mut self, issuer: impl Into<String>) -> &mut Self {
174        self.iss = Some(issuer.into());
175        self
176    }
177
178    /// Set audience | 设置受众
179    pub fn set_audience(&mut self, audience: impl Into<String>) -> &mut Self {
180        self.aud = Some(audience.into());
181        self
182    }
183
184    /// Set JWT ID | 设置 JWT ID
185    pub fn set_jti(&mut self, jti: impl Into<String>) -> &mut Self {
186        self.jti = Some(jti.into());
187        self
188    }
189
190    /// Set login type | 设置登录类型
191    pub fn set_login_type(&mut self, login_type: impl Into<String>) -> &mut Self {
192        self.login_type = Some(login_type.into());
193        self
194    }
195
196    /// Set device identifier | 设置设备标识
197    pub fn set_device(&mut self, device: impl Into<String>) -> &mut Self {
198        self.device = Some(device.into());
199        self
200    }
201
202    /// Add custom claim | 添加自定义声明
203    pub fn add_claim(&mut self, key: impl Into<String>, value: Value) -> &mut Self {
204        self.extra.insert(key.into(), value);
205        self
206    }
207
208    /// Get custom claim | 获取自定义声明
209    pub fn get_claim(&self, key: &str) -> Option<&Value> {
210        self.extra.get(key)
211    }
212    
213    /// Set all custom claims at once | 一次设置所有自定义声明
214    pub fn set_claims(&mut self, claims: HashMap<String, Value>) -> &mut Self {
215        self.extra = claims;
216        self
217    }
218    
219    /// Get all custom claims | 获取所有自定义声明
220    pub fn get_claims(&self) -> &HashMap<String, Value> {
221        &self.extra
222    }
223
224    /// Check if token is expired | 检查 token 是否过期
225    pub fn is_expired(&self) -> bool {
226        if let Some(exp) = self.exp {
227            let now = Utc::now().timestamp();
228            now >= exp
229        } else {
230            false
231        }
232    }
233
234    /// Get remaining time in seconds | 获取剩余时间(秒)
235    pub fn remaining_time(&self) -> Option<i64> {
236        self.exp.map(|exp| {
237            let now = Utc::now().timestamp();
238            (exp - now).max(0)
239        })
240    }
241}
242
243/// JWT Manager | JWT 管理器
244///
245/// Manages JWT token generation, validation, and parsing
246/// 管理 JWT token 的生成、验证和解析
247#[derive(Clone)]
248pub struct JwtManager {
249    /// Secret key for HMAC algorithms | HMAC 算法的密钥
250    secret: String,
251
252    /// Algorithm to use | 使用的算法
253    algorithm: JwtAlgorithm,
254
255    /// Issuer | 签发者
256    issuer: Option<String>,
257
258    /// Audience | 受众
259    audience: Option<String>,
260}
261
262impl JwtManager {
263    /// Create new JWT manager with HS256 algorithm | 创建使用 HS256 算法的新 JWT 管理器
264    ///
265    /// # Arguments | 参数
266    ///
267    /// * `secret` - Secret key | 密钥
268    pub fn new(secret: impl Into<String>) -> Self {
269        Self {
270            secret: secret.into(),
271            algorithm: JwtAlgorithm::HS256,
272            issuer: None,
273            audience: None,
274        }
275    }
276
277    /// Create JWT manager with custom algorithm | 创建使用自定义算法的 JWT 管理器
278    pub fn with_algorithm(secret: impl Into<String>, algorithm: JwtAlgorithm) -> Self {
279        Self {
280            secret: secret.into(),
281            algorithm,
282            issuer: None,
283            audience: None,
284        }
285    }
286
287    /// Set issuer | 设置签发者
288    pub fn set_issuer(mut self, issuer: impl Into<String>) -> Self {
289        self.issuer = Some(issuer.into());
290        self
291    }
292
293    /// Set audience | 设置受众
294    pub fn set_audience(mut self, audience: impl Into<String>) -> Self {
295        self.audience = Some(audience.into());
296        self
297    }
298
299    /// Generate JWT token | 生成 JWT token
300    ///
301    /// # Arguments | 参数
302    ///
303    /// * `claims` - JWT claims | JWT 声明
304    ///
305    /// # Returns | 返回
306    ///
307    /// JWT token string | JWT token 字符串
308    pub fn generate(&self, claims: &JwtClaims) -> SaTokenResult<String> {
309        let mut final_claims = claims.clone();
310
311        // Set issuer and audience if configured
312        // 如果配置了签发者和受众,则设置
313        if self.issuer.is_some() && final_claims.iss.is_none() {
314            final_claims.iss = self.issuer.clone();
315        }
316        if self.audience.is_some() && final_claims.aud.is_none() {
317            final_claims.aud = self.audience.clone();
318        }
319
320        let header = Header::new(self.algorithm.into());
321        let encoding_key = EncodingKey::from_secret(self.secret.as_bytes());
322
323        encode(&header, &final_claims, &encoding_key).map_err(|e| {
324            SaTokenError::InvalidToken(format!("Failed to generate JWT: {}", e))
325        })
326    }
327
328    /// Validate and parse JWT token | 验证并解析 JWT token
329    ///
330    /// # Arguments | 参数
331    ///
332    /// * `token` - JWT token string | JWT token 字符串
333    ///
334    /// # Returns | 返回
335    ///
336    /// Decoded JWT claims | 解码的 JWT 声明
337    pub fn validate(&self, token: &str) -> SaTokenResult<JwtClaims> {
338        let mut validation = Validation::new(self.algorithm.into());
339
340        // Explicitly enable expiration validation | 明确启用过期验证
341        validation.validate_exp = true;
342        
343        // Set leeway to 0 for strict validation | 设置时间偏差为0以进行严格验证
344        validation.leeway = 0;
345
346        // Configure validation | 配置验证
347        if let Some(ref iss) = self.issuer {
348            validation.set_issuer(&[iss]);
349        }
350        if let Some(ref aud) = self.audience {
351            validation.set_audience(&[aud]);
352        }
353
354        let decoding_key = DecodingKey::from_secret(self.secret.as_bytes());
355
356        let token_data = decode::<JwtClaims>(token, &decoding_key, &validation).map_err(|e| {
357            match e.kind() {
358                jsonwebtoken::errors::ErrorKind::ExpiredSignature => {
359                    SaTokenError::TokenExpired
360                }
361                _ => SaTokenError::InvalidToken(format!("JWT validation failed: {}", e)),
362            }
363        })?;
364
365        Ok(token_data.claims)
366    }
367
368    /// Decode JWT without validation (unsafe) | 不验证解码 JWT(不安全)
369    ///
370    /// Warning: This does not validate the signature!
371    /// 警告:这不会验证签名!
372    pub fn decode_without_validation(&self, token: &str) -> SaTokenResult<JwtClaims> {
373        let mut validation = Validation::new(self.algorithm.into());
374        validation.insecure_disable_signature_validation();
375        validation.validate_exp = false;
376
377        let decoding_key = DecodingKey::from_secret(self.secret.as_bytes());
378
379        let token_data = decode::<JwtClaims>(token, &decoding_key, &validation).map_err(|e| {
380            SaTokenError::InvalidToken(format!("Failed to decode JWT: {}", e))
381        })?;
382
383        Ok(token_data.claims)
384    }
385
386    /// Refresh JWT token | 刷新 JWT token
387    ///
388    /// Creates a new token with updated expiration time
389    /// 创建具有更新过期时间的新 token
390    ///
391    /// # Arguments | 参数
392    ///
393    /// * `token` - Original JWT token | 原始 JWT token
394    /// * `extend_seconds` - Seconds to extend | 延长的秒数
395    pub fn refresh(&self, token: &str, extend_seconds: i64) -> SaTokenResult<String> {
396        let mut claims = self.validate(token)?;
397
398        // Update expiration time | 更新过期时间
399        claims.set_expiration(extend_seconds);
400
401        // Update issued at time | 更新签发时间
402        claims.iat = Some(Utc::now().timestamp());
403
404        self.generate(&claims)
405    }
406
407    /// Extract user ID from token without full validation | 从 token 提取用户 ID(无需完整验证)
408    ///
409    /// Useful for quick user identification
410    /// 用于快速用户识别
411    pub fn extract_login_id(&self, token: &str) -> SaTokenResult<String> {
412        let claims = self.decode_without_validation(token)?;
413        Ok(claims.login_id)
414    }
415}
416
417#[cfg(test)]
418mod tests {
419    use super::*;
420
421    #[test]
422    fn test_jwt_claims_creation() {
423        let mut claims = JwtClaims::new("user_123");
424        claims.set_expiration(3600);
425        claims.set_issuer("sa-token");
426        claims.add_claim("role", serde_json::json!("admin"));
427
428        assert_eq!(claims.login_id, "user_123");
429        assert!(claims.exp.is_some());
430        assert_eq!(claims.iss, Some("sa-token".to_string()));
431        assert_eq!(
432            claims.get_claim("role"),
433            Some(&serde_json::json!("admin"))
434        );
435    }
436
437    #[test]
438    fn test_jwt_generate_and_validate() {
439        let jwt_manager = JwtManager::new("test-secret-key");
440
441        let mut claims = JwtClaims::new("user_123");
442        claims.set_expiration(3600);
443
444        // Generate token | 生成 token
445        let token = jwt_manager.generate(&claims).unwrap();
446        assert!(!token.is_empty());
447
448        // Validate token | 验证 token
449        let decoded = jwt_manager.validate(&token).unwrap();
450        assert_eq!(decoded.login_id, "user_123");
451        assert!(!decoded.is_expired());
452    }
453
454    #[test]
455    fn test_jwt_expired() {
456        let jwt_manager = JwtManager::new("test-secret-key");
457
458        let mut claims = JwtClaims::new("user_123");
459        // Set expiration to 10 seconds in the past to account for leeway
460        // 设置过期时间为10秒前以考虑时间偏差
461        let exp_time = Utc::now() - Duration::seconds(10);
462        claims.set_expiration_at(exp_time);
463
464        let token = jwt_manager.generate(&claims).unwrap();
465
466        // Should fail validation due to expiration | 应该因过期而验证失败
467        let result = jwt_manager.validate(&token);
468        assert!(result.is_err());
469        
470        // Verify it's specifically an expiration error | 验证是过期错误
471        match result {
472            Err(SaTokenError::TokenExpired) => {}, // Expected | 预期
473            _ => panic!("Expected TokenExpired error"),
474        }
475    }
476
477    #[test]
478    fn test_jwt_refresh() {
479        let jwt_manager = JwtManager::new("test-secret-key");
480
481        let mut claims = JwtClaims::new("user_123");
482        claims.set_expiration(3600);
483
484        let original_token = jwt_manager.generate(&claims).unwrap();
485
486        // Refresh token | 刷新 token
487        let new_token = jwt_manager.refresh(&original_token, 7200).unwrap();
488        assert_ne!(original_token, new_token);
489
490        // Validate new token | 验证新 token
491        let decoded = jwt_manager.validate(&new_token).unwrap();
492        assert_eq!(decoded.login_id, "user_123");
493    }
494
495    #[test]
496    fn test_jwt_custom_claims() {
497        let jwt_manager = JwtManager::new("test-secret-key");
498
499        let mut claims = JwtClaims::new("user_123");
500        claims.set_expiration(3600);
501        claims.add_claim("role", serde_json::json!("admin"));
502        claims.add_claim("permissions", serde_json::json!(["read", "write"]));
503
504        let token = jwt_manager.generate(&claims).unwrap();
505        let decoded = jwt_manager.validate(&token).unwrap();
506
507        assert_eq!(decoded.get_claim("role"), Some(&serde_json::json!("admin")));
508        assert_eq!(
509            decoded.get_claim("permissions"),
510            Some(&serde_json::json!(["read", "write"]))
511        );
512    }
513
514    #[test]
515    fn test_extract_login_id() {
516        let jwt_manager = JwtManager::new("test-secret-key");
517
518        let mut claims = JwtClaims::new("user_123");
519        claims.set_expiration(3600);
520
521        let token = jwt_manager.generate(&claims).unwrap();
522        let login_id = jwt_manager.extract_login_id(&token).unwrap();
523
524        assert_eq!(login_id, "user_123");
525    }
526}
527