Skip to content

Commit eb662e4

Browse files
authored
Add user password encryption salt (#191)
* Add user password encryption salt * update the salt logic * Update the SQL files * Add an update admin login permission interface * update the is_staff field comment * update the staff error msg * Update user backend management operation permissions
1 parent 811fd27 commit eb662e4

File tree

10 files changed

+59
-14
lines changed

10 files changed

+59
-14
lines changed

backend/app/api/v1/user.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ async def super_set(request: Request, pk: int):
9494
return await response_base.fail()
9595

9696

97+
@router.put('/{pk}/staff', summary='修改用户后台登录权限', dependencies=[DependsRBAC])
98+
async def staff_set(request: Request, pk: int):
99+
count = await UserService.update_staff(request=request, pk=pk)
100+
if count > 0:
101+
return await response_base.success()
102+
return await response_base.fail()
103+
104+
97105
@router.put('/{pk}/status', summary='修改用户状态', dependencies=[DependsRBAC])
98106
async def status_set(request: Request, pk: int):
99107
count = await UserService.update_status(request=request, pk=pk)

backend/app/common/casbin_rbac.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ async def rbac_verify(self, request: Request, _: dict = DependsJwtAuth) -> None:
4646
user_roles = request.user.roles
4747
if not user_roles:
4848
raise AuthorizationError(msg='用户未分配角色,授权失败')
49+
if not request.user.is_staff:
50+
raise AuthorizationError(msg='此用户已被禁止后台管理操作')
4951
data_scope = any(role.data_scope == 1 for role in user_roles)
5052
if data_scope:
5153
return

backend/app/common/jwt.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ def superuser_verify(request: Request) -> bool:
195195
is_superuser = request.user.is_superuser
196196
if not is_superuser:
197197
raise AuthorizationError(msg='仅管理员有权操作')
198+
if not request.user.is_staff:
199+
raise AuthorizationError(msg='此管理员已被禁止后台管理操作')
198200
return is_superuser
199201

200202

backend/app/crud/crud_user.py

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

6+
from fast_captcha import text_captcha
67
from sqlalchemy import select, update, desc, and_
78
from sqlalchemy.ext.asyncio import AsyncSession
89
from sqlalchemy.orm import selectinload
@@ -29,11 +30,14 @@ async def update_login_time(self, db: AsyncSession, username: str, login_time: d
2930
await db.commit()
3031
return user.rowcount
3132

32-
async def create(self, db: AsyncSession, create: CreateUser) -> NoReturn:
33-
create.password = await jwt.get_hash_password(create.password)
34-
new_user = self.model(**create.dict(exclude={'roles'}))
33+
async def create(self, db: AsyncSession, obj: CreateUser) -> NoReturn:
34+
salt = text_captcha(5)
35+
obj.password = await jwt.get_hash_password(obj.password + salt)
36+
dict_obj = obj.dict(exclude={'roles'})
37+
dict_obj.update({'salt': salt})
38+
new_user = self.model(**dict_obj)
3539
role_list = []
36-
for role_id in create.roles:
40+
for role_id in obj.roles:
3741
role_list.append(await db.get(Role, role_id))
3842
new_user.roles.extend(role_list)
3943
db.add(new_user)
@@ -64,9 +68,9 @@ async def check_email(self, db: AsyncSession, email: str) -> User | None:
6468
mail = await db.execute(select(self.model).where(self.model.email == email))
6569
return mail.scalars().first()
6670

67-
async def reset_password(self, db: AsyncSession, pk: int, password: str) -> int:
71+
async def reset_password(self, db: AsyncSession, pk: int, password: str, salt: str) -> int:
6872
user = await db.execute(
69-
update(self.model).where(self.model.id == pk).values(password=await jwt.get_hash_password(password))
73+
update(self.model).where(self.model.id == pk).values(password=await jwt.get_hash_password(password + salt))
7074
)
7175
return user.rowcount
7276

@@ -94,6 +98,10 @@ async def get_super(self, db: AsyncSession, user_id: int) -> bool:
9498
user = await self.get(db, user_id)
9599
return user.is_superuser
96100

101+
async def get_staff(self, db: AsyncSession, user_id: int) -> bool:
102+
user = await self.get(db, user_id)
103+
return user.is_staff
104+
97105
async def get_status(self, db: AsyncSession, user_id: int) -> bool:
98106
user = await self.get(db, user_id)
99107
return user.status
@@ -109,6 +117,13 @@ async def set_super(self, db: AsyncSession, user_id: int) -> int:
109117
)
110118
return user.rowcount
111119

120+
async def set_staff(self, db: AsyncSession, user_id: int) -> int:
121+
staff_status = await self.get_staff(db, user_id)
122+
user = await db.execute(
123+
update(self.model).where(self.model.id == user_id).values(is_staff=False if staff_status else True)
124+
)
125+
return user.rowcount
126+
112127
async def set_status(self, db: AsyncSession, user_id: int) -> int:
113128
status = await self.get_status(db, user_id)
114129
user = await db.execute(

backend/app/models/sys_user.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ 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] = mapped_column(String(255), comment='密码')
25+
salt: Mapped[str] = mapped_column(String(5), comment='加密盐')
2526
email: Mapped[str] = mapped_column(String(50), unique=True, index=True, comment='邮箱')
2627
is_superuser: Mapped[bool] = mapped_column(default=False, comment='超级权限(0否 1是)')
28+
is_staff: Mapped[bool] = mapped_column(default=False, comment='后台管理登陆(0否 1是)')
2729
status: Mapped[int] = mapped_column(default=1, comment='用户账号状态(0停用 1正常)')
2830
is_multi_login: Mapped[bool] = mapped_column(default=False, comment='是否重复登陆(0否 1是)')
2931
avatar: Mapped[str | None] = mapped_column(String(255), default=None, comment='头像')

backend/app/schemas/role.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
class RoleBase(SchemaBase):
1313
name: str
14-
data_scope: RoleDataScopeType = Field(default=RoleDataScopeType.custom, description='权限范围(1:全部数据权限 2:自定义数据权限)')
14+
data_scope: RoleDataScopeType = Field(
15+
default=RoleDataScopeType.custom, description='权限范围(1:全部数据权限 2:自定义数据权限)'
16+
) # E501
1517
status: StatusType = Field(default=StatusType.enable)
1618
remark: str | None = None
1719

backend/app/services/auth_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async def swagger_login(self, *, form_data: OAuth2PasswordRequestForm):
2929
current_user = await UserDao.get_by_username(db, form_data.username)
3030
if not current_user:
3131
raise errors.NotFoundError(msg='用户不存在')
32-
elif not await jwt.password_verify(form_data.password, current_user.password):
32+
elif not await jwt.password_verify(form_data.password + current_user.salt, current_user.password):
3333
raise errors.AuthorizationError(msg='密码错误')
3434
elif not current_user.status:
3535
raise errors.AuthorizationError(msg='用户已锁定, 登陆失败')
@@ -47,7 +47,7 @@ async def login(self, *, request: Request, obj: AuthLogin, background_tasks: Bac
4747
current_user = await UserDao.get_by_username(db, obj.username)
4848
if not current_user:
4949
raise errors.NotFoundError(msg='用户不存在')
50-
elif not await jwt.password_verify(obj.password, current_user.password):
50+
elif not await jwt.password_verify(obj.password + current_user.salt, current_user.password):
5151
raise errors.AuthorizationError(msg='密码错误')
5252
elif not current_user.status:
5353
raise errors.AuthorizationError(msg='用户已锁定, 登陆失败')

backend/app/services/user_service.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ async def register(*, obj: CreateUser) -> NoReturn:
4040
@staticmethod
4141
async def pwd_reset(*, request: Request, obj: ResetPassword) -> int:
4242
async with async_db_session.begin() as db:
43-
iop = obj.old_password
44-
if not await password_verify(iop, request.user.password):
43+
op = obj.old_password
44+
if not await password_verify(op + request.user.salt, request.user.password):
4545
raise errors.ForbiddenError(msg='旧密码错误')
4646
np1 = obj.new_password
4747
np2 = obj.confirm_password
4848
if np1 != np2:
4949
raise errors.ForbiddenError(msg='两次密码输入不一致')
50-
count = await UserDao.reset_password(db, request.user.id, obj.new_password)
50+
count = await UserDao.reset_password(db, request.user.id, obj.new_password, request.user.salt)
5151
prefix = [
5252
f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}:',
5353
f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{request.user.id}:',
@@ -128,6 +128,18 @@ async def update_permission(*, request: Request, pk: int) -> int:
128128
count = await UserDao.set_super(db, pk)
129129
return count
130130

131+
@staticmethod
132+
async def update_staff(*, request: Request, pk: int) -> int:
133+
async with async_db_session.begin() as db:
134+
await jwt.superuser_verify(request)
135+
if not await UserDao.get(db, pk):
136+
raise errors.NotFoundError(msg='用户不存在')
137+
else:
138+
if pk == request.user.id:
139+
raise errors.ForbiddenError(msg='禁止修改自身后台管理登陆权限')
140+
count = await UserDao.set_staff(db, pk)
141+
return count
142+
131143
@staticmethod
132144
async def update_status(*, request: Request, pk: int) -> int:
133145
async with async_db_session.begin() as db:

backend/sql/create_tables.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,10 @@ CREATE TABLE sys_user
202202
username VARCHAR(20) NOT NULL COMMENT '用户名',
203203
nickname VARCHAR(20) NOT NULL COMMENT '昵称',
204204
password VARCHAR(255) NOT NULL COMMENT '密码',
205+
salt VARCHAR(5) NOT NULL COMMENT '加密盐',
205206
email VARCHAR(50) NOT NULL COMMENT '邮箱',
206207
is_superuser BOOL NOT NULL COMMENT '超级权限(0否 1是)',
208+
is_staff BOOL NOT NULL COMMENT '后台管理登陆(0否 1是)',
207209
status INTEGER NOT NULL COMMENT '用户账号状态(0停用 1正常)',
208210
is_multi_login BOOL NOT NULL COMMENT '是否重复登陆(0否 1是)',
209211
avatar VARCHAR(255) COMMENT '头像',

backend/sql/init_test_data.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ VALUES (1, 'test', 2, 1, null, '2023-06-26 17:13:45', null);
2626
INSERT INTO fba.sys_role_menu (id, role_id, menu_id)
2727
VALUES (1, 1, 1);
2828

29-
INSERT INTO fba.sys_user (id, uuid, username, nickname, password, email, is_superuser, status, is_multi_login, avatar, phone, join_time, last_login_time, dept_id, created_time, updated_time)
30-
VALUES (1, 'af4c804f-3966-4949-ace2-3bb7416ea926', 'test', 'test', '$2b$12$TpdL7kKriqhpJHSBMT.Fr.hJNBx5SdUybi.NT1DV5MojYpV9PpRre', 'test@gmail.com', 1, 1, 0, null, null, '2023-06-26 17:13:45', null, 1, '2023-06-26 17:13:45', null);
29+
INSERT INTO fba.sys_user (id, uuid, username, nickname, password, salt, email, is_superuser, is_staff, status, is_multi_login, avatar, phone, join_time, last_login_time, dept_id, created_time, updated_time)
30+
VALUES (1, 'af4c804f-3966-4949-ace2-3bb7416ea926', 'test', 'test', '$2b$12$eYLKJttq2/e66er1VK7NFezNIYkAu.XTGrJppJpqOW1DHmbcBf5ou', 'NvxNx', 'test@example.com', 1, 1, 1, 0, null, null, '2023-06-26 17:13:45', null, 1, '2023-06-26 17:13:45', null);
3131

3232
INSERT INTO fba.sys_user_role (id, user_id, role_id)
3333
VALUES (1, 1, 1);

0 commit comments

Comments
 (0)