fix: 认证审查修复(角色依赖连锁409门/停用租户token取舍注记/公共面整理)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ TDD 循环闭环;Task 3 为 /api/platform/ 前缀加中间件豁免,豁免≠放
|
||||
|
||||
安全纪律:
|
||||
- 防枚举: 用户不存在/停用/密码错同一 401 静态文案;查无用户时跑等代价
|
||||
哑 scrypt(auth._dummy_credentials)抹平时序差。
|
||||
哑 scrypt(auth.dummy_credentials)抹平时序差。
|
||||
- 密码/token 永不进日志: 本模块不 log 任何凭据字段,HTTPException
|
||||
detail 均静态串。
|
||||
- token 走 Authorization: Bearer(无 Cookie 免 CSRF,spec §7.2)。
|
||||
@@ -19,8 +19,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.platform.auth import (
|
||||
CurrentUser,
|
||||
_dummy_credentials,
|
||||
_bearer_token,
|
||||
bearer_token,
|
||||
dummy_credentials,
|
||||
hash_password,
|
||||
login_limiter,
|
||||
platform_user,
|
||||
@@ -51,7 +51,12 @@ def _client_ip(request: Request) -> str:
|
||||
|
||||
@router.post("/login")
|
||||
async def login(body: LoginRequest, request: Request):
|
||||
"""限流 → 查用户(active)→ 验密 → 发 token。"""
|
||||
"""限流 → 查用户(active)→ 验密 → 发 token。
|
||||
|
||||
token 是签发时快照(校验不回查库):租户停用后其管理员的存量 token
|
||||
最长再存活一个 TTL(默认 8h)——本实现接受该窗口,不做停用即时锁死;
|
||||
若需要即时失效,归 Task 5 租户管理端点(validate 时回查 Tenant.status
|
||||
并吊销该租户 token),此处注记归属而非留白。"""
|
||||
ip = _client_ip(request)
|
||||
if not login_limiter.allow(body.username, ip):
|
||||
raise HTTPException(status_code=429, detail=LOCKED_DETAIL)
|
||||
@@ -70,7 +75,7 @@ async def login(body: LoginRequest, request: Request):
|
||||
else:
|
||||
# 防枚举(时序): 查无用户/停用也跑一次等代价 scrypt,响应耗时与
|
||||
# 密码错不可区分;结果仍统一 401
|
||||
dummy_hash, dummy_salt = _dummy_credentials()
|
||||
dummy_hash, dummy_salt = dummy_credentials()
|
||||
verify_password(body.password, dummy_hash, dummy_salt)
|
||||
ok = False
|
||||
|
||||
@@ -100,7 +105,7 @@ async def logout(request: Request, user: platform_user):
|
||||
(Task 5 起所有 /api/platform/* 受保护端点统一走 platform_user,
|
||||
login/change-password 豁免)。依赖已验过 token,此处重读头取原串
|
||||
撤销——409/401 都在依赖层先行。"""
|
||||
token_store.revoke(_bearer_token(request) or "")
|
||||
token_store.revoke(bearer_token(request) or "")
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -128,5 +133,5 @@ async def change_password(
|
||||
db_user.must_change_password = False
|
||||
await session.commit()
|
||||
|
||||
token_store.clear_must_change(_bearer_token(request) or "")
|
||||
token_store.clear_must_change(bearer_token(request) or "")
|
||||
return {"ok": True, "must_change_password": False}
|
||||
|
||||
Reference in New Issue
Block a user