From d057c56c26bc49eaef321eb8bf8b944af85bfa8e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 2 Oct 2026 16:37:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(auth):=20=E5=AF=86=E7=A0=81=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E6=94=B9=E8=B5=B0=20User=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81=EF=BC=88=E5=AE=A1=E8=AE=A1=E8=A1=A8=E6=98=8E?= =?UTF-8?q?=E6=96=87=E5=AF=BC=E8=87=B4=E6=81=92=E5=88=A4=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AdminUserManager.VerifyPassword 原查 Password 明文审计表并用 bcrypt 比对:必然抛 ValueError 被吞成 False,恒判「密码不匹配」 - 改为与登录同源:AuthManager.GetUserByUUID(...).CheckPassword(...),保证「登录能过 = 校验能过」 - ResetPassword 一并修:原来只写审计表、不更新 User.UserPassword,导致「重置成功」但登录密码未变;现两处同写 - 移除因此不再使用的 crypto.VerifyPassword 导入 --- auth/managers.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/auth/managers.py b/auth/managers.py index 3feeca8..0ac058f 100644 --- a/auth/managers.py +++ b/auth/managers.py @@ -4,7 +4,7 @@ from django.db.models import Q from django.utils import timezone as _timezone from gvsdsdk.models import Role, UserRole, UserRoleDepartment, UserRoleDepartmentPosition, UserRoleWorkgroup, RolePermission, Permission, Position, PositionCategory, PositionTemplate, PositionTemplatePermission, AppPosition, AppVirtualPosition, AppVirtualBinding, App, TenantApp, CompanyApp, UserApp, AppConfig, Shop, RoleShop, User2FA, UserAvatar, Password, UserPending2FA, AccountActivation, Tenant, Company, Department, DepartmentSMTM, Workgroup, Message, Menu, WorkOrder, AuditLog, ItemReference, Backpack, FileAsset, CompanyInfo -from gvsdsdk.auth.crypto import HashPassword, VerifyPassword +from gvsdsdk.auth.crypto import HashPassword from gvsdsdk.model_utils import _GetUserModel, UUIDObj # 系统公司 UUID(00000000-0000-0000-0000-000000000001),全局唯一 @@ -2527,7 +2527,11 @@ class AdminUserManager: @staticmethod def ResetPassword(UserUUID, NewPassword): - """重置用户密码(写入独立 Password 表,bcrypt 哈希) + """重置用户密码 + + 必须**同时**写 `User` 表的登录密码与 `Password` 审计表: + 登录校验(LoginView → User.CheckPassword)读的是 `User.UserPassword`, + 旧实现只写审计表,导致返回「重置成功」但实际登录密码并未改变。 Returns: (成功?, 消息) @@ -2535,6 +2539,8 @@ class AdminUserManager: user = AuthManager.GetUserByUUID(UserUUID) if user is None: return False, '用户不存在' + user.SetPassword(NewPassword) + user.save(update_fields=['UserPassword']) pw_obj, _ = Password.objects.get_or_create(UserUUID=user.UserUUID) pw_obj.Password = HashPassword(NewPassword) pw_obj.save(update_fields=['Password']) @@ -2542,21 +2548,28 @@ class AdminUserManager: @staticmethod def VerifyPassword(UserUUID, PlainPassword): - """校验用户密码(独立 Password 表) + """校验用户密码 - 注意:形参**不能**叫 `Password` —— 会遮蔽模块级导入的 `Password` 模型 + 以 **User 表自身的登录密码**(bcrypt)为准 —— 与登录流程(LoginView 的 + `User.CheckPassword`)同源,保证「登录能过 = 校验能过」。 + + 旧实现查 `Password` 审计表并用 bcrypt 比对,但该表按设计是**明文审计副本** + (见 ChangePasswordAudit 的说明),bcrypt 比对必然抛 ValueError 被吞成 False, + 导致任何输入都被判为「密码不匹配」。 + + 注意:形参名不能叫 `Password` —— 会遮蔽模块级导入的 `Password` 模型 (`Password.objects` 会变成对 str 取属性,抛 AttributeError)。 Returns: (用户记录存在?, 密码是否匹配) """ - pw_obj = Password.objects.filter(UserUUID=UUIDObj(UserUUID).bytes).first() - if pw_obj is None: + user = AuthManager.GetUserByUUID(UserUUID) + if user is None: return False, False - try: - return True, VerifyPassword(PlainPassword, pw_obj.Password) - except (ValueError, TypeError): + if not PlainPassword: return True, False + # User.CheckPassword 内部已捕获异常并返回 False + return True, bool(user.CheckPassword(PlainPassword)) @staticmethod def SetAccountLicense(UserUUID, License):