Laravel 8 API 多表用户认证:实现多Guard策略
在现代Web应用开发中,尤其是面向服务架构(SOA)或微服务架构,一个API后端可能需要同时服务于不同类型的用户,例如普通用户、管理员、商家等。这些不同类型的用户数据通常存储在不同的数据库表中,使用统一的认证逻辑和用户模型会变得复杂且不灵活。Laravel 8提供了强大且灵活的认证系统,通过其多Guard(守卫)策略,我们可以优雅地实现基于多表的API用户认证。本文将深入探讨如何在Laravel 8中为API构建一个支持多用户表(如users表和admins表)的认证系统。

一、理解Laravel的认证核心:Guards与Providers
在开始实现之前,必须理解Laravel认证系统的两个核心概念:Guards和Providers。
Guard(守卫):定义了如何对每个请求进行用户认证。它决定了使用哪个“提供者”来检索用户,以及如何存储认证状态(例如使用Session或Token)。常见的Guard包括
web(使用Session)和api(使用Token)。Provider(提供者):定义了如何从持久化存储(如数据库)中检索用户。它指定了使用哪个Eloquent模型以及对应的数据表。
默认情况下,Laravel为web和apiGuard配置了同一个User模型提供者。多表认证的本质就是为不同的用户类型创建不同的Guard和Provider配对。
二、项目结构与数据准备
假设我们有一个API项目,需要同时认证来自users表的普通客户和来自admins表的管理员。
首先,创建对应的数据表和模型:
# 生成用户和管理员的迁移文件 php artisan make:migration create_users_table php artisan make:migration create_admins_table # 生成对应的模型 php artisan make:model User php artisan make:model Admin
在迁移文件中定义表结构(这里为简化示例):
// database/migrations/xxxx_create_users_table.php
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
// database/migrations/xxxx_create_admins_table.php
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('username')->unique();
$table->string('password');
$table->timestamps();
});
}运行迁移:php artisan migrate。
三、配置多Guard与Provider
接下来,我们需要在config/auth.php文件中进行配置。
// config/auth.php return [ 'defaults' => [ 'guard' => 'api', // 默认使用api守卫 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', // 可以使用sanctum或passport,这里示例用token 'provider' => 'users', 'hash' => false, ], // 新增一个用于管理员的API守卫 'admin-api' => [ 'driver' => 'token', 'provider' => 'admins', // 指向admins提供者 'hash' => false, ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => AppModelsUser::class, // 指向User模型 ], // 新增一个用于管理员的提供者 'admins' => [ 'driver' => 'eloquent', 'model' => AppModelsAdmin::class, // 指向Admin模型 ], ], 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], 'admins' => [ 'provider' => 'admins', 'table' => 'admin_password_resets', // 可选,如果需要独立密码重置表 'expire' => 60, 'throttle' => 60, ], ], ];
四、实现用户模型与认证逻辑
确保User和Admin模型都继承自IlluminateFoundationAuthUser并使用HasApiTokens特性(如果使用Laravel Sanctum)。为简化,我们使用Laravel自带的Token认证。
// app/Models/User.php
<?php
namespace AppModels;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
}
// app/Models/Admin.php
<?php
namespace AppModels;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
class Admin extends Authenticatable
{
use Notifiable;
protected $table = 'admins'; // 显式指定表名
protected $fillable = ['username', 'password'];
protected $hidden = ['password', 'remember_token'];
}五、创建认证控制器与路由
我们需要为普通用户和管理员分别创建登录和获取信息的API端点。
首先,创建控制器:
php artisan make:controller Api/Auth/UserAuthController php artisan make:controller Api/Auth/AdminAuthController
在UserAuthController中实现用户登录:
// app/Http/Controllers/Api/Auth/UserAuthController.php
<?php
namespace AppHttpControllersApiAuth;
use AppHttpControllersController;
use AppModelsUser;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
use IlluminateSupportFacadesHash;
class UserAuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
// 使用 'api' guard 对应的 User 模型进行认证
$credentials = $request->only('email', 'password');
if (Auth::guard('api')->attempt($credentials)) {
$user = Auth::guard('api')->user();
// 生成一个简单的API令牌(生产环境建议使用Sanctum/Passport)
$token = $user->createToken('UserAPIToken')->plainTextToken;
return response()->json([
'user' => $user,
'access_token' => $token,
'token_type' => 'Bearer',
]);
}
return response()->json(['error' => 'Unauthorized'], 401);
}
public function profile(Request $request)
{
// 此路由应通过 'auth:api' 中间件保护
return response()->json(Auth::guard('api')->user());
}
public function logout(Request $request)
{
// 撤销当前令牌
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Successfully logged out']);
}
}在AdminAuthController中实现管理员登录(逻辑类似,但使用不同的guard):
// app/Http/Controllers/Api/Auth/AdminAuthController.php
<?php
namespace AppHttpControllersApiAuth;
use AppHttpControllersController;
use AppModelsAdmin;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
class AdminAuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'username' => 'required|string',
'password' => 'required',
]);
// 关键:使用 'admin-api' guard 进行认证
$credentials = $request->only('username', 'password');
if (Auth::guard('admin-api')->attempt($credentials)) {
$admin = Auth::guard('admin-api')->user();
$token = $admin->createToken('AdminAPIToken')->plainTextToken;
return response()->json([
'admin' => $admin,
'access_token' => $token,
'token_type' => 'Bearer',
]);
}
return response()->json(['error' => 'Unauthorized'], 401);
}
public function profile(Request $request)
{
// 此路由应通过 'auth:admin-api' 中间件保护
return response()->json(Auth::guard('admin-api')->user());
}
}接下来,在routes/api.php中定义路由:
// routes/api.php
use AppHttpControllersApiAuthUserAuthController;
use AppHttpControllersApiAuthAdminAuthController;
Route::prefix('user')->group(function () {
Route::post('/login', [UserAuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () { // 如果使用Sanctum
Route::get('/profile', [UserAuthController::class, 'profile']);
Route::post('/logout', [UserAuthController::class, 'logout']);
});
});
Route::prefix('admin')->group(function () {
Route::post('/login', [AdminAuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/profile', [AdminAuthController::class, 'profile']);
});
});注意:上述示例使用了Laravel Sanctum进行API令牌管理。你需要先安装并配置Sanctum(composer require laravel/sanctum, php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"),并在User和Admin模型中使用use LaravelSanctumHasApiTokens;特性。如果你使用其他驱动(如Passport或自定义Token),请相应调整认证逻辑。
六、使用中间件保护路由
Laravel的auth中间件可以指定guard,从而保护特定用户类型的路由。
// 在控制器构造函数或路由中指定guard
Route::middleware('auth:api')->get('/user/profile', function (Request $request) {
// 只有通过 'api' guard 认证的普通用户才能访问
return $request->user(); // 返回 User 模型实例
});
Route::middleware('auth:admin-api')->get('/admin/dashboard', function (Request $request) {
// 只有通过 'admin-api' guard 认证的管理员才能访问
return $request->user(); // 返回 Admin 模型实例
});你也可以在控制器构造函数中设置默认guard:
public function __construct()
{
// 将此控制器下的所有方法都设置为需要 'admin-api' guard 认证
$this->middleware('auth:admin-api');
}七、测试API端点
使用Postman或cURL测试认证流程。
普通用户登录:向
POST https://www.ipipp.com/api/user/login发送JSON请求体{"email": "user@example.com", "password": "password"}。成功后将返回包含access_token的响应。访问受保护的用户资料:在请求头中添加
Authorization: Bearer {access_token},访问GET https://www.ipipp.com/api/user/profile。管理员登录:向
POST https://www.ipipp.com/api/admin/login发送JSON请求体{"username": "admin1", "password": "password"}。访问受保护的管理员资料:使用管理员的token,访问
GET https://www.ipipp.com/api/admin/profile。
尝试用普通用户的token访问管理员端点,应该会收到401 Unauthorized错误,因为中间件auth:admin-api会使用admin-api guard来验证token,而该token是由User模型颁发的,验证会失败。
八、总结与最佳实践
通过配置多Guard和Provider,Laravel 8能够清晰、高效地支持多表API用户认证。关键步骤总结如下:
规划用户类型:明确需要区分的用户角色及其对应的数据表。
配置
auth.php:为每种用户类型定义独立的guard和provider。创建模型:每个用户类型对应一个继承
Authenticatable的Eloquent模型。实现控制器:在登录等认证逻辑中,显式使用对应的guard(如
Auth::guard('admin-api')->attempt(...))。保护路由:使用
auth:{guard-name}中间件来保护特定用户类型的路由。选择令牌驱动:对于生产环境,强烈建议使用Laravel Sanctum或Passport来管理API令牌,它们提供了更安全、更完整的功能(如令牌作用域、过期时间)。
这种架构的优势在于职责分离清晰,扩展性强。未来如果需要加入第三种用户类型(如merchants),只需重复上述模式:创建表和模型、添加新的guard和provider配置、实现对应的控制器和路由即可,无需修改现有代码。