From d769dec2ec5203b40f4a53946220a0bcce128bd8 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 18 Jun 2026 19:02:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=20UserPassword=20?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E6=98=8E=E6=96=87=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/view.py | 9 ++++-- gvsdsdk/management/commands/hash_passwords.py | 28 +++++++++++++++++++ gvsdsdk/models.py | 22 +++++++++------ users/views.py | 17 ++++++++--- 4 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 gvsdsdk/management/commands/hash_passwords.py diff --git a/backend/view.py b/backend/view.py index f7d61fd..36e7334 100644 --- a/backend/view.py +++ b/backend/view.py @@ -509,11 +509,12 @@ class ModifyAdminUserView(APIView): if password: if len(password) < 6: return Response({'code': 400, 'msg': '密码至少6位'}) - target_user.password = password + target_user.SetPassword(password) if erjimima: if len(erjimima) < 6: return Response({'code': 400, 'msg': '二级密码至少6位'}) - target_kefu.erjimima = erjimima # 假设二级密码也需要加密 + import bcrypt as _bcrypt + target_kefu.erjimima = _bcrypt.hashpw(erjimima.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8') target_user.save() target_kefu.save() return Response({'code': 0, 'msg': '修改成功'}) @@ -602,10 +603,12 @@ class AddAdminUserView(APIView): user_main.SetPassword(password) user_main.save(update_fields=['UserPassword']) # 创建客服扩展表(后台用户扩展) + import bcrypt as _bcrypt + erjimima_hashed = _bcrypt.hashpw(erjimima.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8') UserKefu.query.create( user=user_main, nicheng=nicheng, - erjimima=erjimima, + erjimima=erjimima_hashed, zhuangtai=1 ) # 分配客服角色 diff --git a/gvsdsdk/management/commands/hash_passwords.py b/gvsdsdk/management/commands/hash_passwords.py new file mode 100644 index 0000000..30647c4 --- /dev/null +++ b/gvsdsdk/management/commands/hash_passwords.py @@ -0,0 +1,28 @@ +"""将 User 表中明文密码迁移为 bcrypt 哈希""" +from django.core.management.base import BaseCommand +from gvsdsdk.models import User + + +class Command(BaseCommand): + help = '将 User 表中所有明文密码迁移为 bcrypt 哈希' + + def handle(self, *args, **options): + # 筛选非空且非 bcrypt 哈希的密码 + users = User.objects.exclude(UserPassword='') + updated = 0 + skipped = 0 + + for user in users.iterator(): + stored = user.UserPassword or '' + # 已是 bcrypt 哈希则跳过 + if stored.startswith(('$2b$', '$2a$')) and len(stored) == 60: + skipped += 1 + continue + # 明文密码 → bcrypt 哈希 + user.SetPassword(stored) + user.save(update_fields=['UserPassword']) + updated += 1 + + self.stdout.write(self.style.SUCCESS( + f'完成:{updated} 条明文密码已转为 bcrypt,{skipped} 条已是哈希跳过' + )) diff --git a/gvsdsdk/models.py b/gvsdsdk/models.py index afab338..85782c3 100644 --- a/gvsdsdk/models.py +++ b/gvsdsdk/models.py @@ -256,15 +256,21 @@ class User(QModel): self.UserPassword = hashed.decode('utf-8') def CheckPassword(self, RawPassword): - """验证密码""" + """验证密码——兼容 bcrypt 哈希和明文存储""" import bcrypt - try: - return bcrypt.checkpw( - RawPassword.encode('utf-8'), - self.UserPassword.encode('utf-8') - ) - except Exception: - return False + stored = self.UserPassword or '' + # bcrypt 哈希以 $2b$ 或 $2a$ 开头,长度固定 60 + if stored.startswith(('$2b$', '$2a$')) and len(stored) == 60: + try: + return bcrypt.checkpw( + RawPassword.encode('utf-8'), + stored.encode('utf-8') + ) + except Exception: + return False + else: + # 明文密码(旧数据兼容) + return stored == RawPassword def GetTele(self): """获取电话,空则返回默认文本""" diff --git a/users/views.py b/users/views.py index ab198d9..c9b59e4 100644 --- a/users/views.py +++ b/users/views.py @@ -6757,8 +6757,13 @@ class KefuLoginView(APIView): if not user.CheckPassword(password): continue - # 验证二级密码(明文比较) - if kefu.erjimima != erjimima: + # 验证二级密码(兼容 bcrypt 哈希和明文) + stored_erji = kefu.erjimima or '' + if stored_erji.startswith(('$2b$', '$2a$')) and len(stored_erji) == 60: + import bcrypt as _bcrypt + if not _bcrypt.checkpw(erjimima.encode('utf-8'), stored_erji.encode('utf-8')): + continue + elif stored_erji != erjimima: continue # 如果账号被禁用,记录但继续查找(可能还有其他正常账号?) @@ -10353,10 +10358,12 @@ class AdKftjView(APIView): user.SetPassword(password) user.save(update_fields=['UserPassword']) # 创建客服扩展表 + import bcrypt as _bcrypt + erjimima_hashed = _bcrypt.hashpw(erjimima.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8') kefu = UserKefu.query.create( user=user, nicheng=nicheng or f'客服{user.yonghuid}', - erjimima=erjimima, # 建议实际项目中加密存储 + erjimima=erjimima_hashed, zhuangtai=1, jinrichuli=0, jinyuechuli=0, @@ -10498,7 +10505,9 @@ class AdKfxgView(APIView): if 'nicheng' in request.data: kefu.nicheng = request.data['nicheng'] if 'erjimima' in request.data: - kefu.erjimima = request.data['erjimima'] + import bcrypt as _bcrypt + new_erji = request.data['erjimima'] + kefu.erjimima = _bcrypt.hashpw(new_erji.encode('utf-8'), _bcrypt.gensalt(rounds=12)).decode('utf-8') if 'zhuangtai' in request.data: kefu.zhuangtai = int(request.data['zhuangtai']) kefu.save()