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