# 许可证验证接口文档 ## 📋 接口概述 **接口路径:** `POST /api/v1/auth/verify` **功能:** 验证用户的登录 token 是否有效(用于心跳检测) **调用频率:** 每 60 秒调用一次 --- ## 📥 请求参数 ### Headers ```json { "Content-Type": "application/json", "Authorization": "Bearer {token}" } ``` ### Body (JSON) ```json { "username": "string", // 用户名 "device_id": "string", // 设备 ID "timestamp": 1234567890 // 客户端时间戳(毫秒) } ``` ### 示例请求 ```http POST /api/v1/auth/verify HTTP/1.1 Host: 127.0.0.1:8000 Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... { "username": "zuowei", "device_id": "065dfbc8-9539-405e-8af0-7cfd56000e6", "timestamp": 1702828800000 } ``` --- ## 📤 响应格式 ### 成功响应(200 OK) ```json { "valid": true, "username": "zuowei", "expire_date": "2025-12-31T23:59:59Z" // 可选:账户过期时间 } ``` ### 失败响应 1:Token 无效(401 Unauthorized) ```json { "detail": "Token 无效或已过期" } ``` ### 失败响应 2:账户已过期(403 Forbidden) ```json { "valid": false, "message": "账户已过期" } ``` ### 失败响应 3:会话不存在(404 Not Found) ```json { "detail": "会话不存在或已登出" } ``` --- ## 🔧 后端实现示例(FastAPI) ### 1. 在 `Server/app/api/v1/auth.py` 添加路由 ```python from fastapi import Depends, HTTPException, status from pydantic import BaseModel from app.core.security import get_current_user from app.db import get_db from sqlalchemy.orm import Session from app.models.user import User, UserSession from datetime import datetime, timezone class VerifyRequest(BaseModel): """验证请求""" username: str device_id: str timestamp: int class VerifyResponse(BaseModel): """验证响应""" valid: bool username: str = None expire_date: str = None @router.post("/verify", response_model=VerifyResponse) async def verify_license( request: VerifyRequest, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db) ): """ 验证许可证(心跳检测) 检查: 1. Token 是否有效(通过 Depends(get_current_user) 自动验证) 2. 用户是否存在 3. 会话是否活跃 4. 账户是否过期 """ # 1. 查询用户 user = db.query(User).filter(User.username == request.username).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在" ) # 2. 检查账户是否过期 if user.expire_date: expire_dt = user.expire_date if expire_dt.tzinfo is None: expire_dt = expire_dt.replace(tzinfo=timezone.utc) if datetime.now(timezone.utc) > expire_dt: return VerifyResponse( valid=False, username=request.username, expire_date=user.expire_date.isoformat() if user.expire_date else None ) # 3. 检查会话是否活跃 session = db.query(UserSession).filter( UserSession.user_id == user.id, UserSession.device_id == request.device_id, UserSession.active == True ).first() if not session: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="会话不存在或已登出" ) # 4. 更新最后活跃时间 session.last_seen_at = datetime.now(timezone.utc) db.commit() # 5. 返回验证成功 return VerifyResponse( valid=True, username=request.username, expire_date=user.expire_date.isoformat() if user.expire_date else None ) ``` --- ### 2. 更新 `Server/app/schemas/auth.py` 添加验证相关的 Schema: ```python class VerifyRequest(BaseModel): """验证请求模型""" username: str device_id: str timestamp: int class VerifyResponse(BaseModel): """验证响应模型""" valid: bool username: Optional[str] = None expire_date: Optional[str] = None ``` --- ## 🧪 测试验证接口 ### 使用 curl 测试 ```bash curl -X POST http://127.0.0.1:8000/api/v1/auth/verify \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN_HERE" \ -d '{ "username": "zuowei", "device_id": "test-device-123", "timestamp": 1702828800000 }' ``` ### 预期响应 **成功:** ```json { "valid": true, "username": "zuowei", "expire_date": null } ``` **Token 无效:** ```json { "detail": "Could not validate credentials" } ``` **会话不存在:** ```json { "detail": "会话不存在或已登出" } ``` --- ## 📊 数据库表设计(参考) 确保数据库有以下表和字段: ### users 表 ```sql CREATE TABLE users ( id INTEGER PRIMARY KEY, username VARCHAR UNIQUE NOT NULL, expire_date DATETIME, -- 账户过期时间(NULL = 永久) ... ); ``` ### user_sessions 表 ```sql CREATE TABLE user_sessions ( id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, device_id VARCHAR NOT NULL, active BOOLEAN DEFAULT TRUE, last_seen_at DATETIME, -- 最后活跃时间 ... FOREIGN KEY (user_id) REFERENCES users(id) ); ``` --- ## 🔐 安全注意事项 1. **Token 验证** - 使用 `Depends(get_current_user)` 确保 Token 有效 - Token 过期自动返回 401 2. **会话管理** - 验证成功时更新 `last_seen_at` - 可用于统计在线时长 3. **过期检查** - 支持账户过期时间 - 过期返回 `valid: false` 4. **频率控制** - 前端已做 30 秒缓存,减少请求频率 - 后端可添加 Rate Limiting --- ## 🚀 部署检查 添加接口后,确认: 1. ✅ 路由已注册 ```python # Server/app/main.py app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth") ``` 2. ✅ 数据库表存在 ```bash # 检查 users 和 user_sessions 表 ``` 3. ✅ Token 验证正常 ```bash # 测试登录获取 Token # 测试 verify 接口 ``` 4. ✅ 前端心跳正常 ``` # 前端每 60 秒调用一次 # 30 秒内不重复验证(有缓存) ``` --- ## 📝 完整实现清单 - [ ] 添加 `VerifyRequest` 和 `VerifyResponse` Schema - [ ] 在 `auth.py` 添加 `/verify` 路由 - [ ] 实现 Token 验证逻辑 - [ ] 实现账户过期检查 - [ ] 实现会话活跃检查 - [ ] 更新最后活跃时间 - [ ] 测试接口(成功、失败、过期等场景) - [ ] 部署到服务器 --- ## 💡 可选增强 ### 1. 添加限流(Rate Limiting) ```python from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) @router.post("/verify") @limiter.limit("120/minute") # 每分钟最多 120 次 async def verify_license(...): ... ``` ### 2. 添加详细日志 ```python import logging logger = logging.getLogger(__name__) @router.post("/verify") async def verify_license(...): logger.info(f"[Verify] {request.username} from {request.device_id}") ... ``` ### 3. 返回更多信息 ```python class VerifyResponse(BaseModel): valid: bool username: str = None expire_date: str = None permissions: list = [] # 用户权限列表 online_time: int = 0 # 在线时长(秒) ``` --- **实现这个接口后,前端的心跳检测就能正常工作了!** ✅