Files
zclaw_openfang/crates/zclaw-saas/src/auth/password.rs
iven 7de294375b
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
feat(auth): 添加异步密码哈希和验证函数
refactor(relay): 复用HTTP客户端和请求体序列化结果

feat(kernel): 添加获取单个审批记录的方法

fix(store): 改进SaaS连接错误分类和降级处理

docs: 更新审计文档和系统架构文档

refactor(prompt): 优化SQL查询参数化绑定

refactor(migration): 使用静态SQL和COALESCE更新配置项

feat(commands): 添加审批执行状态追踪和事件通知

chore: 更新启动脚本以支持Admin后台

fix(auth-guard): 优化授权状态管理和错误处理

refactor(db): 使用异步密码哈希函数

refactor(totp): 使用异步密码验证函数

style: 清理无用文件和注释

docs: 更新功能全景和审计文档

refactor(service): 优化HTTP客户端重用和请求处理

fix(connection): 改进SaaS不可用时的降级处理

refactor(handlers): 使用异步密码验证函数

chore: 更新依赖和工具链配置
2026-03-29 21:45:29 +08:00

67 lines
2.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 密码哈希 (Argon2id)
//!
//! Argon2 是 CPU 密集型操作(~100-500ms不能在 tokio worker 线程上直接执行,
//! 否则会阻塞整个异步运行时。所有 async 上下文必须使用 `hash_password_async`
//! 和 `verify_password_async`。
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
use crate::error::{SaasError, SaasResult};
/// 哈希密码(同步版本,仅用于测试和启动时 seed
pub fn hash_password(password: &str) -> SaasResult<String> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2
.hash_password(password.as_bytes(), &salt)
.map_err(|e| SaasError::PasswordHash(e.to_string()))?;
Ok(hash.to_string())
}
/// 验证密码(同步版本,仅用于测试)
pub fn verify_password(password: &str, hash: &str) -> SaasResult<bool> {
let parsed_hash = PasswordHash::new(hash)
.map_err(|e| SaasError::PasswordHash(e.to_string()))?;
Ok(Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
/// 异步哈希密码 — 在 spawn_blocking 线程池中执行 Argon2
pub async fn hash_password_async(password: String) -> SaasResult<String> {
tokio::task::spawn_blocking(move || hash_password(&password))
.await
.map_err(|e| SaasError::Internal(format!("spawn_blocking error: {e}")))?
}
/// 异步验证密码 — 在 spawn_blocking 线程池中执行 Argon2
pub async fn verify_password_async(password: String, hash: String) -> SaasResult<bool> {
tokio::task::spawn_blocking(move || verify_password(&password, &hash))
.await
.map_err(|e| SaasError::Internal(format!("spawn_blocking error: {e}")))?
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_and_verify() {
let hash = hash_password("correct_password").unwrap();
assert!(verify_password("correct_password", &hash).unwrap());
assert!(!verify_password("wrong_password", &hash).unwrap());
}
#[test]
fn test_different_hashes_for_same_password() {
let hash1 = hash_password("same_password").unwrap();
let hash2 = hash_password("same_password").unwrap();
assert_ne!(hash1, hash2);
assert!(verify_password("same_password", &hash1).unwrap());
assert!(verify_password("same_password", &hash2).unwrap());
}
}