Skip to content

Commit b29a3cb

Browse files
authored
Update user password encryption method (#463)
1 parent a15693c commit b29a3cb

File tree

8 files changed

+35
-33
lines changed

8 files changed

+35
-33
lines changed

backend/app/admin/crud/crud_user.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
from fast_captcha import text_captcha
3+
import bcrypt
4+
45
from sqlalchemy import and_, desc, select
56
from sqlalchemy.ext.asyncio import AsyncSession
67
from sqlalchemy.orm import selectinload
@@ -70,8 +71,8 @@ async def create(self, db: AsyncSession, obj: RegisterUserParam, *, social: bool
7071
:return:
7172
"""
7273
if not social:
73-
salt = text_captcha(5)
74-
obj.password = get_hash_password(f'{obj.password}{salt}')
74+
salt = bcrypt.gensalt()
75+
obj.password = get_hash_password(f'{obj.password}', salt)
7576
dict_obj = obj.model_dump()
7677
dict_obj.update({'is_staff': True, 'salt': salt})
7778
else:
@@ -88,8 +89,8 @@ async def add(self, db: AsyncSession, obj: AddUserParam) -> None:
8889
:param obj:
8990
:return:
9091
"""
91-
salt = text_captcha(5)
92-
obj.password = get_hash_password(f'{obj.password}{salt}')
92+
salt = bcrypt.gensalt()
93+
obj.password = get_hash_password(f'{obj.password}', salt)
9394
dict_obj = obj.model_dump(exclude={'roles'})
9495
dict_obj.update({'salt': salt})
9596
new_user = self.model(**dict_obj)

backend/app/admin/model/sys_user.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime
44
from typing import Union
55

6-
from sqlalchemy import ForeignKey, String
6+
from sqlalchemy import VARBINARY, ForeignKey, String
77
from sqlalchemy.orm import Mapped, mapped_column, relationship
88

99
from backend.app.admin.model.sys_user_role import sys_user_role
@@ -22,7 +22,7 @@ class User(Base):
2222
username: Mapped[str] = mapped_column(String(20), unique=True, index=True, comment='用户名')
2323
nickname: Mapped[str] = mapped_column(String(20), unique=True, comment='昵称')
2424
password: Mapped[str | None] = mapped_column(String(255), comment='密码')
25-
salt: Mapped[str | None] = mapped_column(String(5), comment='加密盐')
25+
salt: Mapped[bytes | None] = mapped_column(VARBINARY(255), comment='加密盐')
2626
email: Mapped[str] = mapped_column(String(50), unique=True, index=True, comment='邮箱')
2727
is_superuser: Mapped[bool] = mapped_column(default=False, comment='超级权限(0否 1是)')
2828
is_staff: Mapped[bool] = mapped_column(default=False, comment='后台管理登陆(0否 1是)')

backend/app/admin/service/auth_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def swagger_login(*, obj: HTTPBasicCredentials) -> tuple[str, User]:
3434
current_user = await user_dao.get_by_username(db, obj.username)
3535
if not current_user:
3636
raise errors.NotFoundError(msg='用户名或密码有误')
37-
elif not password_verify(f'{obj.password}{current_user.salt}', current_user.password):
37+
elif not password_verify(f'{obj.password}', current_user.password):
3838
raise errors.AuthorizationError(msg='用户名或密码有误')
3939
elif not current_user.status:
4040
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')
@@ -53,7 +53,7 @@ async def login(
5353
raise errors.NotFoundError(msg='用户名或密码有误')
5454
user_uuid = current_user.uuid
5555
username = current_user.username
56-
if not password_verify(obj.password + current_user.salt, current_user.password):
56+
if not password_verify(obj.password, current_user.password):
5757
raise errors.AuthorizationError(msg='用户名或密码有误')
5858
elif not current_user.status:
5959
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')

backend/app/admin/service/user_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ async def add(*, request: Request, obj: AddUserParam) -> None:
7171
async def pwd_reset(*, request: Request, obj: ResetPasswordParam) -> int:
7272
async with async_db_session.begin() as db:
7373
user = await user_dao.get(db, request.user.id)
74-
if not password_verify(f'{obj.old_password}{user.salt}', user.password):
74+
if not password_verify(f'{obj.old_password}', user.password):
7575
raise errors.ForbiddenError(msg='原密码错误')
7676
np1 = obj.new_password
7777
np2 = obj.confirm_password
7878
if np1 != np2:
7979
raise errors.ForbiddenError(msg='密码输入不一致')
80-
new_pwd = get_hash_password(f'{obj.new_password}{user.salt}')
80+
new_pwd = get_hash_password(f'{obj.new_password}', user.salt)
8181
count = await user_dao.reset_password(db, request.user.id, new_pwd)
8282
key_prefix = [
8383
f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}',

backend/common/security/jwt.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from fastapi.security import HTTPBearer
77
from fastapi.security.utils import get_authorization_scheme_param
88
from jose import ExpiredSignatureError, JWTError, jwt
9-
from passlib.context import CryptContext
9+
from pwdlib import PasswordHash
10+
from pwdlib.hashers.bcrypt import BcryptHasher
1011
from pydantic_core import from_json
1112
from sqlalchemy.ext.asyncio import AsyncSession
1213

@@ -20,21 +21,21 @@
2021
from backend.utils.serializers import select_as_dict
2122
from backend.utils.timezone import timezone
2223

23-
pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
24-
25-
2624
# JWT authorizes dependency injection
2725
DependsJwtAuth = Depends(HTTPBearer())
2826

27+
password_hash = PasswordHash((BcryptHasher(),))
28+
2929

30-
def get_hash_password(password: str) -> str:
30+
def get_hash_password(password: str, salt: bytes | None) -> str:
3131
"""
3232
Encrypt passwords using the hash algorithm
3333
3434
:param password:
35+
:param salt:
3536
:return:
3637
"""
37-
return pwd_context.hash(password)
38+
return password_hash.hash(password, salt=salt)
3839

3940

4041
def password_verify(plain_password: str, hashed_password: str) -> bool:
@@ -45,7 +46,7 @@ def password_verify(plain_password: str, hashed_password: str) -> bool:
4546
:param hashed_password: The hash ciphers to compare
4647
:return:
4748
"""
48-
return pwd_context.verify(plain_password, hashed_password)
49+
return password_hash.verify(plain_password, hashed_password)
4950

5051

5152
async def create_access_token(sub: str, multi_login: bool) -> AccessToken:

backend/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies = [
2727
"itsdangerous>=2.2.0",
2828
"loguru>=0.7.2",
2929
"msgspec>=0.18.6",
30-
"passlib>=1.7.4",
30+
"pwdlib>=0.2.1",
3131
"path==17.0.0",
3232
"phonenumbers>=8.13.0",
3333
"psutil>=6.0.0",
@@ -46,7 +46,7 @@ dependencies = [
4646
# https://github.com/celery/celery/issues/7874
4747
"celery-aio-pool==0.1.0rc7",
4848
"asgi-correlation-id>=4.3.3",
49-
"python-socketio[asyncio]>=5.11.4",
49+
"python-socketio>=5.11.4",
5050
]
5151

5252
[dependency-groups]

backend/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ msgspec==0.18.6
6060
nodeenv==1.9.1
6161
orjson==3.10.7
6262
packaging==24.1
63-
passlib==1.7.4
6463
path==17.0.0
6564
phonenumbers==8.13.27
6665
pillow==10.4.0
@@ -70,6 +69,7 @@ pre-commit==4.0.1
7069
prometheus-client==0.21.0
7170
prompt-toolkit==3.0.48
7271
psutil==6.1.0
72+
pwdlib==0.2.1
7373
pyasn1==0.6.1
7474
pycparser==2.22 ; platform_python_implementation != 'PyPy'
7575
pydantic==2.9.1

backend/uv.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)