115 lines
4.2 KiB
Python
115 lines
4.2 KiB
Python
|
||
from fastapi import APIRouter, HTTPException, Depends
|
||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||
from app.models.models import LoginRequest, LoginResponse
|
||
from app.core.database import get_db_connection
|
||
from app.services.jwt_service import jwt_service
|
||
from app.core.auth import get_current_user
|
||
import hashlib
|
||
|
||
security = HTTPBearer()
|
||
|
||
router = APIRouter()
|
||
|
||
def hash_password(password: str) -> str:
|
||
return hashlib.sha256(password.encode()).hexdigest()
|
||
|
||
@router.post("/auth/login", response_model=LoginResponse)
|
||
def login(request: LoginRequest):
|
||
with get_db_connection() as connection:
|
||
cursor = connection.cursor(dictionary=True)
|
||
|
||
query = "SELECT user_id, username, caption, email, password_hash FROM users WHERE username = %s"
|
||
cursor.execute(query, (request.username,))
|
||
user = cursor.fetchone()
|
||
|
||
if not user:
|
||
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||
|
||
hashed_input = hash_password(request.password)
|
||
if user['password_hash'] != hashed_input and user['password_hash'] != request.password:
|
||
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
||
|
||
# 创建JWT token
|
||
token_data = {
|
||
"user_id": user['user_id'],
|
||
"username": user['username'],
|
||
"caption": user['caption']
|
||
}
|
||
token = jwt_service.create_access_token(token_data)
|
||
|
||
return LoginResponse(
|
||
user_id=user['user_id'],
|
||
username=user['username'],
|
||
caption=user['caption'],
|
||
email=user['email'],
|
||
token=token
|
||
)
|
||
|
||
@router.post("/auth/logout")
|
||
def logout(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||
"""登出接口,撤销当前token"""
|
||
token = credentials.credentials
|
||
|
||
# 验证token并获取用户信息(不查询数据库)
|
||
payload = jwt_service.verify_token(token)
|
||
if not payload:
|
||
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||
|
||
user_id = payload.get("user_id")
|
||
if not user_id:
|
||
raise HTTPException(status_code=401, detail="Invalid token payload")
|
||
|
||
# 撤销当前token
|
||
revoked = jwt_service.revoke_token(token, user_id)
|
||
|
||
if revoked:
|
||
return {"message": "Logged out successfully"}
|
||
else:
|
||
return {"message": "Already logged out or token not found"}
|
||
|
||
@router.post("/auth/logout-all")
|
||
def logout_all(current_user: dict = Depends(get_current_user)):
|
||
"""登出所有设备"""
|
||
user_id = current_user['user_id']
|
||
revoked_count = jwt_service.revoke_all_user_tokens(user_id)
|
||
return {"message": f"Logged out from {revoked_count} devices"}
|
||
|
||
@router.post("/auth/admin/revoke-user-tokens/{user_id}")
|
||
def admin_revoke_user_tokens(user_id: int, credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||
"""管理员功能:撤销指定用户的所有token"""
|
||
token = credentials.credentials
|
||
|
||
# 验证管理员token(不查询数据库)
|
||
payload = jwt_service.verify_token(token)
|
||
if not payload:
|
||
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||
|
||
admin_user_id = payload.get("user_id")
|
||
if not admin_user_id:
|
||
raise HTTPException(status_code=401, detail="Invalid token payload")
|
||
|
||
# 这里可以添加管理员权限检查,目前暂时允许任何登录用户操作
|
||
# if not payload.get('is_admin'):
|
||
# raise HTTPException(status_code=403, detail="需要管理员权限")
|
||
|
||
revoked_count = jwt_service.revoke_all_user_tokens(user_id)
|
||
return {"message": f"Revoked {revoked_count} tokens for user {user_id}"}
|
||
|
||
@router.get("/auth/me")
|
||
def get_me(current_user: dict = Depends(get_current_user)):
|
||
"""获取当前用户信息"""
|
||
return current_user
|
||
|
||
@router.post("/auth/refresh")
|
||
def refresh_token(current_user: dict = Depends(get_current_user)):
|
||
"""刷新token"""
|
||
# 这里需要从请求中获取当前token,为简化先返回新token
|
||
token_data = {
|
||
"user_id": current_user['user_id'],
|
||
"username": current_user['username'],
|
||
"caption": current_user['caption']
|
||
}
|
||
new_token = jwt_service.create_access_token(token_data)
|
||
return {"token": new_token}
|