Files
DP/tempdocs/许可证验证接口文档.md

372 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 许可证验证接口文档
## 📋 接口概述
**接口路径:** `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...
```
---
## 📤 响应格式
### 成功响应200 OK
```json
{
"valid": true,
"username": "zuowei",
"expire_date": "2025-12-31T23:59:59Z" // 可选:账户过期时间
}
```
### 失败响应 1Token 无效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 # 在线时长(秒)
```
---
**实现这个接口后,前端的心跳检测就能正常工作了!**