sa_token_core/
nonce.rs

1// Author: 金书记
2//
3//! Nonce Manager | Nonce 管理器
4//!
5//! Prevents replay attacks by tracking used nonces
6//! 通过跟踪已使用的 nonce 来防止重放攻击
7
8use std::sync::Arc;
9use chrono::{DateTime, Utc};
10use sa_token_adapter::storage::SaStorage;
11use crate::error::{SaTokenError, SaTokenResult};
12use uuid::Uuid;
13
14/// Nonce Manager | Nonce 管理器
15///
16/// Manages nonce generation and validation to prevent replay attacks
17/// 管理 nonce 的生成和验证以防止重放攻击
18#[derive(Clone)]
19pub struct NonceManager {
20    storage: Arc<dyn SaStorage>,
21    timeout: i64,
22}
23
24impl NonceManager {
25    /// Create new nonce manager | 创建新的 nonce 管理器
26    ///
27    /// # Arguments | 参数
28    ///
29    /// * `storage` - Storage backend | 存储后端
30    /// * `timeout` - Nonce validity period in seconds | Nonce 有效期(秒)
31    pub fn new(storage: Arc<dyn SaStorage>, timeout: i64) -> Self {
32        Self { storage, timeout }
33    }
34
35    /// Generate a new nonce | 生成新的 nonce
36    ///
37    /// # Returns | 返回
38    ///
39    /// Unique nonce string | 唯一的 nonce 字符串
40    pub fn generate(&self) -> String {
41        format!("nonce_{}_{}", Utc::now().timestamp_millis(), Uuid::new_v4().simple())
42    }
43
44    /// Store and mark nonce as used | 存储并标记 nonce 为已使用
45    ///
46    /// # Arguments | 参数
47    ///
48    /// * `nonce` - Nonce to store | 要存储的 nonce
49    /// * `login_id` - Associated user ID | 关联的用户ID
50    pub async fn store(&self, nonce: &str, login_id: &str) -> SaTokenResult<()> {
51        let key = format!("sa:nonce:{}", nonce);
52        let value = serde_json::json!({
53            "login_id": login_id,
54            "created_at": Utc::now().to_rfc3339(),
55        }).to_string();
56
57        let ttl = Some(std::time::Duration::from_secs(self.timeout as u64));
58        self.storage.set(&key, &value, ttl)
59            .await
60            .map_err(|e| SaTokenError::StorageError(e.to_string()))?;
61
62        Ok(())
63    }
64
65    /// Validate nonce and ensure it hasn't been used | 验证 nonce 并确保未被使用
66    ///
67    /// # Arguments | 参数
68    ///
69    /// * `nonce` - Nonce to validate | 要验证的 nonce
70    ///
71    /// # Returns | 返回
72    ///
73    /// `Ok(true)` if valid and not used, `Ok(false)` if already used
74    /// 如果有效且未使用返回 `Ok(true)`,如果已使用返回 `Ok(false)`
75    pub async fn validate(&self, nonce: &str) -> SaTokenResult<bool> {
76        let key = format!("sa:nonce:{}", nonce);
77        
78        // Check if nonce exists (has been used)
79        // 检查 nonce 是否存在(已被使用)
80        let exists = self.storage.get(&key)
81            .await
82            .map_err(|e| SaTokenError::StorageError(e.to_string()))?
83            .is_some();
84
85        Ok(!exists) // Valid if NOT exists | 不存在则有效
86    }
87
88    /// Validate and consume nonce in one operation | 一次操作验证并消费 nonce
89    ///
90    /// # Arguments | 参数
91    ///
92    /// * `nonce` - Nonce to validate and consume | 要验证和消费的 nonce
93    /// * `login_id` - Associated user ID | 关联的用户ID
94    ///
95    /// # Returns | 返回
96    ///
97    /// `Ok(())` if valid, error if already used or invalid
98    /// 如果有效返回 `Ok(())`,如果已使用或无效返回错误
99    pub async fn validate_and_consume(&self, nonce: &str, login_id: &str) -> SaTokenResult<()> {
100        if !self.validate(nonce).await? {
101            return Err(SaTokenError::NonceAlreadyUsed);
102        }
103
104        self.store(nonce, login_id).await?;
105        Ok(())
106    }
107
108    /// Extract timestamp from nonce and check if it's within valid time window
109    /// 从 nonce 中提取时间戳并检查是否在有效时间窗口内
110    ///
111    /// # Arguments | 参数
112    ///
113    /// * `nonce` - Nonce to check | 要检查的 nonce
114    /// * `window_seconds` - Time window in seconds | 时间窗口(秒)
115    pub fn check_timestamp(&self, nonce: &str, window_seconds: i64) -> SaTokenResult<bool> {
116        // Nonce format: nonce_TIMESTAMP_UUID
117        let parts: Vec<&str> = nonce.split('_').collect();
118        if parts.len() < 2 || parts[0] != "nonce" {
119            return Err(SaTokenError::InvalidNonceFormat);
120        }
121
122        let timestamp_ms = parts[1].parse::<i64>()
123            .map_err(|_| SaTokenError::InvalidNonceTimestamp)?;
124
125        let nonce_time = DateTime::from_timestamp_millis(timestamp_ms)
126            .ok_or_else(|| SaTokenError::InvalidNonceTimestamp)?;
127
128        let now = Utc::now();
129        let diff = (now - nonce_time).num_seconds().abs();
130
131        Ok(diff <= window_seconds)
132    }
133
134    /// Clean up expired nonces (implementation depends on storage)
135    /// 清理过期的 nonce(实现依赖于存储)
136    ///
137    /// Note: Most storage backends automatically expire keys with TTL
138    /// 注意:大多数存储后端会自动过期带 TTL 的键
139    pub async fn cleanup_expired(&self) -> SaTokenResult<()> {
140        // Storage with TTL support will auto-cleanup
141        // 支持 TTL 的存储会自动清理
142        Ok(())
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use sa_token_storage_memory::MemoryStorage;
150
151    #[tokio::test]
152    async fn test_nonce_generation() {
153        let storage = Arc::new(MemoryStorage::new());
154        let nonce_mgr = NonceManager::new(storage, 60);
155
156        let nonce1 = nonce_mgr.generate();
157        let nonce2 = nonce_mgr.generate();
158
159        assert_ne!(nonce1, nonce2);
160        assert!(nonce1.starts_with("nonce_"));
161    }
162
163    #[tokio::test]
164    async fn test_nonce_validation() {
165        let storage = Arc::new(MemoryStorage::new());
166        let nonce_mgr = NonceManager::new(storage, 60);
167
168        let nonce = nonce_mgr.generate();
169
170        // First validation should succeed
171        assert!(nonce_mgr.validate(&nonce).await.unwrap());
172
173        // Store the nonce
174        nonce_mgr.store(&nonce, "user_123").await.unwrap();
175
176        // Second validation should fail (already used)
177        assert!(!nonce_mgr.validate(&nonce).await.unwrap());
178    }
179
180    #[tokio::test]
181    async fn test_nonce_validate_and_consume() {
182        let storage = Arc::new(MemoryStorage::new());
183        let nonce_mgr = NonceManager::new(storage, 60);
184
185        let nonce = nonce_mgr.generate();
186
187        // First use should succeed
188        nonce_mgr.validate_and_consume(&nonce, "user_123").await.unwrap();
189
190        // Second use should fail
191        let result = nonce_mgr.validate_and_consume(&nonce, "user_123").await;
192        assert!(result.is_err());
193    }
194
195    #[tokio::test]
196    async fn test_nonce_timestamp_check() {
197        let storage = Arc::new(MemoryStorage::new());
198        let nonce_mgr = NonceManager::new(storage, 60);
199
200        let nonce = nonce_mgr.generate();
201
202        // Should be within 60 seconds
203        assert!(nonce_mgr.check_timestamp(&nonce, 60).unwrap());
204
205        // Should also be within 1 second
206        assert!(nonce_mgr.check_timestamp(&nonce, 1).unwrap());
207    }
208}
209