Skip to content

Commit de9b10a

Browse files
authored
Fix token whitelist and new token storage (#220)
* Fix token whitelist and new token storage * Fix logout interface logic
1 parent 10f5fa6 commit de9b10a

File tree

8 files changed

+50
-23
lines changed

8 files changed

+50
-23
lines changed

backend/app/api/v1/auth/auth.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,19 @@ async def user_login(request: Request, obj: AuthLogin, background_tasks: Backgro
4343

4444

4545
@router.post('/new_token', summary='创建新 token', dependencies=[DependsJwtAuth])
46-
async def create_new_token(refresh_token: Annotated[str, Query(...)]):
47-
access_token, access_expire = await AuthService.new_token(refresh_token=refresh_token)
48-
data = GetNewToken(access_token=access_token, access_token_expire_time=access_expire)
46+
async def create_new_token(request: Request, refresh_token: Annotated[str, Query(...)]):
47+
(
48+
new_access_token,
49+
new_refresh_token,
50+
new_access_token_expire_time,
51+
new_refresh_token_expire_time,
52+
) = await AuthService.new_token(request=request, refresh_token=refresh_token)
53+
data = GetNewToken(
54+
access_token=new_access_token,
55+
access_token_expire_time=new_access_token_expire_time,
56+
refresh_token=new_refresh_token,
57+
refresh_token_expire_time=new_refresh_token_expire_time,
58+
)
4959
return await response_base.success(data=data)
5060

5161

backend/app/common/jwt.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,25 @@ async def create_refresh_token(sub: str, expire_time: datetime | None = None, **
9696
return refresh_token, expire
9797

9898

99-
async def create_new_token(sub: str, refresh_token: str, **kwargs) -> tuple[str, datetime]:
99+
async def create_new_token(sub: str, token: str, refresh_token: str, **kwargs) -> tuple[str, str, datetime, datetime]:
100100
"""
101101
Generate new token
102102
103103
:param sub:
104+
:param token
104105
:param refresh_token:
105106
:return:
106107
"""
107108
redis_refresh_token = await redis_client.get(f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{sub}:{refresh_token}')
108109
if not redis_refresh_token or redis_refresh_token != refresh_token:
109110
raise TokenError(msg='refresh_token 已过期')
110-
new_token, expire = await create_access_token(sub, **kwargs)
111-
return new_token, expire
111+
new_access_token, new_access_token_expire_time = await create_access_token(sub, **kwargs)
112+
new_refresh_token, new_refresh_token_expire_time = await create_refresh_token(sub, **kwargs)
113+
token_key = f'{settings.TOKEN_REDIS_PREFIX}:{sub}:{token}'
114+
refresh_token_key = f'{settings.TOKEN_REDIS_PREFIX}:{sub}:{refresh_token}'
115+
await redis_client.delete(token_key)
116+
await redis_client.delete(refresh_token_key)
117+
return new_access_token, new_refresh_token, new_access_token_expire_time, new_refresh_token_expire_time
112118

113119

114120
@sync_to_async

backend/app/common/rbac.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ async def rbac_verify(self, request: Request, _: dict = DependsJwtAuth) -> None:
3434
:param _:
3535
:return:
3636
"""
37+
path = request.url.path
38+
if path in settings.TOKEN_EXCLUDE:
39+
return
3740
# 强制校验 JWT 授权状态
3841
if not request.auth.scopes:
3942
raise TokenError
@@ -71,7 +74,6 @@ async def rbac_verify(self, request: Request, _: dict = DependsJwtAuth) -> None:
7174
else:
7275
# casbin 权限校验
7376
method = request.method
74-
path = request.url.path
7577
forbid_menu_path = [
7678
menu.path for role in user_roles for menu in role.menus if menu.status == StatusType.disable
7779
]

backend/app/core/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def validator_api_url(cls, values):
9999
TOKEN_URL_SWAGGER: str = f'{API_V1_STR}/auth/swagger_login'
100100
TOKEN_REDIS_PREFIX: str = 'fba_token'
101101
TOKEN_REFRESH_REDIS_PREFIX: str = 'fba_refresh_token'
102-
TOKEN_WHITELIST: list[str] = [ # 白名单
102+
TOKEN_EXCLUDE: list[str] = [ # 白名单
103103
f'{API_V1_STR}/auth/login',
104104
]
105105

backend/app/middleware/jwt_auth_middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async def authenticate(self, request: Request):
3636
if not auth:
3737
return
3838

39-
if request.url.path in settings.TOKEN_WHITELIST:
39+
if request.url.path in settings.TOKEN_EXCLUDE:
4040
return
4141

4242
scheme, token = auth.split()

backend/app/schemas/token.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ class GetLoginToken(AccessTokenBase):
2626

2727

2828
class GetNewToken(AccessTokenBase):
29-
pass
29+
refresh_token: str
30+
refresh_token_type: str = 'Bearer'
31+
refresh_token_expire_time: datetime

backend/app/services/auth_service.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,26 @@ async def login(self, *, request: Request, obj: AuthLogin, background_tasks: Bac
9393
return access_token, refresh_token, access_token_expire_time, refresh_token_expire_time, user
9494

9595
@staticmethod
96-
async def new_token(*, refresh_token: str) -> tuple[str, datetime]:
96+
async def new_token(*, request: Request, refresh_token: str) -> tuple[str, str, datetime, datetime]:
9797
user_id = await jwt.jwt_decode(refresh_token)
98+
if str(request.user.id) != user_id:
99+
raise errors.TokenError(msg='刷新 token 无效')
98100
async with async_db_session() as db:
99101
current_user = await UserDao.get(db, user_id)
100102
if not current_user:
101103
raise errors.NotFoundError(msg='用户不存在')
102104
elif not current_user.status:
103-
raise errors.AuthorizationError(msg='用户已锁定, 获取失败')
104-
access_new_token, access_new_token_expire_time = await jwt.create_new_token(
105-
str(current_user.id), refresh_token, multi_login=current_user.is_multi_login
105+
raise errors.AuthorizationError(msg='用户已锁定,操作失败')
106+
current_token = await get_token(request)
107+
(
108+
new_access_token,
109+
new_refresh_token,
110+
new_access_token_expire_time,
111+
new_refresh_token_expire_time,
112+
) = await jwt.create_new_token(
113+
str(current_user.id), current_token, refresh_token, multi_login=current_user.is_multi_login
106114
)
107-
return access_new_token, access_new_token_expire_time
115+
return new_access_token, new_refresh_token, new_access_token_expire_time, new_refresh_token_expire_time
108116

109117
@staticmethod
110118
async def logout(*, request: Request) -> NoReturn:

backend/app/services/user_service.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
from fastapi import Request
66
from sqlalchemy import Select
77

8-
from backend.app.common import jwt
98
from backend.app.common.exception import errors
10-
from backend.app.common.jwt import get_token, password_verify
9+
from backend.app.common.jwt import get_token, password_verify, superuser_verify
1110
from backend.app.common.redis import redis_client
1211
from backend.app.core.conf import settings
1312
from backend.app.crud.crud_dept import DeptDao
@@ -86,7 +85,7 @@ async def get_userinfo(*, username: str) -> User:
8685
@staticmethod
8786
async def update(*, request: Request, username: str, obj: UpdateUser) -> int:
8887
async with async_db_session.begin() as db:
89-
await jwt.superuser_verify(request)
88+
await superuser_verify(request)
9089
input_user = await UserDao.get_with_relation(db, username=username)
9190
if not input_user:
9291
raise errors.NotFoundError(msg='用户不存在')
@@ -139,7 +138,7 @@ async def get_select(*, dept: int, username: str = None, phone: str = None, stat
139138
@staticmethod
140139
async def update_permission(*, request: Request, pk: int) -> int:
141140
async with async_db_session.begin() as db:
142-
await jwt.superuser_verify(request)
141+
await superuser_verify(request)
143142
if not await UserDao.get(db, pk):
144143
raise errors.NotFoundError(msg='用户不存在')
145144
else:
@@ -151,7 +150,7 @@ async def update_permission(*, request: Request, pk: int) -> int:
151150
@staticmethod
152151
async def update_staff(*, request: Request, pk: int) -> int:
153152
async with async_db_session.begin() as db:
154-
await jwt.superuser_verify(request)
153+
await superuser_verify(request)
155154
if not await UserDao.get(db, pk):
156155
raise errors.NotFoundError(msg='用户不存在')
157156
else:
@@ -163,7 +162,7 @@ async def update_staff(*, request: Request, pk: int) -> int:
163162
@staticmethod
164163
async def update_status(*, request: Request, pk: int) -> int:
165164
async with async_db_session.begin() as db:
166-
await jwt.superuser_verify(request)
165+
await superuser_verify(request)
167166
if not await UserDao.get(db, pk):
168167
raise errors.NotFoundError(msg='用户不存在')
169168
else:
@@ -175,7 +174,7 @@ async def update_status(*, request: Request, pk: int) -> int:
175174
@staticmethod
176175
async def update_multi_login(*, request: Request, pk: int) -> int:
177176
async with async_db_session.begin() as db:
178-
await jwt.superuser_verify(request)
177+
await superuser_verify(request)
179178
if not await UserDao.get(db, pk):
180179
raise errors.NotFoundError(msg='用户不存在')
181180
else:
@@ -199,7 +198,7 @@ async def update_multi_login(*, request: Request, pk: int) -> int:
199198
@staticmethod
200199
async def delete(*, request: Request, username: str) -> int:
201200
async with async_db_session.begin() as db:
202-
await jwt.superuser_verify(request)
201+
await superuser_verify(request)
203202
input_user = await UserDao.get_by_username(db, username)
204203
if not input_user:
205204
raise errors.NotFoundError(msg='用户不存在')

0 commit comments

Comments
 (0)