# 认证系统开发文档 ## 概述 本文档详细介绍了基于Laravel 12和Laravel Sanctum实现的Token认证系统,适用于Web端、移动端及各种API调用场景。 ## 系统架构 ### 1. 整体结构 ``` 认证系统 ├── 控制器层 (AuthController) │ ├── 用户认证逻辑 │ ├── Token管理 │ └── 设备管理 ├── 模型层 (User) │ ├── 用户数据管理 │ ├── Sanctum Token集成 │ └── 数据验证 ├── 路由层 (admin.php) │ ├── 公开路由 (登录) │ └── 受保护路由 (需Token) └── 中间件层 (auth:sanctum) ├── Token验证 └── 用户授权 ``` ### 2. 文件结构 ``` app/ ├── Http/Controllers/Admin/ │ └── AuthController.php # 认证控制器 ├── Models/ │ └── User.php # 用户模型 └── Http/Middleware/ └── (使用Laravel内置auth:sanctum中间件) routes/ └── admin.php # 后台认证路由 database/ ├── migrations/ │ ├── create_users_table.php # 用户表结构 │ └── create_personal_access_tokens_table.php # Token表结构 └── seeders/ └── AdminUserSeeder.php # 测试用户数据 config/ └── sanctum.php # Sanctum配置文件 docs/ └── 认证系统开发文档.md # 本文档 ``` ## 核心组件详细说明 ### 1. AuthController 认证控制器 **位置**: `app/Http/Controllers/Admin/AuthController.php` **职责**: - 处理用户登录认证 - 生成和管理API Token - 处理用户登出 - 提供用户信息查询 - 管理多设备登录 **主要方法**: #### login() - 用户登录 - **功能**: 验证用户凭据,生成Token - **输入**: username, password, device_name(可选) - **输出**: 用户信息 + Token - **验证**: 用户名密码、用户状态检查 #### logout() - 登出当前设备 - **功能**: 删除当前设备的Token - **输入**: 当前请求的Token - **输出**: 成功确认消息 #### logoutAll() - 登出所有设备 - **功能**: 删除用户所有设备的Token - **输入**: 当前请求的Token - **输出**: 成功确认消息 #### me() - 获取用户信息 - **功能**: 返回当前登录用户信息 - **输入**: Token认证 - **输出**: 用户详细信息 + Token状态 #### refresh() - 刷新Token - **功能**: 生成新Token,删除旧Token - **输入**: 当前Token + device_name - **输出**: 新Token信息 #### devices() - 获取设备列表 - **功能**: 查看用户所有登录设备 - **输入**: Token认证 - **输出**: 设备列表 + Token信息 #### deleteDevice() - 删除指定设备 - **功能**: 根据Token ID删除指定设备 - **输入**: token_id参数 - **输出**: 删除确认消息 ### 2. User 用户模型 **位置**: `app/Models/User.php` **特性**: - 继承Laravel Authenticatable - 集成HasApiTokens trait (Sanctum) - 自定义时间戳字段映射 - 用户状态验证 **重要配置**: ```php // 自定义时间戳字段 const CREATED_AT = 'create_time'; const UPDATED_AT = 'update_time'; // 可批量赋值字段 protected $fillable = [ 'username', 'nickname', 'email', 'mobile', 'password', 'dept_id', 'avatar', 'status' ]; // 隐藏敏感字段 protected $hidden = ['password']; ``` ### 3. 路由配置 **位置**: `routes/admin.php` **结构**: ```php // 公开路由 (无需认证) Route::prefix('auth')->group(function () { Route::post('/login', [AuthController::class, 'login']); }); // 受保护路由 (需要Token认证) Route::middleware('auth:sanctum')->group(function () { // 认证相关 Route::prefix('auth')->group(function () { Route::post('/logout', [AuthController::class, 'logout']); Route::post('/logout-all', [AuthController::class, 'logoutAll']); Route::get('/me', [AuthController::class, 'me']); Route::post('/refresh', [AuthController::class, 'refresh']); Route::get('/devices', [AuthController::class, 'devices']); Route::delete('/devices', [AuthController::class, 'deleteDevice']); }); // 业务功能 Route::get('/dashboard', function () { /* 仪表盘逻辑 */ }); }); ``` ## 业务流程 ### 1. 用户登录流程 ```mermaid graph TD A[客户端发送登录请求] --> B[AuthController::login] B --> C[验证请求参数] C --> D{参数是否有效?} D -->|否| E[返回参数错误] D -->|是| F[验证用户凭据] F --> G{用户名密码是否正确?} G -->|否| H[返回认证失败] G -->|是| I[检查用户状态] I --> J{用户是否可用?} J -->|否| K[返回用户状态错误] J -->|是| L[生成API Token] L --> M[返回用户信息和Token] ``` ### 2. API请求流程 ```mermaid graph TD A[客户端发送API请求] --> B[携带Bearer Token] B --> C[auth:sanctum中间件验证] C --> D{Token是否有效?} D -->|否| E[返回401未授权] D -->|是| F[获取Token对应用户] F --> G{用户是否存在且可用?} G -->|否| H[返回403禁止访问] G -->|是| I[执行控制器方法] I --> J[返回业务数据] ``` ### 3. Token管理流程 ```mermaid graph TD A[Token生成] --> B[存储到personal_access_tokens表] B --> C[设置设备名称和权限] C --> D[Token使用验证] D --> E{Token是否过期?} E -->|是| F[自动清理] E -->|否| G[继续使用] G --> H[用户主动登出] H --> I[删除指定Token] I --> J[Token失效] ``` ## 数据库设计 ### 1. system_users 表 (用户表) ```sql CREATE TABLE `system_users` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `nickname` varchar(50) DEFAULT NULL COMMENT '昵称', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `mobile` varchar(20) DEFAULT NULL COMMENT '手机号', `password` varchar(255) NOT NULL COMMENT '密码', `avatar` varchar(500) DEFAULT NULL COMMENT '头像', `dept_id` int DEFAULT NULL COMMENT '部门ID', `status` tinyint DEFAULT '1' COMMENT '状态 1:正常 0:禁用', `deleted` tinyint DEFAULT '0' COMMENT '删除 1:已删除 0:正常', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) COMMENT='系统用户表'; ``` ### 2. personal_access_tokens 表 (Token表) ```sql CREATE TABLE `personal_access_tokens` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `tokenable_type` varchar(255) NOT NULL, `tokenable_id` bigint unsigned NOT NULL, `name` varchar(255) NOT NULL, `token` varchar(64) NOT NULL, `abilities` text, `last_used_at` timestamp NULL DEFAULT NULL, `expires_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `personal_access_tokens_token_unique` (`token`), KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`) ) COMMENT='API访问令牌表'; ``` ## 使用方法 ### 1. 客户端集成示例 #### JavaScript/Web端 ```javascript class AuthService { constructor() { this.token = localStorage.getItem('auth_token'); this.baseURL = '/admin'; } // 登录 async login(username, password, deviceName = 'Web Browser') { const response = await fetch(`${this.baseURL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ username, password, device_name: deviceName }) }); const data = await response.json(); if (data.success) { this.token = data.data.token.access_token; localStorage.setItem('auth_token', this.token); return data.data; } throw new Error(data.message); } // 发送认证请求 async apiRequest(url, options = {}) { const headers = { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json', 'Accept': 'application/json', ...options.headers }; return fetch(url, { ...options, headers }); } // 获取用户信息 async getUser() { const response = await this.apiRequest(`${this.baseURL}/auth/me`); return response.json(); } // 登出 async logout() { await this.apiRequest(`${this.baseURL}/auth/logout`, { method: 'POST' }); this.token = null; localStorage.removeItem('auth_token'); } } ``` #### 移动端示例 (React Native) ```javascript import AsyncStorage from '@react-native-async-storage/async-storage'; class MobileAuthService { constructor() { this.token = null; this.baseURL = 'https://your-api.com/admin'; this.loadToken(); } async loadToken() { this.token = await AsyncStorage.getItem('auth_token'); } async login(username, password) { const deviceInfo = { device_name: `${Platform.OS} ${DeviceInfo.getSystemVersion()}` }; const response = await fetch(`${this.baseURL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ username, password, ...deviceInfo }) }); const data = await response.json(); if (data.success) { this.token = data.data.token.access_token; await AsyncStorage.setItem('auth_token', this.token); return data.data; } throw new Error(data.message); } } ``` ### 2. 服务端扩展示例 #### 添加新的认证路由 ```php // routes/admin.php Route::middleware('auth:sanctum')->group(function () { // 用户管理 Route::prefix('users')->group(function () { Route::get('/', [UserController::class, 'index']); Route::post('/', [UserController::class, 'store']); Route::put('/{id}', [UserController::class, 'update']); Route::delete('/{id}', [UserController::class, 'destroy']); }); }); ``` #### 创建新的业务控制器 ```php user(); // 业务逻辑 $users = User::paginate(10); return $this->success($users, '获取用户列表成功'); } } ``` ## 配置说明 ### 1. Sanctum配置 **文件**: `config/sanctum.php` **重要配置项**: ```php // Token过期时间 (分钟) 'expiration' => null, // null表示永不过期 // 中间件配置 'middleware' => [ 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, ], // 前缀配置 'prefix' => 'sanctum', // 数据库连接 'connection' => env('SANCTUM_CONNECTION'), ``` ### 2. 环境变量配置 **.env 文件**: ```env # 应用配置 APP_NAME="Study API V2" APP_ENV=local APP_DEBUG=true APP_URL=http://localhost:8000 # 数据库配置 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=study_api_v2 DB_USERNAME=root DB_PASSWORD= # Sanctum配置 SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1 SANCTUM_EXPIRATION=null ``` ## 安全考虑 ### 1. Token安全 **存储安全**: - ✅ 客户端使用安全存储 (Web: localStorage, 移动端: Keychain/Keystore) - ✅ 避免在URL参数中传递Token - ✅ 使用HTTPS传输 - ⚠️ 定期轮换Token (refresh机制) **传输安全**: - ✅ 始终使用 `Authorization: Bearer {token}` 头 - ✅ 不在Cookie中存储Token (避免CSRF) - ✅ 设置合适的CORS策略 ### 2. 用户验证 **登录安全**: - ✅ 密码哈希存储 (bcrypt) - ✅ 用户状态检查 (status, deleted字段) - ✅ 失败次数限制 (可扩展) - ⚠️ 双因素认证 (可扩展) **会话管理**: - ✅ 多设备登录管理 - ✅ 设备标识和追踪 - ✅ 远程登出功能 - ✅ Token使用时间记录 ### 3. API安全 **访问控制**: - ✅ 基于Token的认证 - ✅ 路由级别的权限控制 - ✅ 用户状态实时验证 - ⚠️ 角色权限系统 (可扩展) **数据保护**: - ✅ 敏感数据过滤 (密码等) - ✅ 统一错误响应格式 - ✅ 请求参数验证 - ✅ SQL注入防护 (Eloquent ORM) ## 错误处理 ### 1. 常见错误码 | 错误码 | 说明 | 处理方式 | |--------|------|----------| | 401 | 未授权访问 | 重新登录 | | 403 | 禁止访问 | 检查用户状态 | | 422 | 参数验证失败 | 修正请求参数 | | 500 | 服务器内部错误 | 联系技术支持 | ### 2. 错误响应格式 ```json { "success": false, "message": "具体错误信息", "code": 422, "data": null, "errors": { "username": ["用户名不能为空"], "password": ["密码长度至少6位"] } } ``` ### 3. 客户端错误处理示例 ```javascript async function handleApiCall() { try { const response = await authService.apiRequest('/admin/auth/me'); const data = await response.json(); if (!response.ok) { switch (response.status) { case 401: // Token过期或无效,重新登录 authService.logout(); window.location.href = '/login'; break; case 403: // 用户被禁用 alert('账户已被禁用,请联系管理员'); break; case 422: // 参数错误 console.error('参数错误:', data.errors); break; default: console.error('API错误:', data.message); } return; } // 处理成功响应 console.log('用户信息:', data.data); } catch (error) { console.error('网络错误:', error); } } ``` ## 性能优化 ### 1. 数据库优化 **索引优化**: - ✅ username字段唯一索引 - ✅ personal_access_tokens表Token字段索引 - ✅ 用户状态字段索引 **查询优化**: - ✅ 避免N+1查询问题 - ✅ 使用Eloquent关联查询 - ✅ 合理使用缓存 ### 2. Token管理优化 **清理策略**: - ✅ 定期清理过期Token - ✅ 限制单用户Token数量 - ✅ 异步处理Token操作 **缓存策略**: - ⚠️ Redis缓存用户信息 (可扩展) - ⚠️ Token黑名单缓存 (可扩展) ## 监控和日志 ### 1. 日志记录 **登录日志**: ```php // 在AuthController中添加 Log::info('User login attempt', [ 'username' => $request->username, 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), 'timestamp' => now() ]); ``` **API访问日志**: ```php // 可以创建中间件记录API访问 Log::info('API access', [ 'user_id' => Auth::id(), 'endpoint' => $request->path(), 'method' => $request->method(), 'ip' => $request->ip() ]); ``` ### 2. 监控指标 **关键指标**: - 登录成功/失败率 - Token使用频率 - API响应时间 - 并发用户数 ## 注意事项 ### 1. 开发注意事项 **代码规范**: - ✅ 遵循PSR-4自动加载规范 - ✅ 使用Laravel最佳实践 - ✅ 保持代码注释完整 - ✅ 单一职责原则 **测试要求**: - ⚠️ 编写单元测试 - ⚠️ 集成测试覆盖 - ⚠️ API文档同步更新 ### 2. 部署注意事项 **环境配置**: - ✅ 生产环境关闭DEBUG - ✅ 配置正确的APP_URL - ✅ 设置强密码和密钥 - ✅ 配置HTTPS **安全配置**: - ✅ 限制文件权限 - ✅ 配置防火墙规则 - ✅ 定期更新依赖包 - ✅ 监控异常访问 ### 3. 维护注意事项 **定期维护**: - 清理过期Token - 检查用户状态 - 更新安全补丁 - 备份重要数据 **扩展考虑**: - 角色权限系统 - 多租户支持 - 接口版本控制 - 限流和熔断 ## 技术支持 ### 1. 常见问题 **Q: Token过期时间如何设置?** A: 在 `config/sanctum.php` 中设置 `expiration` 字段,单位为分钟。设置为 `null` 表示永不过期。 **Q: 如何实现Token自动刷新?** A: 客户端可以调用 `/admin/auth/refresh` 接口获取新Token,同时删除旧Token。 **Q: 如何限制用户同时登录设备数?** A: 在登录时可以检查用户现有Token数量,超过限制时删除最旧的Token。 ### 2. 扩展资源 **相关文档**: - [Laravel Sanctum官方文档](https://laravel.com/docs/11.x/sanctum) - [Laravel认证文档](https://laravel.com/docs/11.x/authentication) - [API资源文档](https://laravel.com/docs/11.x/eloquent-resources) **工具推荐**: - Postman/Insomnia (API测试) - Laravel Telescope (调试工具) - Laravel Horizon (队列监控) --- **文档版本**: v1.0 **最后更新**: 2024年12月 **作者**: 开发团队 **审核**: 技术负责人