newrun
This commit is contained in:
371
temp_backup/Server_redundant/tempdocs/许可证验证接口文档.md
Normal file
371
temp_backup/Server_redundant/tempdocs/许可证验证接口文档.md
Normal file
@@ -0,0 +1,371 @@
|
||||
# 许可证验证接口文档
|
||||
|
||||
## 📋 接口概述
|
||||
|
||||
**接口路径:** `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" // 可选:账户过期时间
|
||||
}
|
||||
```
|
||||
|
||||
### 失败响应 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 # 在线时长(秒)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**实现这个接口后,前端的心跳检测就能正常工作了!** ✅
|
||||
|
||||
|
||||
---
|
||||
|
||||
**实现这个接口后,前端的心跳检测就能正常工作了!** ✅
|
||||
|
||||
Reference in New Issue
Block a user