rwadurian/backend/services/auth-service/src/domain/value-objects/password.vo.ts

69 lines
1.8 KiB
TypeScript

import * as bcrypt from 'bcrypt';
/**
* 密码值对象
* 包含密码验证规则和加密逻辑
*/
export class Password {
private static readonly MIN_LENGTH = 8;
private static readonly MAX_LENGTH = 32;
private static readonly SALT_ROUNDS = 12;
private constructor(public readonly hash: string) {}
/**
* 从明文密码创建(会进行加密)
*/
static async create(plainPassword: string): Promise<Password> {
Password.validatePlain(plainPassword);
const hash = await bcrypt.hash(plainPassword, Password.SALT_ROUNDS);
return new Password(hash);
}
/**
* 从已加密的 hash 重建
*/
static fromHash(hash: string): Password {
return new Password(hash);
}
/**
* 验证明文密码是否匹配
*/
async verify(plainPassword: string): Promise<boolean> {
return bcrypt.compare(plainPassword, this.hash);
}
/**
* 验证密码格式
*/
private static validatePlain(password: string): void {
if (password.length < Password.MIN_LENGTH) {
throw new Error(`密码长度不能少于 ${Password.MIN_LENGTH}`);
}
if (password.length > Password.MAX_LENGTH) {
throw new Error(`密码长度不能超过 ${Password.MAX_LENGTH}`);
}
// 至少包含一个字母和一个数字
if (!/[a-zA-Z]/.test(password) || !/\d/.test(password)) {
throw new Error('密码必须包含字母和数字');
}
}
/**
* 检查密码强度(仅验证格式,不加密)
*/
static checkStrength(password: string): { valid: boolean; message?: string } {
try {
Password.validatePlain(password);
return { valid: true };
} catch (error) {
return { valid: false, message: (error as Error).message };
}
}
toString(): string {
return '[PROTECTED]';
}
}