study-api-v2/docs/认证系统开发文档.md

690 lines
17 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.

# 认证系统开发文档
## 概述
本文档详细介绍了基于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
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\BaseController;
use Illuminate\Http\Request;
class UserController extends BaseController
{
public function index(Request $request)
{
// 获取当前认证用户
$currentUser = $request->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月
**作者**: 开发团队
**审核**: 技术负责人