Skip to content

Commit dc934e7

Browse files
perf: login module
1 parent 0515075 commit dc934e7

22 files changed

Lines changed: 411 additions & 92 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,5 @@ cython_debug/
181181

182182
# PyPI configuration file
183183
.pypirc
184+
185+
.DS_Store

backend/apps/system/api/login.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
from fastapi import APIRouter, HTTPException
2-
from apps.system.schemas.auth import LocalLoginSchema
1+
from typing import Annotated
2+
from fastapi import APIRouter, Depends, HTTPException
3+
from fastapi.security import OAuth2PasswordRequestForm
34
from common.core.deps import SessionDep
45
from ..crud.user import authenticate
56
from common.core.security import create_access_token
67
from datetime import timedelta
78
from common.core.config import settings
9+
from common.core.schemas import Token
10+
router = APIRouter(tags=["login"], prefix="/login")
811

9-
router = APIRouter(tags=["login"])
10-
11-
@router.post("/localLogin")
12+
@router.post("/access-token")
1213
def local_login(
1314
session: SessionDep,
14-
schema: LocalLoginSchema,
15-
) -> str:
16-
user = authenticate(session=session, account=schema.account, password=schema.password)
15+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
16+
) -> Token:
17+
user = authenticate(session=session, account=form_data.username, password=form_data.password)
1718
if not user:
1819
raise HTTPException(status_code=400, detail="Incorrect account or password")
1920
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
20-
return create_access_token(
21-
user.id, expires_delta=access_token_expires
22-
)
21+
user_dict = user.to_dict()
22+
return Token(access_token=create_access_token(
23+
user_dict, expires_delta=access_token_expires
24+
))

backend/apps/system/api/user.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@
55

66

77
@router.get("/info")
8-
async def user_info():
9-
return {
10-
"name": "admin"
11-
}
8+
async def user_info(current_user: CurrentUser):
9+
return current_user.to_dict()

backend/apps/system/middleware/__init__.py

Whitespace-only changes.

backend/apps/system/middleware/token.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

backend/apps/system/models/user.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,10 @@ class sys_user(SQLModel, table=True):
66
account: str = Field(max_length=255, unique=True)
77
password: str = Field(max_length=255)
88
oid: int = Field(default=1)
9+
10+
def to_dict(self):
11+
return {
12+
"id": self.id,
13+
"account": self.account,
14+
"oid": self.oid
15+
}

backend/common/core/deps.py

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,43 @@
22

33
import jwt
44
from fastapi import Depends, HTTPException, status
5-
from fastapi.security import OAuth2PasswordBearer
5+
# from fastapi.security import OAuth2PasswordBearer
66
from jwt.exceptions import InvalidTokenError
7-
from pydantic import ValidationError
7+
from pydantic import BaseModel, ValidationError
88
from sqlmodel import Session
9-
9+
from common.core.schemas import TokenPayload, XOAuth2PasswordBearer
1010
from common.core import security
1111
from common.core.config import settings
1212
from common.core.db import get_session
1313
from apps.system.models.user import sys_user
14-
import threading
15-
""" reusable_oauth2 = OAuth2PasswordBearer(
14+
reusable_oauth2 = XOAuth2PasswordBearer(
1615
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
1716
)
18-
"""
1917

20-
thread_local = threading.local()
18+
19+
2120

2221
SessionDep = Annotated[Session, Depends(get_session)]
23-
# TokenDep = Annotated[str, Depends(reusable_oauth2)]
22+
TokenDep = Annotated[str, Depends(reusable_oauth2)]
2423

25-
def get_current_user(session: SessionDep, token: str) -> sys_user:
24+
def get_current_user(session: SessionDep, token: TokenDep) -> sys_user:
2625
try:
2726
payload = jwt.decode(
2827
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
2928
)
30-
# token_data = TokenPayload(**payload)
29+
token_data = TokenPayload(**payload)
3130
except (InvalidTokenError, ValidationError):
3231
raise HTTPException(
3332
status_code=status.HTTP_403_FORBIDDEN,
3433
detail="Could not validate credentials",
3534
)
36-
user = session.get(sys_user, payload.sub)
35+
user = session.get(sys_user, token_data.id)
3736
if not user:
3837
raise HTTPException(status_code=404, detail="User not found")
3938
""" if not user.is_active:
4039
raise HTTPException(status_code=400, detail="Inactive user") """
41-
thread_local.current_user = user
4240
return user
43-
def get_current_user_from_thread():
44-
return getattr(thread_local, "current_user", None)
45-
CurrentUser = Annotated[sys_user, Depends(get_current_user_from_thread)]
46-
""" def get_current_user(session: SessionDep, token: TokenDep) -> User:
47-
try:
48-
payload = jwt.decode(
49-
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
50-
)
51-
token_data = TokenPayload(**payload)
52-
except (InvalidTokenError, ValidationError):
53-
raise HTTPException(
54-
status_code=status.HTTP_403_FORBIDDEN,
55-
detail="Could not validate credentials",
56-
)
57-
user = session.get(User, token_data.sub)
58-
if not user:
59-
raise HTTPException(status_code=404, detail="User not found")
60-
if not user.is_active:
61-
raise HTTPException(status_code=400, detail="Inactive user")
62-
return user
63-
41+
CurrentUser = Annotated[sys_user, Depends(get_current_user)]
6442

65-
CurrentUser = Annotated[User, Depends(get_current_user)]
6643

6744

68-
def get_current_active_superuser(current_user: CurrentUser) -> User:
69-
if not current_user.is_superuser:
70-
raise HTTPException(
71-
status_code=403, detail="The user doesn't have enough privileges"
72-
)
73-
return current_user """

backend/common/core/schemas.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Optional
2+
from fastapi import HTTPException, Request
3+
from fastapi.security import OAuth2PasswordBearer
4+
from pydantic import BaseModel
5+
from sqlmodel import SQLModel
6+
from starlette.status import HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED
7+
from common.core.config import settings
8+
from fastapi.security.utils import get_authorization_scheme_param
9+
class TokenPayload(BaseModel):
10+
account: str | None = None
11+
id: int | None = None
12+
oid: int | None = None
13+
14+
class Token(SQLModel):
15+
access_token: str
16+
token_type: str = "bearer"
17+
18+
class XOAuth2PasswordBearer(OAuth2PasswordBearer):
19+
async def __call__(self, request: Request) -> Optional[str]:
20+
authorization = request.headers.get(settings.TOKEN_KEY)
21+
scheme, param = get_authorization_scheme_param(authorization)
22+
if not authorization or scheme.lower() != "bearer":
23+
if self.auto_error:
24+
raise HTTPException(
25+
status_code=HTTP_401_UNAUTHORIZED,
26+
detail="Not authenticated",
27+
headers={"WWW-Authenticate": "Bearer"},
28+
)
29+
else:
30+
return None
31+
return param

backend/common/core/security.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
ALGORITHM = "HS256"
1313

1414

15-
def create_access_token(subject: str | Any, expires_delta: timedelta) -> str:
15+
def create_access_token(data: dict | Any, expires_delta: timedelta) -> str:
16+
to_encode = data.copy()
1617
expire = datetime.now(timezone.utc) + expires_delta
17-
to_encode = {"exp": expire, "account": str(subject)}
18+
to_encode.update({"exp": expire})
19+
# to_encode = {"exp": expire, "account": str(subject)}
1820
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
1921
return encoded_jwt
2022

backend/main.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
from fastapi import FastAPI
33
from fastapi.routing import APIRoute
44
from starlette.middleware.cors import CORSMiddleware
5-
from starlette.middleware.base import BaseHTTPMiddleware
65
from apps.api import api_router
76
from common.core.config import settings
87

9-
from apps.system.middleware.token import verify_token
108

119
def custom_generate_unique_id(route: APIRoute) -> str:
1210
return f"{route.tags[0]}-{route.name}"
@@ -32,7 +30,6 @@ def custom_generate_unique_id(route: APIRoute) -> str:
3230
)
3331

3432
app.include_router(api_router, prefix=settings.API_V1_STR)
35-
# app.add_middleware(BaseHTTPMiddleware, dispatch=verify_token)
3633

3734
if __name__ == "__main__":
3835
import uvicorn

0 commit comments

Comments
 (0)