Laravel多认证守卫?多守卫如何配置?

39次阅读

Laravel支持多认证守卫,通过在config/auth.php中配置多个guardsproviders,可实现不同用户类型(如普通用户、管理员、API客户端)的独立认证。每个守卫指定认证驱动(如sessiontoken)和用户数据源,例如为管理员添加admin守卫并关联Admin模型,通过Auth::guard(‘admin’)进行登录和用户获取,路由中使用auth:admin中间件保护专属路径。适用于前后台分离、API服务、多用户模型等场景,提升安全性与代码清晰度。还可自定义用户提供者(如对接外部API)和认证守卫(如JWT、SSO),通过实现对应接口并注册到服务容器,灵活应对复杂认证需求。

Laravel多认证守卫?多守卫如何配置?

Laravel支持多认证守卫,这允许你的应用同时管理不同类型的用户,比如普通用户、管理员或API客户端,各自拥有独立的认证逻辑和会话管理。核心思想是在

config/auth.php

文件中定义多个守卫(guards)和用户提供者(providers),然后根据需要引用它们。

Laravel的多认证守卫机制,其实就是为了解决“我是谁?”这个问题在不同场景下的复杂性。想象一下,一个电商平台,用户需要登录才能购物,管理员需要登录才能管理商品和订单,而移动app可能通过API令牌来验证用户身份。这些“用户”虽然都是用户,但他们的认证方式、存储位置甚至权限模型都可能大相径庭。Laravel的守卫(Guards)和提供者(Providers)就是为此而生。

config/auth.php

这个配置文件里,你可以找到两个关键的数组:

guards

providers

guards

数组定义了你的应用中有哪些认证“通道”。每个守卫都有一个

driver

(通常是

session

用于Web,

token

sanctum

用于API)和一个

provider

,这个

provider

指明了去哪里找用户。

例如,默认情况下,你可能会看到一个

web

守卫:

'guards' => [     'web' => [         'driver' => 'session',         'provider' => 'users',     ], ],

如果你想为管理员创建一个独立的认证系统,你可以添加一个

admin

守卫:

'guards' => [     'web' => [         'driver' => 'session',         'provider' => 'users',     ],     'admin' => [         'driver' => 'session', // 管理员也用会话认证         'provider' => 'admins', // 但用户数据来自admins提供者     ],     'api' => [         'driver' => 'token', // API用token认证         'provider' => 'users', // API用户可能和web用户是同一批         'hash' => false,     ], ],

接着,

providers

数组定义了如何从数据源中检索用户。每个提供者都有一个

driver

(通常是

eloquent

,指向一个模型)和一个

model

继续上面的例子,如果管理员有独立的数据库表(比如

admins

表,对应

AppModelsAdmin

模型),你就需要为它定义一个提供者:

'providers' => [     'users' => [         'driver' => 'eloquent',         'model' => AppModelsUser::class,     ],     'admins' => [ // 新增一个admins提供者         'driver' => 'eloquent',         'model' => AppModelsAdmin::class, // 指向Admin模型     ], ],

这样一来,

admin

守卫就会通过

admins

提供者去

AppModelsAdmin

模型中查找用户。

当你在控制器中尝试认证时,可以通过

Auth::guard('admin')->attempt($credentials)

来指定使用哪个守卫进行登录。相应的,

Auth::guard('admin')->user()

会返回当前通过

admin

守卫认证的用户实例。在路由中间件中,你可以使用

auth:admin

来保护管理员专属的路由,确保只有通过

admin

守卫认证的用户才能访问。

为什么我的应用需要多个认证守卫?

我个人觉得,当你发现自己的应用开始出现用户角色混乱、权限管理复杂到难以维护时,多守卫就是个救星。它不仅仅是代码层面的一个配置项,更是一种架构思想,帮助我们清晰地划分不同用户群体的认证边界。

举几个常见的例子:

  • 后端分离的Web应用与管理后台: 最典型的场景。用户在前端通过会话(Session)登录,而管理员则通过另一个独立的会话登录管理后台。他们可能使用不同的登录页面,甚至存储在不同的数据库表中。多守卫能够确保这两套认证系统互不干扰,前端用户无法通过自己的会话直接访问管理后台,反之亦然。
  • Web应用与API服务: 如果你的应用同时提供Web界面和供移动App或第三方服务调用的API接口,那么认证方式通常会不同。Web端可能依赖Session/Cookie,而API则倾向于使用Token(如JWT、Laravel Sanctum的API令牌)。这时,你可以为Web端配置一个
    web

    守卫,为API配置一个

    api

    守卫,它们各自拥有独立的认证驱动和用户提供者,使得API请求不再需要Session状态,更轻量、无状态。

  • 不同类型的用户模型: 比如一个SaaS平台,可能有普通用户(
    User

    模型)、企业管理员(

    CompanyAdmin

    模型)和平台超级管理员(

    SuperAdmin

    模型)。虽然他们都是“用户”,但各自的业务逻辑和数据结构可能差异很大。为每种用户类型配置一个守卫和对应的提供者,可以让他们在各自的领域内独立认证,避免在一个庞大的

    User

    模型中塞入所有角色和字段,导致模型臃肿,逻辑混乱。

  • 安全隔离: 独立守卫可以在一定程度上提供更好的安全隔离。即使一个守卫的认证逻辑出现问题,也可能不会影响到其他守卫。比如,Web端的Session被劫持,可能只会影响到Web用户,而API端的Token认证仍然安全。

总的来说,当你的应用的用户群体、认证方式或用户数据源开始多样化时,多认证守卫就成了提升代码清晰度、系统可维护性和安全性的不二选择。它让我们能够以更优雅的方式处理复杂的用户认证需求,而不是在一个单点上打补丁。

如何在控制器和中间件中灵活使用不同守卫?

配置好多个守卫之后,关键就在于如何在实际的业务逻辑中正确地调用它们。这主要体现在登录、获取当前用户和路由保护上。

在控制器中进行登录和用户操作:

当你需要让用户通过某个特定的守卫登录时,直接调用

Auth

Facade的

guard()

方法来指定守卫:

use IlluminateSupportFacadesAuth; use IlluminateHttpRequest;  class AdminAuthController extends Controller {     public function login(Request $request)     {         $credentials = $request->only('email', 'password');          if (Auth::guard('admin')->attempt($credentials)) {             // 认证成功             return redirect()->intended('/admin/dashboard');         }          // 认证失败         return back()->withErrors([             'email' => '提供的凭据与我们的记录不符。',         ]);     }      public function logout()     {         Auth::guard('admin')->logout(); // 只登出admin守卫的用户         return redirect('/admin/login');     }      public function profile()     {         // 获取当前通过admin守卫认证的用户         $admin = Auth::guard('admin')->user();         if ($admin) {             return view('admin.profile', compact('admin'));         }         return redirect('/admin/login'); // 未认证则重定向     } }

这里需要注意的是,

Auth::guard('admin')->attempt()

只会尝试通过

admin

守卫认证用户,不会影响到

web

守卫的状态。同样,

Auth::guard('admin')->logout()

也只会清除

admin

守卫的会话信息。我记得有一次,就是因为忘了在中间件里明确指定守卫,结果管理员登录后,系统却还是按普通用户权限在跑,查了半天才发现是这个小细节,因为默认的

Auth::user()

会去取默认守卫的用户。

在路由中使用中间件保护:

Laravel的

Auth

中间件非常灵活,你可以通过冒号来指定要使用的守卫。

Laravel多认证守卫?多守卫如何配置?

Brizy

Brizy是一个面向机构和 SaaS 的白标网站生成器,可以在几分钟内创建网站页面。

Laravel多认证守卫?多守卫如何配置?166

查看详情 Laravel多认证守卫?多守卫如何配置?

use AppHttpControllersAdminAuthController; use AppHttpControllersAdminDashboardController;  // 管理员登录路由 Route::get('/admin/login', [AdminAuthController::class, 'showLoginForm'])->name('admin.login'); Route::post('/admin/login', [AdminAuthController::class, 'login']); Route::post('/admin/logout', [AdminAuthController::class, 'logout'])->name('admin.logout');  // 保护管理员后台路由组 Route::middleware(['auth:admin'])->prefix('admin')->group(function () {     Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('admin.dashboard');     Route::get('/users', [AdminDashboardController::class, 'users']);     // ... 其他管理员专属路由 });  // 普通用户路由,使用默认的web守卫(或明确指定auth:web) Route::middleware(['auth'])->group(function () { // auth中间件默认使用config/auth.php中的default guard     Route::get('/profile', function () {         return view('user.profile', ['user' => Auth::user()]);     }); });
auth:admin

会确保只有通过

admin

守卫认证的用户才能访问该路由组。如果用户未认证,它会重定向到

config/auth.php

中为

admin

守卫配置的

login

路由(通常是

admin.login

)。这个重定向的逻辑在

app/Http/Middleware/Authenticate.php

中,你可以根据需要修改

redirectTo

方法。

理解并正确使用

Auth::guard('守卫名')

auth:守卫名

是实现多认证守卫的关键。它让你的认证逻辑变得清晰,避免了不同用户类型之间的权限交叉和混乱。

自定义认证守卫和用户提供者有哪些高级用法?

这块其实是Laravel认证机制最强大也最容易被忽视的地方。很多时候,我们不应该被框架的默认实现限制住,而是要学会如何“撬动”它,让它为我们的特殊需求服务。比如,我曾经遇到一个项目,用户数据分散在好几个不同的服务里,不用自定义提供者根本没法统一认证。

自定义用户提供者(User Provider):

当你的用户数据不是存储在传统的Eloquent模型中,或者需要从外部服务、NoSQL数据库等地方获取时,你就需要自定义用户提供者。一个自定义的用户提供者需要实现

IlluminateContractsAuthUserProvider

接口,这个接口定义了几个关键方法:

  • retrieveById($identifier)

    : 根据用户ID获取用户。

  • retrieveByToken($identifier, $token)

    : 根据ID和“记住我”令牌获取用户。

  • updateRememberToken(Authenticatable $user, $token)

    : 更新用户的“记住我”令牌。

  • retrieveByCredentials(array $credentials)

    : 根据登录凭据(如邮箱和密码)获取用户。

  • validateCredentials(Authenticatable $user, array $credentials)

    : 验证用户密码。

举个例子,如果你的用户数据存储在一个外部API中:

  1. 创建自定义用户提供者类:

    // app/Providers/ExternalUserProvider.php <?php  namespace AppProviders;  use IlluminateContractsAuthAuthenticatable; use IlluminateContractsAuthUserProvider; use AppModelsExternalUser; // 假设你有一个模型来封装外部用户数据  class ExternalUserProvider implements UserProvider {     public function retrieveById($identifier)     {         // 调用外部API获取用户数据,并封装成Authenticatable对象         $userData = $this->fetchUserFromExternalApi($identifier);         if ($userData) {             return new ExternalUser($userData);         }         return null;     }      public function retrieveByToken($identifier, $token)     {         // 实现通过token获取用户逻辑         return null; // 或者根据你的外部系统实现     }      public function updateRememberToken(Authenticatable $user, $token)     {         // 如果你的外部系统支持“记住我”功能,在这里更新     }      public function retrieveByCredentials(array $credentials)     {         // 调用外部API,根据email获取用户         $userData = $this->fetchUserFromExternalApiByEmail($credentials['email']);         if ($userData) {             return new ExternalUser($userData);         }         return null;     }      public function validateCredentials(Authenticatable $user, array $credentials)     {         // 调用外部API验证密码,或者在本地进行哈希验证         return $this->verifyPasswordWithExternalApi($user->getAuthIdentifier(), $credentials['password']);     }      // 辅助方法,模拟调用外部API     protected function fetchUserFromExternalApi($id) { /* ... */ }     protected function fetchUserFromExternalApiByEmail($email) { /* ... */ }     protected function verifyPasswordWithExternalApi($id, $password) { /* ... */ } }
    ExternalUser

    模型需要实现

    IlluminateContractsAuthAuthenticatable

    接口,这样Laravel才能正确地处理它。

  2. AuthServiceProvider

    中注册:

    // app/Providers/AuthServiceProvider.php use IlluminateSupportFacadesAuth; use AppProvidersExternalUserProvider;  class AuthServiceProvider extends ServiceProvider {     public function boot()     {         $this->registerPolicies();          Auth::provider('external', function ($app, array $config) {             return new ExternalUserProvider();         });     } }
  3. config/auth.php

    中使用:

    'providers' => [     // ...     'external_users' => [         'driver' => 'external', // 使用你注册的提供者名称     ], ], 'guards' => [     // ...     'external_web' => [         'driver' => 'session',         'provider' => 'external_users', // 指向新的提供者     ], ],

自定义认证守卫(Guard):

当你需要完全自定义认证逻辑,比如实现OAuth2、JWT认证,或者与一个复杂的单点登录(SSO)系统集成时,你可能需要自定义认证守卫。一个自定义守卫需要实现

IlluminateContractsAuthGuard

接口。这个接口提供了诸如

check()

guest()

user()

id()

validate()

setUser()

等方法,用于管理认证状态和用户。

通常,自定义守卫会与自定义用户提供者结合使用。例如,一个JWT守卫可能在

validate()

方法中解析请求头中的JWT令牌,然后通过用户提供者获取用户。

  1. 创建自定义守卫类:

    // app/Guards/JwtGuard.php <?php  namespace AppGuards;  use IlluminateAuthGuardHelpers; use IlluminateContractsAuthGuard; use IlluminateContractsAuthUserProvider; use IlluminateHttpRequest; use FirebaseJWTJWT; // 假设使用Firebase/php-jwt  class JwtGuard implements Guard {     use GuardHelpers;      protected $provider;     protected $request;     protected $user;      public function __construct(UserProvider $provider, Request $request)     {         $this->provider = $provider;         $this->request = $request;     }      public function user()     {         if (! is_null($this->user)) {             return $this->user;         }          $token = $this->request->bearerToken(); // 从Bearer Token中获取JWT          if (! $token) {             return null;         }          try {             $payload = JWT::decode($token, new Key(config('app.jwt_secret'), 'HS256'));             // 假设JWT payload中包含用户ID             return $this->user = $this->provider->retrieveById($payload->sub);         } catch (Exception $e) {             return null;         }     }      public function validate(array $credentials = [])     {         // JWT通常不需要传统的用户名密码验证,令牌本身就是凭证         // 但如果你想在生成令牌前验证,可以在这里做         // 对于JWT守卫,user()方法已经包含了验证逻辑         return ! is_null($this->user());     }      // 其他Guard接口方法根据需要实现 }
  2. AuthServiceProvider

    中注册:

    // app/Providers/AuthServiceProvider.php use AppGuardsJwtGuard;  class AuthServiceProvider extends ServiceProvider {     public function boot()     {         $this->registerPolicies();          Auth::extend('jwt', function ($app, $name, array $config) {             // 返回一个JwtGuard实例             return new JwtGuard(Auth::createUserProvider($config['provider']), $app['request']);         });     } }
  3. config/auth.php

    中使用:

    'guards' => [     // ...     'api' => [         'driver' => 'jwt', // 使用你注册的守卫名称         'provider' => 'users', // JWT可以和现有的用户提供者结合     ], ],

自定义守卫和提供者是Laravel认证系统的高级扩展点,它赋予了我们极大的灵活性去适应各种复杂的认证场景。虽然实现起来需要对Laravel的认证流程有深入理解,但一旦掌握,它就能让你的应用在面对非标准认证需求时游刃有余。这不仅仅是写几行代码,更是对框架底层机制的一种“驾驭”,我觉得这才是真正的技术乐趣所在。

以上就是Laravel多认证守卫?多守卫如何配置?的详细内容,更多请关注laravel php word 前端 go cookie cad app session 后端 ai 路由 php laravel 架构 中间件 Array Cookie Session Token 数据结构 接口 nosql 数据库 http

laravel php word 前端 go cookie cad app session 后端 ai 路由 php laravel 架构 中间件 Array Cookie Session Token 数据结构 接口 nosql 数据库 http

text=ZqhQzanResources