82 lines
3.4 KiB
Python
82 lines
3.4 KiB
Python
from starlette.middleware.base import BaseHTTPMiddleware
|
||
from fastapi import Request, Response
|
||
import time
|
||
from app.services.terminal_service import terminal_service
|
||
from app.services.jwt_service import jwt_service
|
||
from app.core.response import create_api_response
|
||
|
||
class TerminalCheckMiddleware(BaseHTTPMiddleware):
|
||
async def dispatch(self, request: Request, call_next):
|
||
# 1. 检查是否有 Imei 头,没有则认为是普通请求,直接放行
|
||
imei = request.headers.get("Imei")
|
||
if not imei:
|
||
return await call_next(request)
|
||
|
||
# 2. 检查时间戳 (防重放/时钟同步)
|
||
# 优先从Header获取,如果没有则尝试从Query Parameter获取
|
||
# client_time_str = request.headers.get("time") or request.query_params.get("time")
|
||
|
||
# if client_time_str:
|
||
# try:
|
||
# client_time = int(client_time_str)
|
||
# server_time = int(time.time() * 1000)
|
||
|
||
# # 允许 10 分钟的误差 (10 * 60 * 1000 = 600000 ms)
|
||
# # 考虑到网络延迟和设备时间未校准,设置宽松一点
|
||
# if abs(server_time - client_time) > 600000:
|
||
# return create_api_response(
|
||
# code="400",
|
||
# message="设备时间与服务器时间差距过大,请校准时间"
|
||
# )
|
||
# except ValueError:
|
||
# # 时间格式错误,暂时忽略或返回错误
|
||
# pass
|
||
|
||
# 3. 提取其他设备信息
|
||
device_type = request.headers.get("deviceType", "UNKNOWN")
|
||
# device_info 可能是 "UNIS iMeeting a7"
|
||
device_info = request.headers.get("deviceInfo", "Unknown Device")
|
||
|
||
# 获取客户端IP (考虑代理)
|
||
client_ip = request.client.host
|
||
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"]
|
||
|
||
# 获取当前用户ID (如果已登录)
|
||
user_id = None
|
||
auth_header = request.headers.get("Authorization")
|
||
if auth_header and auth_header.startswith("Bearer "):
|
||
try:
|
||
token = auth_header.split(" ")[1]
|
||
payload = jwt_service.verify_token(token)
|
||
if payload:
|
||
user_id = payload.get("user_id")
|
||
except Exception:
|
||
pass # 忽略token解析错误,只记录设备在线状态
|
||
|
||
# 4. 调用服务进行检查和更新
|
||
# 注意:这里是同步调用数据库,但在 async 中可能会阻塞 loop
|
||
# 理想情况下 terminal_service 应该是 async 的,或者使用 run_in_executor
|
||
# 但由于数据库操作较快,且 mysql-connector 是同步的,暂时直接调用
|
||
# 如果并发高,建议将 service 改为 async
|
||
|
||
result = terminal_service.check_and_update_terminal(
|
||
imei=imei,
|
||
terminal_type=device_type,
|
||
terminal_name=device_info,
|
||
ip_address=client_ip,
|
||
user_id=user_id
|
||
)
|
||
|
||
if not result["allowed"]:
|
||
return create_api_response(
|
||
code="403",
|
||
message=result["reason"]
|
||
)
|
||
|
||
# 5. 放行请求
|
||
response = await call_next(request)
|
||
return response
|