# 滑动过期Token机制使用说明 ## 📋 功能概述 实现了滑动过期(Sliding Expiration)Token机制,确保用户在活跃使用系统时不会遇到token超时问题。 ### 🎯 核心特性 - ✅ **自动刷新**:每次API请求时自动检查并刷新token - ✅ **滑动过期**:只要用户保持活跃,token永远不会过期 - ✅ **安全性**:完全无活动时,token会在设定时间后过期 - ✅ **透明化**:前端无需特殊处理,自动更新token - ✅ **向后兼容**:不影响现有API接口 ## ⚙️ 配置项 在 `config.ini` 中添加以下配置: ```ini # 后台管理Token配置 ADMIN_TOKEN_EXPIRE_MINUTES=10 # 访问token有效期(分钟) ADMIN_REFRESH_TOKEN_EXPIRE_HOURS=24 # 刷新token有效期(小时) ``` ## 🔧 工作原理 ### 1. Token刷新时机 - 当token使用时间超过总有效期的**50%**时自动刷新 - 例如:10分钟有效期的token,在使用5分钟后会被刷新 ### 2. 刷新流程 ``` 用户请求API → 验证token → 检查是否需要刷新 → 生成新token → 返回响应 + 新token ``` ### 3. 前端处理 - 检查响应头中的 `X-New-Token` - 如果存在,更新本地存储的token - 后续请求使用新token ## 🚀 使用方法 ### 方法1:使用依赖注入(推荐) ```python from app.utils.auth_decorator import get_current_user_with_sliding_expiration @router.get("/my-api") async def my_api( user_info: dict = Depends(get_current_user_with_sliding_expiration) ): user = user_info["user"] # 当前用户对象 new_token = user_info["new_token"] # 新token(可能为None) # 构造响应 response_data = {"message": "success", "data": {...}} # 如果token被刷新,添加到响应中 if new_token: response_data["token_refreshed"] = True response_data["new_token"] = new_token return response_data ``` ### 方法2:手动调用AuthService ```python from app.services.auth_service import AuthService @router.get("/manual-api") async def manual_api( credentials: HTTPAuthorizationCredentials = Depends(security), db: AsyncSession = Depends(get_db_connection) ): auth_service = AuthService(db) user, new_token = await auth_service.get_current_user(credentials.credentials) if not user: return {"code": 401, "message": "无效的访问令牌"} response_data = {"code": 0, "message": "success", "data": {...}} if new_token: response_data["token_refreshed"] = True response_data["new_token"] = new_token return response_data ``` ### 方法3:使用中间件(全局) 在主应用文件中添加: ```python from app.middleware.token_refresh_middleware import TokenRefreshMiddleware app.add_middleware( TokenRefreshMiddleware, exclude_paths=[ "/auth/login", "/auth/captcha", "/docs", "/openapi.json" ] ) ``` ## 💻 前端集成 ### JavaScript示例 ```javascript class ApiClient { constructor() { this.token = localStorage.getItem('access_token'); } async request(url, options = {}) { const headers = { 'Authorization': `Bearer ${this.token}`, ...options.headers }; const response = await fetch(url, { ...options, headers }); const data = await response.json(); // 检查token刷新 if (data.token_refreshed && data.new_token) { this.token = data.new_token; localStorage.setItem('access_token', data.new_token); console.log('Token已自动刷新'); } // 也可以检查响应头 const newToken = response.headers.get('X-New-Token'); if (newToken) { this.token = newToken; localStorage.setItem('access_token', newToken); console.log('Token已通过响应头刷新'); } return data; } } ``` ### Vue.js示例 ```javascript // axios拦截器 axios.interceptors.response.use( response => { // 检查响应数据中的token刷新 if (response.data?.token_refreshed && response.data?.new_token) { const newToken = response.data.new_token; localStorage.setItem('access_token', newToken); axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`; console.log('Token已刷新'); } // 检查响应头中的token刷新 const headerToken = response.headers['x-new-token']; if (headerToken) { localStorage.setItem('access_token', headerToken); axios.defaults.headers.common['Authorization'] = `Bearer ${headerToken}`; console.log('Token已通过响应头刷新'); } return response; }, error => { if (error.response?.status === 401) { // token完全过期,跳转登录 router.push('/login'); } return Promise.reject(error); } ); ``` ## 📊 响应格式 ### 普通响应 ```json { "code": 0, "message": "success", "data": {...} } ``` ### Token刷新时的响应 ```json { "code": 0, "message": "success", "data": {...}, "token_refreshed": true, "new_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." } ``` **响应头:** ``` X-New-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... X-Token-Refreshed: true ``` ## 🔍 测试验证 ### 运行测试脚本 ```bash cd LQAdminPlatform python test_sliding_token.py ``` ### 测试结果示例 ``` 🚀 开始测试滑动过期Token机制 ============================================================ 🧪 测试4: 配置加载 ✅ 后台管理Token过期时间: 10 分钟 ✅ 通用Token过期时间: 30 分钟 ✅ 刷新Token过期时间: 24 小时 🎯 实际使用的过期时间: 10 分钟 🧪 测试1: Token创建 ✅ Token创建成功: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ✅ Token验证成功: 用户=testuser 过期时间: 2026-01-30 04:06:08+00:00 🎉 所有测试完成! ``` ## 🧪 API测试示例 ### 测试滑动过期API ```bash # 1. 登录获取token curl -X POST "http://localhost:8000/auth/login" \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": "password"}' # 2. 使用token调用API(支持滑动过期) curl -X GET "http://localhost:8000/system/info" \ -H "Authorization: Bearer YOUR_TOKEN_HERE" # 3. 检查响应中的token_refreshed字段 ``` ### 预期响应 ```json { "code": 0, "message": "获取系统信息成功", "data": { "system_name": "LQ后台管理系统", "version": "1.0.0", "current_time": "2026-01-30T11:56:08.325863+00:00", "current_user": { "id": "user123", "username": "admin", "email": "admin@example.com", "is_superuser": true } }, "token_refreshed": true, "new_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." } ``` ## 🔍 调试和监控 ### 日志输出 ``` INFO - Token已刷新,用户: admin INFO - 系统信息API - Token已刷新,用户: admin ``` ### 检查token状态 ```python from app.services.jwt_token import verify_and_refresh_token payload, new_token = verify_and_refresh_token(current_token) if new_token: print("Token需要刷新") else: print("Token仍然有效") ``` ## ⚠️ 注意事项 1. **配置优先级**:优先使用 `ADMIN_TOKEN_EXPIRE_MINUTES`,回退到 `ACCESS_TOKEN_EXPIRE_MINUTES` 2. **数据库更新**:每次token刷新都会更新数据库中的token记录 3. **并发处理**:多个并发请求可能同时触发token刷新,这是正常的 4. **安全考虑**:完全无活动的用户仍会在设定时间后被强制登出 5. **前端处理**:建议同时检查响应数据和响应头中的新token ## 🧪 测试场景 ### 测试1:正常使用 1. 用户登录获得10分钟有效期token 2. 5分钟后发起API请求 3. 系统自动刷新token,返回新的10分钟有效期token 4. 用户可以继续使用系统 ### 测试2:长时间无活动 1. 用户登录后10分钟内无任何操作 2. 10分钟后发起API请求 3. 系统返回401错误,要求重新登录 ### 测试3:频繁操作 1. 用户每分钟都有API请求 2. 系统在第5分钟时刷新token 3. 用户可以无限期使用系统(只要保持活跃) ## 📈 性能影响 - **CPU开销**:每次请求增加token验证和可能的刷新操作 - **数据库开销**:token刷新时需要更新数据库记录 - **网络开销**:响应中增加新token信息 - **整体影响**:轻微,用户体验显著提升 ## 🔄 升级指南 ### 从旧版本升级 1. 更新配置文件,添加新的token配置项 2. 重启应用服务 3. 前端代码添加token自动更新逻辑 4. 测试验证功能正常 ### 回滚方案 如需回滚到旧的token机制: 1. 移除中间件配置 2. 使用原有的 `verify_token` 函数 3. 恢复原有的API装饰器 --- **总结**:滑动过期Token机制大大提升了用户体验,用户在正常使用过程中永远不会遇到token超时问题,同时保持了系统的安全性。配置简单,使用方便,前端集成透明。