2025-09-26 07:47:37 +00:00
|
|
|
|
import hashlib
|
|
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
2025-12-26 08:58:36 +00:00
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
2025-09-26 07:47:37 +00:00
|
|
|
|
from fastapi.responses import JSONResponse
|
2025-08-29 08:37:29 +00:00
|
|
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
2025-09-26 07:47:37 +00:00
|
|
|
|
|
|
|
|
|
|
from app.core.auth import get_current_user
|
2025-08-05 01:46:40 +00:00
|
|
|
|
from app.core.database import get_db_connection
|
2025-09-26 07:47:37 +00:00
|
|
|
|
from app.models.models import LoginRequest, LoginResponse
|
2025-08-29 08:37:29 +00:00
|
|
|
|
from app.services.jwt_service import jwt_service
|
2025-09-26 07:47:37 +00:00
|
|
|
|
from app.core.response import create_api_response
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
security = HTTPBearer()
|
2025-08-05 01:46:40 +00:00
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
def hash_password(password: str) -> str:
|
|
|
|
|
|
return hashlib.sha256(password.encode()).hexdigest()
|
|
|
|
|
|
|
2025-09-26 07:47:37 +00:00
|
|
|
|
@router.post("/auth/login")
|
2025-12-26 08:58:36 +00:00
|
|
|
|
def login(request_body: LoginRequest, request: Request):
|
2025-08-05 01:46:40 +00:00
|
|
|
|
with get_db_connection() as connection:
|
|
|
|
|
|
cursor = connection.cursor(dictionary=True)
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
2025-09-11 05:16:58 +00:00
|
|
|
|
query = "SELECT user_id, username, caption, email, password_hash, role_id FROM users WHERE username = %s"
|
2025-12-26 08:58:36 +00:00
|
|
|
|
cursor.execute(query, (request_body.username,))
|
2025-08-05 01:46:40 +00:00
|
|
|
|
user = cursor.fetchone()
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
2025-08-05 01:46:40 +00:00
|
|
|
|
if not user:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="用户名或密码错误")
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
|
|
|
|
|
hashed_input = hash_password(request_body.password)
|
2025-09-11 05:16:58 +00:00
|
|
|
|
if user['password_hash'] != hashed_input:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="用户名或密码错误")
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
# 创建JWT token
|
|
|
|
|
|
token_data = {
|
|
|
|
|
|
"user_id": user['user_id'],
|
|
|
|
|
|
"username": user['username'],
|
2025-09-11 05:16:58 +00:00
|
|
|
|
"caption": user['caption'],
|
|
|
|
|
|
"role_id": user['role_id']
|
2025-08-29 08:37:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
token = jwt_service.create_access_token(token_data)
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
|
|
|
|
|
# 记录登录日志
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取客户端IP地址(考虑代理)
|
|
|
|
|
|
client_ip = request.client.host if request.client else None
|
|
|
|
|
|
if "x-forwarded-for" in request.headers:
|
|
|
|
|
|
client_ip = request.headers["x-forwarded-for"].split(",")[0].strip()
|
|
|
|
|
|
elif "x-real-ip" in request.headers:
|
|
|
|
|
|
client_ip = request.headers["x-real-ip"]
|
|
|
|
|
|
|
|
|
|
|
|
# 获取User-Agent
|
|
|
|
|
|
user_agent = request.headers.get("user-agent", None)
|
|
|
|
|
|
|
|
|
|
|
|
# 插入登录日志
|
|
|
|
|
|
log_query = """
|
|
|
|
|
|
INSERT INTO user_logs (user_id, action_type, ip_address, user_agent)
|
|
|
|
|
|
VALUES (%s, %s, %s, %s)
|
|
|
|
|
|
"""
|
|
|
|
|
|
cursor.execute(log_query, (user['user_id'], 'login', client_ip, user_agent))
|
|
|
|
|
|
connection.commit()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
# 日志记录失败不影响登录流程
|
|
|
|
|
|
print(f"Failed to log user login: {e}")
|
|
|
|
|
|
|
2025-09-26 07:47:37 +00:00
|
|
|
|
login_response_data = LoginResponse(
|
2025-08-05 01:46:40 +00:00
|
|
|
|
user_id=user['user_id'],
|
|
|
|
|
|
username=user['username'],
|
|
|
|
|
|
caption=user['caption'],
|
|
|
|
|
|
email=user['email'],
|
2025-09-11 05:16:58 +00:00
|
|
|
|
token=token,
|
|
|
|
|
|
role_id=user['role_id']
|
2025-08-05 01:46:40 +00:00
|
|
|
|
)
|
2025-12-26 08:58:36 +00:00
|
|
|
|
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(
|
2025-12-26 08:58:36 +00:00
|
|
|
|
code="200",
|
|
|
|
|
|
message="登录成功",
|
2025-09-26 07:47:37 +00:00
|
|
|
|
data=login_response_data.dict()
|
|
|
|
|
|
)
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
@router.post("/auth/logout")
|
|
|
|
|
|
def logout(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
|
|
|
|
|
"""登出接口,撤销当前token"""
|
|
|
|
|
|
token = credentials.credentials
|
2025-09-26 07:47:37 +00:00
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
payload = jwt_service.verify_token(token)
|
|
|
|
|
|
if not payload:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="无效或过期的token")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
user_id = payload.get("user_id")
|
|
|
|
|
|
if not user_id:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="无效的token payload")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
revoked = jwt_service.revoke_token(token, user_id)
|
2025-09-26 07:47:37 +00:00
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
if revoked:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="200", message="登出成功")
|
2025-08-29 08:37:29 +00:00
|
|
|
|
else:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="400", message="已经登出或token未找到")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
@router.post("/auth/logout-all")
|
|
|
|
|
|
def logout_all(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
|
"""登出所有设备"""
|
2025-09-26 07:47:37 +00:00
|
|
|
|
user_id = current_user["user_id"]
|
2025-08-29 08:37:29 +00:00
|
|
|
|
revoked_count = jwt_service.revoke_all_user_tokens(user_id)
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="200", message=f"从 {revoked_count} 个设备登出")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
@router.post("/auth/admin/revoke-user-tokens/{user_id}")
|
2025-09-26 07:47:37 +00:00
|
|
|
|
def admin_revoke_user_tokens(
|
|
|
|
|
|
user_id: int, credentials: HTTPAuthorizationCredentials = Depends(security)
|
|
|
|
|
|
):
|
2025-08-29 08:37:29 +00:00
|
|
|
|
"""管理员功能:撤销指定用户的所有token"""
|
|
|
|
|
|
token = credentials.credentials
|
2025-09-26 07:47:37 +00:00
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
payload = jwt_service.verify_token(token)
|
|
|
|
|
|
if not payload:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="无效或过期的token")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
admin_user_id = payload.get("user_id")
|
|
|
|
|
|
if not admin_user_id:
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="401", message="无效的token payload")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
# 这里可以添加管理员权限检查,目前暂时允许任何登录用户操作
|
2025-09-26 07:47:37 +00:00
|
|
|
|
# if payload.get('role_id') != ADMIN_ROLE_ID:
|
|
|
|
|
|
# return create_api_response(code="403", message="需要管理员权限")
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
revoked_count = jwt_service.revoke_all_user_tokens(user_id)
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(
|
|
|
|
|
|
code="200", message=f"为用户 {user_id} 撤销了 {revoked_count} 个token"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
@router.get("/auth/me")
|
|
|
|
|
|
def get_me(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
|
"""获取当前用户信息"""
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(code="200", message="获取用户信息成功", data=current_user)
|
|
|
|
|
|
|
2025-08-29 08:37:29 +00:00
|
|
|
|
|
|
|
|
|
|
@router.post("/auth/refresh")
|
|
|
|
|
|
def refresh_token(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
|
"""刷新token"""
|
|
|
|
|
|
token_data = {
|
2025-09-26 07:47:37 +00:00
|
|
|
|
"user_id": current_user["user_id"],
|
|
|
|
|
|
"username": current_user["username"],
|
|
|
|
|
|
"caption": current_user["caption"],
|
|
|
|
|
|
"role_id": current_user["role_id"],
|
2025-08-29 08:37:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
new_token = jwt_service.create_access_token(token_data)
|
2025-09-26 07:47:37 +00:00
|
|
|
|
return create_api_response(
|
|
|
|
|
|
code="200", message="Token刷新成功", data={"token": new_token}
|
|
|
|
|
|
)
|