NonceManager

Struct NonceManager 

Source
pub struct NonceManager { /* private fields */ }
Expand description

Nonce Manager | Nonce 管理器

Manages nonce generation and validation to prevent replay attacks 管理 nonce 的生成和验证以防止重放攻击

Implementations§

Source§

impl NonceManager

Source

pub fn new(storage: Arc<dyn SaStorage>, timeout: i64) -> Self

Create new nonce manager | 创建新的 nonce 管理器

§Arguments | 参数
  • storage - Storage backend | 存储后端
  • timeout - Nonce validity period in seconds | Nonce 有效期(秒)
Source

pub fn generate(&self) -> String

Generate a new nonce | 生成新的 nonce

Generates a unique nonce using timestamp + UUID to ensure uniqueness. 使用时间戳 + UUID 生成唯一的 nonce 以确保唯一性。

§Returns | 返回

Unique nonce string in format: nonce_{timestamp_ms}_{uuid} 格式为 nonce_{时间戳_毫秒}_{uuid} 的唯一 nonce 字符串

§Format | 格式
nonce_1234567890123_abc123def456
  │         │            │
  │         │            └─ UUID (32 hex chars)
  │         └─ Timestamp in milliseconds
  └─ Prefix
§Example | 示例
let nonce = nonce_manager.generate();
// Returns: "nonce_1701234567890_a1b2c3d4e5f6..."
Source

pub async fn store(&self, nonce: &str, login_id: &str) -> SaTokenResult<()>

Store and mark nonce as used | 存储并标记 nonce 为已使用

Stores the nonce in storage with TTL, marking it as “consumed”. 将 nonce 以 TTL 存储在存储中,标记为“已消费“。

§Arguments | 参数
  • nonce - Nonce to store | 要存储的 nonce
  • login_id - Associated user ID | 关联的用户ID
§Storage Key | 存储键

sa:nonce:{nonce}{"login_id": "...", "created_at": "..."}

§TTL Behavior | TTL 行为

The nonce is automatically removed after the timeout period. Nonce 会在超时期后自动移除。

§Example | 示例
nonce_manager.store("nonce_123_abc", "user_001").await?;
// Storage now contains: sa:nonce:nonce_123_abc (expires after timeout)
Source

pub async fn validate(&self, nonce: &str) -> SaTokenResult<bool>

Validate nonce and ensure it hasn’t been used | 验证 nonce 并确保未被使用

Checks if the nonce exists in storage. If it exists, it has been used. 检查 nonce 是否存在于存储中。如果存在,则已被使用。

§Arguments | 参数
  • nonce - Nonce to validate | 要验证的 nonce
§Returns | 返回
  • Ok(true) - Valid (not used yet) | 有效(尚未使用)
  • Ok(false) - Invalid (already used) | 无效(已使用)
§Logic | 逻辑
Nonce NOT in storage → Valid (can be used)
Nonce IN storage     → Invalid (already used)
 
Nonce 不在存储中 → 有效(可以使用)
Nonce 在存储中   → 无效(已使用)
§Example | 示例
let is_valid = nonce_manager.validate("nonce_123").await?;
if is_valid {
    // Proceed with operation
} else {
    // Reject: nonce already used
}
Source

pub async fn validate_and_consume( &self, nonce: &str, login_id: &str, ) -> SaTokenResult<()>

Validate and consume nonce in one operation | 一次操作验证并消费 nonce

This is the primary method for using nonces in Sa-Token. It checks if the nonce is valid (not used) and immediately marks it as used. 这是在 Sa-Token 中使用 nonce 的主要方法。 它检查 nonce 是否有效(未使用)并立即将其标记为已使用。

§Arguments | 参数
  • nonce - Nonce to validate and consume | 要验证和消费的 nonce
  • login_id - Associated user ID | 关联的用户ID
§Returns | 返回
  • Ok(()) - Nonce is valid and now consumed | Nonce 有效且已消费
  • Err(NonceAlreadyUsed) - Nonce has already been used | Nonce 已被使用
  • Err(StorageError) - Storage operation failed | 存储操作失败
§Security | 安全性

This operation is atomic from the application perspective: 此操作从应用程序角度来看是原子性的:

  1. Check if nonce exists (validate)
  2. If valid, store it immediately (consume)
  3. Return success

If two requests use the same nonce simultaneously, only one will succeed. 如果两个请求同时使用相同的 nonce,只有一个会成功。

§Integration with Login | 与登录集成
// Inside SaTokenManager::login_with_token_info()
if let Some(nonce) = &token_info.nonce {
    self.nonce_manager
        .validate_and_consume(nonce, &login_id)
        .await?; // ← Prevents replay attacks
}
§Example | 示例
// ✅ First use: Success
nonce_manager.validate_and_consume("nonce_123", "user_001").await?;
println!("Login successful");

// ❌ Second use: Error
let result = nonce_manager.validate_and_consume("nonce_123", "user_001").await;
assert!(matches!(result, Err(SaTokenError::NonceAlreadyUsed)));
Source

pub fn check_timestamp( &self, nonce: &str, window_seconds: i64, ) -> SaTokenResult<bool>

Extract timestamp from nonce and check if it’s within valid time window 从 nonce 中提取时间戳并检查是否在有效时间窗口内

Provides additional security by validating the nonce timestamp. This prevents time-based attacks and ensures nonces are fresh. 通过验证 nonce 时间戳提供额外的安全性。 这可以防止基于时间的攻击并确保 nonce 是新鲜的。

§Arguments | 参数
  • nonce - Nonce to check | 要检查的 nonce
  • window_seconds - Maximum age of nonce in seconds | Nonce 的最大年龄(秒)
§Returns | 返回
  • Ok(true) - Timestamp is within the time window | 时间戳在时间窗口内
  • Ok(false) - Timestamp is outside the time window (too old or future) | 时间戳在窗口外(太旧或未来)
  • Err(InvalidNonceFormat) - Nonce format is invalid | Nonce 格式无效
  • Err(InvalidNonceTimestamp) - Timestamp cannot be parsed | 时间戳无法解析
§Use Case | 使用场景
// Validate nonce and its timestamp
let nonce = request.get_nonce();

// Check timestamp: max 60 seconds old
if !nonce_manager.check_timestamp(&nonce, 60)? {
    return Err("Nonce too old");
}

// Then validate and consume
nonce_manager.validate_and_consume(&nonce, user_id).await?;
§Nonce Format | Nonce 格式

Expected format: nonce_{timestamp_ms}_{uuid} 期望格式:nonce_{时间戳_毫秒}_{uuid}

§Security Note | 安全说明

This check should be used in addition to validate_and_consume(), not as a replacement. It provides defense-in-depth. 此检查应与 validate_and_consume() 一起使用,而不是替代。 它提供了深度防御。

Source

pub async fn cleanup_expired(&self) -> SaTokenResult<()>

Clean up expired nonces (implementation depends on storage) 清理过期的 nonce(实现依赖于存储)

§Note | 注意

Most storage backends (Redis, Memcached) automatically expire keys with TTL. This method is provided for storage backends that don’t support TTL. 大多数存储后端(Redis、Memcached)会自动过期带 TTL 的键。 此方法为不支持 TTL 的存储后端提供。

§Automatic Cleanup | 自动清理
  • Redis: Uses EXPIRE command, automatic cleanup
  • Memory: Built-in TTL support, automatic cleanup
  • Database: May need manual cleanup (implement here)
§Manual Implementation | 手动实现

For databases without TTL support: 对于不支持 TTL 的数据库:

pub async fn cleanup_expired(&self) -> SaTokenResult<()> {
    let cutoff = Utc::now() - Duration::seconds(self.timeout);
    // DELETE FROM nonces WHERE created_at < cutoff
    Ok(())
}

Trait Implementations§

Source§

impl Clone for NonceManager

Source§

fn clone(&self) -> NonceManager

Returns a duplicate of the value. Read more
1.0.0§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CloneToUninit for T
where T: Clone,

§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more