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

17 KiB
Raw Blame History

认证系统开发文档

概述

本文档详细介绍了基于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)
  • 自定义时间戳字段映射
  • 用户状态验证

重要配置:

// 自定义时间戳字段
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

结构:

// 公开路由 (无需认证)
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. 用户登录流程

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请求流程

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管理流程

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 表 (用户表)

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表)

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端

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)

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. 服务端扩展示例

添加新的认证路由

// 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

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

重要配置项:

// 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 文件:

# 应用配置
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. 错误响应格式

{
    "success": false,
    "message": "具体错误信息",
    "code": 422,
    "data": null,
    "errors": {
        "username": ["用户名不能为空"],
        "password": ["密码长度至少6位"]
    }
}

3. 客户端错误处理示例

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. 日志记录

登录日志:

// 在AuthController中添加
Log::info('User login attempt', [
    'username' => $request->username,
    'ip' => $request->ip(),
    'user_agent' => $request->userAgent(),
    'timestamp' => now()
]);

API访问日志:

// 可以创建中间件记录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. 扩展资源

相关文档:

工具推荐:

  • Postman/Insomnia (API测试)
  • Laravel Telescope (调试工具)
  • Laravel Horizon (队列监控)

文档版本: v1.0
最后更新: 2024年12月
作者: 开发团队
审核: 技术负责人