1use std::sync::Arc;
9use chrono::{DateTime, Utc};
10use sa_token_adapter::storage::SaStorage;
11use crate::error::{SaTokenError, SaTokenResult};
12use uuid::Uuid;
13
14#[derive(Clone)]
19pub struct NonceManager {
20 storage: Arc<dyn SaStorage>,
21 timeout: i64,
22}
23
24impl NonceManager {
25 pub fn new(storage: Arc<dyn SaStorage>, timeout: i64) -> Self {
32 Self { storage, timeout }
33 }
34
35 pub fn generate(&self) -> String {
41 format!("nonce_{}_{}", Utc::now().timestamp_millis(), Uuid::new_v4().simple())
42 }
43
44 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 pub async fn validate(&self, nonce: &str) -> SaTokenResult<bool> {
76 let key = format!("sa:nonce:{}", nonce);
77
78 let exists = self.storage.get(&key)
81 .await
82 .map_err(|e| SaTokenError::StorageError(e.to_string()))?
83 .is_some();
84
85 Ok(!exists) }
87
88 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 pub fn check_timestamp(&self, nonce: &str, window_seconds: i64) -> SaTokenResult<bool> {
116 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 pub async fn cleanup_expired(&self) -> SaTokenResult<()> {
140 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 assert!(nonce_mgr.validate(&nonce).await.unwrap());
172
173 nonce_mgr.store(&nonce, "user_123").await.unwrap();
175
176 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 nonce_mgr.validate_and_consume(&nonce, "user_123").await.unwrap();
189
190 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 assert!(nonce_mgr.check_timestamp(&nonce, 60).unwrap());
204
205 assert!(nonce_mgr.check_timestamp(&nonce, 1).unwrap());
207 }
208}
209