Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions backend/app/admin/crud/crud_user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fast_captcha import text_captcha
import bcrypt

from sqlalchemy import and_, desc, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
Expand Down Expand Up @@ -70,8 +71,8 @@ async def create(self, db: AsyncSession, obj: RegisterUserParam, *, social: bool
:return:
"""
if not social:
salt = text_captcha(5)
obj.password = get_hash_password(f'{obj.password}{salt}')
salt = bcrypt.gensalt()
obj.password = get_hash_password(f'{obj.password}', salt)
dict_obj = obj.model_dump()
dict_obj.update({'is_staff': True, 'salt': salt})
else:
Expand All @@ -88,8 +89,8 @@ async def add(self, db: AsyncSession, obj: AddUserParam) -> None:
:param obj:
:return:
"""
salt = text_captcha(5)
obj.password = get_hash_password(f'{obj.password}{salt}')
salt = bcrypt.gensalt()
obj.password = get_hash_password(f'{obj.password}', salt)
dict_obj = obj.model_dump(exclude={'roles'})
dict_obj.update({'salt': salt})
new_user = self.model(**dict_obj)
Expand Down
4 changes: 2 additions & 2 deletions backend/app/admin/model/sys_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import datetime
from typing import Union

from sqlalchemy import ForeignKey, String
from sqlalchemy import VARBINARY, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column, relationship

from backend.app.admin.model.sys_user_role import sys_user_role
Expand All @@ -22,7 +22,7 @@ class User(Base):
username: Mapped[str] = mapped_column(String(20), unique=True, index=True, comment='用户名')
nickname: Mapped[str] = mapped_column(String(20), unique=True, comment='昵称')
password: Mapped[str | None] = mapped_column(String(255), comment='密码')
salt: Mapped[str | None] = mapped_column(String(5), comment='加密盐')
salt: Mapped[bytes | None] = mapped_column(VARBINARY(255), comment='加密盐')
email: Mapped[str] = mapped_column(String(50), unique=True, index=True, comment='邮箱')
is_superuser: Mapped[bool] = mapped_column(default=False, comment='超级权限(0否 1是)')
is_staff: Mapped[bool] = mapped_column(default=False, comment='后台管理登陆(0否 1是)')
Expand Down
4 changes: 2 additions & 2 deletions backend/app/admin/service/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async def swagger_login(*, obj: HTTPBasicCredentials) -> tuple[str, User]:
current_user = await user_dao.get_by_username(db, obj.username)
if not current_user:
raise errors.NotFoundError(msg='用户名或密码有误')
elif not password_verify(f'{obj.password}{current_user.salt}', current_user.password):
elif not password_verify(f'{obj.password}', current_user.password):
raise errors.AuthorizationError(msg='用户名或密码有误')
elif not current_user.status:
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')
Expand All @@ -53,7 +53,7 @@ async def login(
raise errors.NotFoundError(msg='用户名或密码有误')
user_uuid = current_user.uuid
username = current_user.username
if not password_verify(obj.password + current_user.salt, current_user.password):
if not password_verify(obj.password, current_user.password):
raise errors.AuthorizationError(msg='用户名或密码有误')
elif not current_user.status:
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')
Expand Down
4 changes: 2 additions & 2 deletions backend/app/admin/service/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ async def add(*, request: Request, obj: AddUserParam) -> None:
async def pwd_reset(*, request: Request, obj: ResetPasswordParam) -> int:
async with async_db_session.begin() as db:
user = await user_dao.get(db, request.user.id)
if not password_verify(f'{obj.old_password}{user.salt}', user.password):
if not password_verify(f'{obj.old_password}', user.password):
raise errors.ForbiddenError(msg='原密码错误')
np1 = obj.new_password
np2 = obj.confirm_password
if np1 != np2:
raise errors.ForbiddenError(msg='密码输入不一致')
new_pwd = get_hash_password(f'{obj.new_password}{user.salt}')
new_pwd = get_hash_password(f'{obj.new_password}', user.salt)
count = await user_dao.reset_password(db, request.user.id, new_pwd)
key_prefix = [
f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}',
Expand Down
15 changes: 8 additions & 7 deletions backend/common/security/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from fastapi.security import HTTPBearer
from fastapi.security.utils import get_authorization_scheme_param
from jose import ExpiredSignatureError, JWTError, jwt
from passlib.context import CryptContext
from pwdlib import PasswordHash
from pwdlib.hashers.bcrypt import BcryptHasher
from pydantic_core import from_json
from sqlalchemy.ext.asyncio import AsyncSession

Expand All @@ -20,21 +21,21 @@
from backend.utils.serializers import select_as_dict
from backend.utils.timezone import timezone

pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')


# JWT authorizes dependency injection
DependsJwtAuth = Depends(HTTPBearer())

password_hash = PasswordHash((BcryptHasher(),))


def get_hash_password(password: str) -> str:
def get_hash_password(password: str, salt: bytes | None) -> str:
"""
Encrypt passwords using the hash algorithm
:param password:
:param salt:
:return:
"""
return pwd_context.hash(password)
return password_hash.hash(password, salt=salt)


def password_verify(plain_password: str, hashed_password: str) -> bool:
Expand All @@ -45,7 +46,7 @@ def password_verify(plain_password: str, hashed_password: str) -> bool:
:param hashed_password: The hash ciphers to compare
:return:
"""
return pwd_context.verify(plain_password, hashed_password)
return password_hash.verify(plain_password, hashed_password)


async def create_access_token(sub: str, multi_login: bool) -> AccessToken:
Expand Down
4 changes: 2 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependencies = [
"itsdangerous>=2.2.0",
"loguru>=0.7.2",
"msgspec>=0.18.6",
"passlib>=1.7.4",
"pwdlib>=0.2.1",
"path==17.0.0",
"phonenumbers>=8.13.0",
"psutil>=6.0.0",
Expand All @@ -46,7 +46,7 @@ dependencies = [
# https://github.com/celery/celery/issues/7874
"celery-aio-pool==0.1.0rc7",
"asgi-correlation-id>=4.3.3",
"python-socketio[asyncio]>=5.11.4",
"python-socketio>=5.11.4",
]

[dependency-groups]
Expand Down
2 changes: 1 addition & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ msgspec==0.18.6
nodeenv==1.9.1
orjson==3.10.7
packaging==24.1
passlib==1.7.4
path==17.0.0
phonenumbers==8.13.27
pillow==10.4.0
Expand All @@ -70,6 +69,7 @@ pre-commit==4.0.1
prometheus-client==0.21.0
prompt-toolkit==3.0.48
psutil==6.1.0
pwdlib==0.2.1
pyasn1==0.6.1
pycparser==2.22 ; platform_python_implementation != 'PyPy'
pydantic==2.9.1
Expand Down
24 changes: 12 additions & 12 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.