Skip to content

Commit e6640e7

Browse files
authored
Fix the merge issues (#87)
* Fix the login log status value. * Fix config information interface constants * Add fuzzy paging query for login logs * Fix fuzzy paging query for query user interface * Fix jwt middleware internal exception not caught
1 parent 61147d4 commit e6640e7

File tree

12 files changed

+80
-36
lines changed

12 files changed

+80
-36
lines changed

backend/app/api/v1/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ async def get_sys_config():
4444
'token_algorithm': settings.TOKEN_ALGORITHM,
4545
'token_expire_seconds': settings.TOKEN_EXPIRE_SECONDS,
4646
'token_swagger_url': settings.TOKEN_URL_SWAGGER,
47-
'log_file_name': settings.LOG_FILE_NAME,
47+
'access_log_filename': settings.LOG_STDOUT_FILENAME,
48+
'error_log_filename': settings.LOG_STDERR_FILENAME,
4849
'middleware_cors': settings.MIDDLEWARE_CORS,
4950
'middleware_gzip': settings.MIDDLEWARE_GZIP,
5051
'middleware_access': settings.MIDDLEWARE_ACCESS,

backend/app/api/v1/login_log.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@
1515
router = APIRouter()
1616

1717

18-
@router.get('', summary='获取所有登录日志', dependencies=[DependsJwtAuth, PageDepends])
19-
async def get_all_login_logs(db: CurrentSession):
20-
log_select = await LoginLogService.get_select()
18+
@router.get('', summary='(模糊条件)分页获取登录日志', dependencies=[DependsJwtAuth, PageDepends])
19+
async def get_all_login_logs(
20+
db: CurrentSession,
21+
username: Annotated[str | None, Query()] = None,
22+
status: Annotated[bool | None, Query()] = None,
23+
ipaddr: Annotated[str | None, Query()] = None,
24+
):
25+
log_select = await LoginLogService.get_select(username=username, status=status, ipaddr=ipaddr)
2126
page_data = await paging_data(db, log_select, GetAllLoginLog)
2227
return response_base.success(data=page_data)
2328

backend/app/api/v1/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def get_all_users(
5757
db: CurrentSession,
5858
username: Annotated[str | None, Query()] = None,
5959
phone: Annotated[str | None, Query()] = None,
60-
status: Annotated[int | None, Query()] = None,
60+
status: Annotated[bool | None, Query()] = None,
6161
):
6262
user_select = await UserService.get_select(username=username, phone=phone, status=status)
6363
page_data = await paging_data(db, user_select, GetAllUserInfo)

backend/app/core/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ def validator_api_url(cls, values):
9191
# Middleware
9292
MIDDLEWARE_CORS: bool = True
9393
MIDDLEWARE_GZIP: bool = True
94-
MIDDLEWARE_JWT_AUTH: bool = True
9594
MIDDLEWARE_ACCESS: bool = False
9695

9796
# Casbin

backend/app/core/registrar.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from backend.app.common.task import scheduler
1414
from backend.app.core.conf import settings
1515
from backend.app.database.db_mysql import create_table
16+
from backend.app.middleware.jwt_auth_middleware import JwtAuthMiddleware
1617
from backend.app.utils.health_check import ensure_unique_route_names
1718
from backend.app.utils.openapi import simplify_operation_ids
1819

@@ -90,32 +91,31 @@ def register_static_file(app: FastAPI):
9091

9192

9293
def register_middleware(app: FastAPI):
93-
# CORS
94-
if settings.MIDDLEWARE_CORS:
95-
from fastapi.middleware.cors import CORSMiddleware
96-
97-
app.add_middleware(
98-
CORSMiddleware,
99-
allow_origins=['*'],
100-
allow_credentials=True,
101-
allow_methods=['*'],
102-
allow_headers=['*'],
103-
)
10494
# Gzip
10595
if settings.MIDDLEWARE_GZIP:
10696
from fastapi.middleware.gzip import GZipMiddleware
10797

10898
app.add_middleware(GZipMiddleware)
109-
# JWT auth
110-
if settings.MIDDLEWARE_JWT_AUTH:
111-
from backend.app.middleware.jwt_auth_middleware import JwtAuthMiddleware
112-
113-
app.add_middleware(AuthenticationMiddleware, backend=JwtAuthMiddleware())
11499
# Api access logs
115100
if settings.MIDDLEWARE_ACCESS:
116101
from backend.app.middleware.access_middleware import AccessMiddleware
117102

118103
app.add_middleware(AccessMiddleware)
104+
# JWT auth: Always open
105+
app.add_middleware(
106+
AuthenticationMiddleware, backend=JwtAuthMiddleware(), on_error=JwtAuthMiddleware.auth_exception_handler
107+
)
108+
# CORS: Always at the end
109+
if settings.MIDDLEWARE_CORS:
110+
from fastapi.middleware.cors import CORSMiddleware
111+
112+
app.add_middleware(
113+
CORSMiddleware,
114+
allow_origins=['*'],
115+
allow_credentials=True,
116+
allow_methods=['*'],
117+
allow_headers=['*'],
118+
)
119119

120120

121121
def register_router(app: FastAPI):

backend/app/crud/crud_login_log.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33
from typing import NoReturn
44

5-
from sqlalchemy import Select, select, desc, delete
5+
from sqlalchemy import Select, select, desc, delete, and_
66
from sqlalchemy.ext.asyncio import AsyncSession
77

88
from backend.app.crud.base import CRUDBase
@@ -11,8 +11,18 @@
1111

1212

1313
class CRUDLoginLog(CRUDBase[LoginLog, CreateLoginLog, UpdateLoginLog]):
14-
async def get_all(self) -> Select:
15-
return select(self.model).order_by(desc(self.model.create_time))
14+
async def get_all(self, username: str = None, status: bool = None, ipaddr: str = None) -> Select:
15+
se = select(self.model).order_by(desc(self.model.create_time))
16+
where_list = []
17+
if username:
18+
where_list.append(self.model.username.like(f'%{username}%'))
19+
if status is not None:
20+
where_list.append(self.model.status == status)
21+
if ipaddr:
22+
where_list.append(self.model.ipaddr.like(f'%{ipaddr}%'))
23+
if where_list:
24+
se = se.where(and_(*where_list))
25+
return se
1626

1727
async def create(self, db: AsyncSession, obj_in: CreateLoginLog) -> NoReturn:
1828
await self.create_(db, obj_in)

backend/app/crud/crud_user.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ async def reset_password(self, db: AsyncSession, pk: int, password: str) -> int:
7070
async def get_all(self, username: str = None, phone: str = None, status: int = None) -> Select:
7171
se = (
7272
select(self.model)
73+
.options(selectinload(self.model.dept))
7374
.options(selectinload(self.model.roles).selectinload(Role.menus))
7475
.order_by(desc(self.model.time_joined))
7576
)
Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
from starlette.authentication import AuthenticationBackend
4-
from fastapi import Request
3+
from typing import Any
4+
5+
from fastapi import Request, Response
6+
from starlette.authentication import AuthenticationBackend, AuthenticationError
7+
from starlette.requests import HTTPConnection
8+
from starlette.responses import JSONResponse
59

610
from backend.app.common import jwt
11+
from backend.app.common.exception.errors import TokenError
12+
from backend.app.core.conf import settings
713
from backend.app.database.db_mysql import async_db_session
814

915

16+
class _AuthenticationError(AuthenticationError):
17+
"""重写内部认证错误类"""
18+
19+
def __init__(self, *, code: int = None, msg: str = None, headers: dict[str, Any] | None = None):
20+
self.code = code
21+
self.msg = msg
22+
self.headers = headers
23+
24+
1025
class JwtAuthMiddleware(AuthenticationBackend):
1126
"""JWT 认证中间件"""
1227

28+
@staticmethod
29+
def auth_exception_handler(conn: HTTPConnection, exc: Exception) -> Response:
30+
"""覆盖内部认证错误处理"""
31+
code = getattr(exc, 'code', 500)
32+
msg = getattr(exc, 'msg', 'Internal Server Error')
33+
return JSONResponse(content={'code': code, 'msg': msg, 'data': None}, status_code=code)
34+
1335
async def authenticate(self, request: Request):
1436
auth = request.headers.get('Authorization')
1537
if not auth:
@@ -19,9 +41,15 @@ async def authenticate(self, request: Request):
1941
if scheme.lower() != 'bearer':
2042
return
2143

22-
sub = await jwt.jwt_authentication(token)
44+
try:
45+
sub = await jwt.jwt_authentication(token)
46+
async with async_db_session() as db:
47+
user = await jwt.get_current_user(db, data=sub)
48+
except TokenError as exc:
49+
raise _AuthenticationError(code=exc.code, msg=exc.detail, headers=exc.headers)
50+
except Exception:
51+
import traceback
2352

24-
async with async_db_session() as db:
25-
user = await jwt.get_current_user(db, data=sub)
53+
raise _AuthenticationError(msg=traceback.format_exc() if settings.ENVIRONMENT == 'dev' else None)
2654

2755
return auth, user

backend/app/models/sys_login_log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class LoginLog(DataClassBase):
1616
id: Mapped[id_key] = mapped_column(init=False)
1717
user_uuid: Mapped[str] = mapped_column(String(50), nullable=False, comment='用户UUID')
1818
username: Mapped[str] = mapped_column(String(20), nullable=False, comment='用户名')
19-
status: Mapped[int] = mapped_column(insert_default=0, comment='登录状态(0失败 1成功)')
19+
status: Mapped[bool] = mapped_column(insert_default=0, comment='登录状态(0失败 1成功)')
2020
ipaddr: Mapped[str] = mapped_column(String(50), nullable=False, comment='登录IP地址')
2121
location: Mapped[str] = mapped_column(String(255), nullable=False, comment='归属地')
2222
browser: Mapped[str] = mapped_column(String(255), nullable=False, comment='浏览器')

backend/app/schemas/login_log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class LoginLogBase(BaseModel):
99
user_uuid: str
1010
username: str
11-
status: int
11+
status: bool
1212
ipaddr: str
1313
location: str
1414
browser: str

backend/app/services/auth_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ async def login(self, *, request: Request, obj: Auth, background_tasks: Backgrou
7171
request=request,
7272
user=current_user,
7373
login_time=self.login_time,
74-
status=LoginLogStatus.fail,
74+
status=LoginLogStatus.fail.value,
7575
msg=e.msg,
7676
)
7777
task = BackgroundTask(LoginLogService.create, **err_log_info)
@@ -84,7 +84,7 @@ async def login(self, *, request: Request, obj: Auth, background_tasks: Backgrou
8484
request=request,
8585
user=user,
8686
login_time=self.login_time,
87-
status=LoginLogStatus.success,
87+
status=LoginLogStatus.success.value,
8888
msg='登录成功',
8989
)
9090
background_tasks.add_task(LoginLogService.create, **log_info)

backend/app/services/login_log_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
class LoginLogService:
2121
@staticmethod
22-
async def get_select() -> Select:
23-
return await LoginLogDao.get_all()
22+
async def get_select(*, username: str, status: bool, ipaddr: str) -> Select:
23+
return await LoginLogDao.get_all(username=username, status=status, ipaddr=ipaddr)
2424

2525
@staticmethod
2626
async def create(

0 commit comments

Comments
 (0)