Files
zclaw_openfang/crates/zclaw-saas/src/auth/password.rs
iven a2f8112d69 feat(saas): Phase 1 — 基础框架与账号管理模块
- 新增 zclaw-saas crate 作为 workspace 成员
- 配置系统 (TOML + 环境变量覆盖)
- 错误类型体系 (SaasError 16 变体, IntoResponse)
- SQLite 数据库 (12 表 schema, 内存/文件双模式, 3 系统角色种子数据)
- JWT 认证 (签发/验证/刷新)
- Argon2id 密码哈希
- 认证中间件 (公开/受保护路由分层)
- 账号管理 CRUD + API Token 管理 + 操作日志
- 7 单元测试 + 5 集成测试全部通过
2026-03-27 12:58:01 +08:00

49 lines
1.5 KiB
Rust

//! 密码哈希 (Argon2id)
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
use crate::error::{SaasError, SaasResult};
/// 哈希密码
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())
}
#[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());
}
}