PayrollManagementSystem/backend/tests/unit/test_security.py
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 FastAPI 六层结构(api/services/repositories/models/schemas/core),
统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存
db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、
登出黑名单(TTL=令牌剩余有效期)、登录日志。

前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。

工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。

测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-09 18:46:36 +08:00

67 lines
2.6 KiB
Python
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.

"""密码哈希单元测试。
重点覆盖 bcrypt 5.0 的 72 字节硬限制 —— 此前是静默截断,现在是抛 ValueError,
若不在边界上把关,用户设一个长密码就会在登录时炸出 500。
"""
import pytest
from app.core.constants import PASSWORD_MAX_BYTES
from app.core.security import hash_password, verify_password
class TestHashPassword:
"""哈希生成。"""
def test_输出为BCrypt格式且定长60字符(self) -> None:
hashed = hash_password("Admin@2026")
assert hashed.startswith("$2")
assert len(hashed) == 60
def test_相同明文两次哈希结果不同(self) -> None:
"""BCrypt 内含随机盐,相同明文每次结果不同 —— 这是防彩虹表的基础。"""
assert hash_password("SamePwd@1") != hash_password("SamePwd@1")
def test_恰好72字节时允许(self) -> None:
password = "a" * PASSWORD_MAX_BYTES
assert len(password.encode("utf-8")) == PASSWORD_MAX_BYTES
assert len(hash_password(password)) == 60
def test_超过72字节时抛ValueError(self) -> None:
with pytest.raises(ValueError):
hash_password("a" * (PASSWORD_MAX_BYTES + 1))
@pytest.mark.parametrize(
("text", "count", "should_pass"),
[("中", 24, True), ("中", 25, False)],
)
def test_中文按字节而非字符计算边界(self, text: str, count: int, should_pass: bool) -> None:
"""一个汉字 3 字节:24 个正好 72 字节,25 个即 75 字节超限。"""
password = text * count
if should_pass:
assert len(hash_password(password)) == 60
else:
with pytest.raises(ValueError):
hash_password(password)
class TestVerifyPassword:
"""哈希校验。"""
def test_正确密码校验通过(self) -> None:
assert verify_password("Admin@2026", hash_password("Admin@2026")) is True
def test_错误密码校验失败(self) -> None:
assert verify_password("WrongPwd@1", hash_password("Admin@2026")) is False
def test_超长密码返回False而不抛异常(self) -> None:
"""校验路径不得因输入长度报错 —— 否则会形成「密码过长」与「密码错误」
可区分的侧信道,且会让恶意长输入直接打出 500。"""
assert verify_password("a" * 200, hash_password("Admin@2026")) is False
def test_损坏的哈希串返回False而不抛异常(self) -> None:
assert verify_password("Admin@2026", "not-a-bcrypt-hash") is False
def test_空哈希串返回False(self) -> None:
assert verify_password("Admin@2026", "") is False