简介

除了提供内置的 身份认证 authentication 服务外, Laravel 还提供了一种简单的方法来授权用户对给定资源的操作。例如, 即使用户已经通过认证,他们也可能没有被授权更新或删除某些 Eloquent 模型或应用程序管理的数据库记录。 Laravel 的授权功能提供了一种简单、有序的方式来管理这些类型的授权检查。

Laravel 提供了两种主要的授权操作方式:拦截器 Gates 和策略 Policies。可以把拦截器和策略想象成路由和控制器。拦截器提供了一种基于闭包的简单授权方法,而策略则像控制器一样,围绕特定模型或资源组织逻辑。在本文档中,我们将首先探讨拦截器,然后研究策略。

在构建应用程序时,你不需要在仅使用拦截器和仅使用策略之间做出选择。大多数应用程序很可能会混合使用拦截器和策略,这是完全正常的!拦截器最适用于与任何模型或资源无关的操作,例如查看管理员仪表板。相反,当你希望为特定模型或资源授权某项操作时,应该使用策略。

拦截器 (Gates)

编写拦截器(Gates)


[!注意]
拦截器(Gates)是学习 Laravel 授权特性基础知识的好方法,但在构建健壮的 Laravel 应用时,应该考虑结合使用策略 policies 来组织授权规则。


拦截器(Gates)是用来确定用户是否有权执行给定操作的闭包函数。通常,拦截器(Gates)通过 php Gate 门面在 php App\Providers\AppServiceProvider 类中的 php boot 函数里定义。拦截器(Gates)始终接收一个用户实例作为第一个参数,可以选择性的接收额外参数,例如相关的 Eloquent 模型。

在下面的例子中,我们将定义一个拦截器(Gates)来确定用户是否可以更新给定的 php App\Models\Post 模型。拦截器(Gates)将通过比较用户的 php id与创建帖子的用户的 php user_id 来实现该判断:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Support\Facades\Gate;
  4. /**
  5. * 启动任何应用程序服务.
  6. */
  7. public function boot(): void
  8. {
  9. Gate::define('update-post', function (User $user, Post $post) {
  10. return $user->id === $post->user_id;
  11. });
  12. }

与控制器一样,拦截器(Gates)也可以使用类回调数组来定义::

  1. use App\Policies\PostPolicy;
  2. use Illuminate\Support\Facades\Gate;
  3. /**
  4. * 启动任何应用程序服务.
  5. */
  6. public function boot(): void
  7. {
  8. Gate::define('update-post', [PostPolicy::class, 'update']);
  9. }

授权操作

要使用拦截器(Gates)授权操作,你应该使用 php Gate 门面提供的 php allowsphp denies 方法。注意,你不需要将当前认证的用户传递给这些方法,Laravel 会自动处理将用户传递给拦截器(gate)闭包。通常,在应用程序的控制器中,需要授权操作之前调用门面授权方法:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. use Illuminate\Support\Facades\Gate;
  8. class PostController extends Controller
  9. {
  10. /**
  11. * 更新给定的帖子。
  12. */
  13. public function update(Request $request, Post $post): RedirectResponse
  14. {
  15. if (! Gate::allows('update-post', $post)) {
  16. abort(403);
  17. }
  18. // 更新帖子...
  19. return redirect('/posts');
  20. }
  21. }

如果你想确定某个指定用户是否有权执行某个操作,可以使用 php Gate 门面的 php forUser 方法:

  1. if (Gate::forUser($user)->allows('update-post', $post)) {
  2. // 该用户可以更新帖子...
  3. }
  4. if (Gate::forUser($user)->denies('update-post', $post)) {
  5. // 该用户无法更新帖子...
  6. }

你可以使用 php anyphp none 方法一次授权多个操作:

  1. if (Gate::any(['update-post', 'delete-post'], $post)) {
  2. // 该用户可以更新或删除帖子...
  3. }
  4. if (Gate::none(['update-post', 'delete-post'], $post)) {
  5. // 该用户不能更新或删除帖子...
  6. }

授权或抛出异常
如果你想要在尝试授权一个操作,但该用户不允许执行该操作时自动抛出 php Illuminate\Auth\Access\AuthorizationException 异常,可以使用 php Gate 门面的 php authorize 方法。Laravel 会自动将 php AuthorizationException 实例转换为 403 HTTP 响应:

  1. Gate::authorize('update-post', $post);
  2. // 该操作已授权...

提供额外上下文
用于授权能力的拦截器方法(php allows, php denies, php check, php any, php none, php authorize, php can, php cannot)以及授权的 Blade 指令(php @can, php @cannot, php @canany)可以接收一个数组作为它们的第二个参数。这些数组元素作为参数传递给拦截器闭包,并可以在做出授权决策时用于额外的上下文:

  1. use App\Models\Category;
  2. use App\Models\User;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('create-post', function (User $user, Category $category, bool $pinned) {
  5. if (! $user->canPublishToGroup($category->group)) {
  6. return false;
  7. } elseif ($pinned && ! $user->canPinPosts()) {
  8. return false;
  9. }
  10. return true;
  11. });
  12. if (Gate::check('create-post', [$category, $pinned])) {
  13. // 该用户可以创建帖子...
  14. }

拦截器响应

到目前为止,我们只考察了返回简单布尔值的拦截器。然而,有时你可能希望返回一个更详细的响应,包括错误信息。为此,你可以从你的拦截器返回一个 php Illuminate\Auth\Access\Response

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::deny('您必须是管理员。');
  8. });

即使你从拦截器返回一个授权响应,php Gate::allows 方法仍然会返回一个简单的布尔值;然而,你可以使用 php Gate::inspect 方法来获取拦截器返回的完整授权响应:

  1. $response = Gate::inspect('edit-settings');
  2. if ($response->allowed()) {
  3. // 该操作已授权...
  4. } else {
  5. echo $response->message();
  6. }

当使用 php Gate::authorize 方法时,如果操作未被授权,则会抛出 php AuthorizationException 异常,授权响应提供的错误信息将传播到 HTTP 响应:

  1. Gate::authorize('edit-settings');
  2. // 该操作已授权...

自定义 HTTP 响应状态
当通过拦截器拒绝一个操作时,会返回一个 php 403 HTTP 响应;然而,有时返回一个替代的 HTTP 状态码可能很有用。你可以使用 php Illuminate\Auth\Access\Response 类的 php denyWithStatus 静态构造函数来自定义授权检查失败返回的 HTTP 状态码:

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::denyWithStatus(404);
  8. });

由于通过 php 404 响应隐藏资源是 Web 应用程序中的一种常见模式,因此提供了 php denyAsNotFound 方法以方便使用:

  1. use App\Models\User;
  2. use Illuminate\Auth\Access\Response;
  3. use Illuminate\Support\Facades\Gate;
  4. Gate::define('edit-settings', function (User $user) {
  5. return $user->isAdmin
  6. ? Response::allow()
  7. : Response::denyAsNotFound();
  8. });

拦截拦截器检查

有时,你可能希望授予特定用户所有权限,你可以使用 php before 方法来定义一个在所有其他授权检查之前运行的闭包:

  1. use App\Models\User;
  2. use Illuminate\Support\Facades\Gate;
  3. Gate::before(function (User $user, string $ability) {
  4. if ($user->isAdministrator()) {
  5. return true;
  6. }
  7. });

如果 php before 闭包返回一个非 null 结果,该结果将被视为授权检查的结果。

你可以使用 php after 方法来定义一个在所有其他授权检查之后执行的闭包:

  1. use App\Models\User;
  2. Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
  3. if ($user->isAdministrator()) {
  4. return true;
  5. }
  6. });

php before 方法类似,如果 php after 闭包返回一个非 null 结果,则该结果将被视为授权检查的结果。

内联授权

有时,你可能希望确定当前已认证用户是否有权执行给定操作,而不编写与该操作对应的专用拦截器。Laravel 允许你通过 php Gate::allowIfphp Gate::denyIf 方法执行这些类型的「内联」授权检查。内联授权不会执行任何定义的 「before」或「after」 授权钩子:

  1. use App\Models\User;
  2. use Illuminate\Support\Facades\Gate;
  3. Gate::allowIf(fn (User $user) => $user->isAdministrator());
  4. Gate::denyIf(fn (User $user) => $user->banned());

如果操作未被授权或当前没有用户认证,Laravel 将自动抛出 php Illuminate\Auth\Access\AuthorizationException 异常。php AuthorizationException 实例会自动被 Laravel 的异常处理器转换为 403 HTTP 响应。

创建策略

生成策略

策略是围绕特定模型或资源组织授权逻辑的类。例如,如果你的应用程序是一个博客,您可能会有一个 php App\Models\Post 模型和一个相应的 php App\Policies\PostPolicy 来授权用户操作,如创建或更新帖子。

你可以使用 php make:policy Artisan 命令生成一个策略。生成的策略将放置在 php app/Policies 目录中。如果该目录在你的应用程序中不存在,Laravel 将为你创建它:

  1. php artisan make:policy PostPolicy

php make:policy 命令将生成一个空的策略类,如果你希望生成一个包含与查看、创建、更新和删除资源相关的示例策略方法的类,你可以在执行命令时提供 php --model 选项:

  1. php artisan make:policy PostPolicy --model=Post

注册策略

策略发现
默认情况下,只要模型和策略遵循标准的 Laravel 命名约定,Laravel 就会自动发现策略。具体来说,策略必须位于包含模型的目录或其上级的 php Policies 目录中。例如,模型可以放置在 php app/Models 目录中,而策略可以放置在 php app/Policies 目录中。在这种情况下,Laravel 将首先在 php app/Models/Policies 目录中检查策略,然后在 php app/Policies 目录中检查。此外,策略名称必须与模型名称匹配,并带有 php Policy 后缀。因此,php User 模型将对应于 php UserPolicy 策略类。

如果你想要定义自己的策略发现逻辑,可以使用 php Gate::guessPolicyNamesUsing 方法注册一个自定义策略发现回调。通常,此方法应在应用程序的 php AppServiceProviderphp boot 方法中调用:

  1. use Illuminate\Support\Facades\Gate;
  2. Gate::guessPolicyNamesUsing(function (string $modelClass) {
  3. // 返回给定模型策略类的名称...
  4. });

手动注册策略
使用 php Gate 门面,你可以在应用程序的 php AppServiceProviderphp boot 方法中手动注册策略及其对应的模型:

  1. use App\Models\Order;
  2. use App\Policies\OrderPolicy;
  3. use Illuminate\Support\Facades\Gate;
  4. /**
  5. * 启动任何应用程序服务
  6. */
  7. public function boot(): void
  8. {
  9. Gate::policy(Order::class, OrderPolicy::class);
  10. }

编写策略

策略方法

一旦策略类被注册,你可以为每个它授权的操作添加方法。例如,让我们在 php PostPolicy 上定义一个 php update 方法,该方法确定给定的 php App\Models\User 是否可以更新给定的 php App\Models\Post 实例。

php update 方法将接收一个 php User 和一个 php Post 实例作为其参数,并应返回 php truephp false 以指示用户是否有权更新给定的 php Post。因此,在这个例子中,我们将验证用户的 php id 与帖子上的 php user_id 是否匹配:

  1. <?php
  2. namespace App\Policies;
  3. use App\Models\Post;
  4. use App\Models\User;
  5. class PostPolicy
  6. {
  7. /**
  8. * 确定用户是否可以更新给定的帖子。
  9. */
  10. public function update(User $user, Post $post): bool
  11. {
  12. return $user->id === $post->user_id;
  13. }
  14. }

你可以根据需要继续在策略上定义其他方法,以授权各种操作。例如,你可以定义 php viewphp delete 方法来授权各种与 php Post 相关的操作,但请记住,策略方法的命名,可以随心所欲。

如果你在通过 Artisan 控制台生成策略时使用了 php --model 选项,它已经包含 php viewAnyphp viewphp createphp updatephp deletephp restorephp forceDelete 操作的方法。


[!注意]
所有策略都通过 Laravel 服务容器 解析,允许你在策略的构造函数中键入所需的任何依赖项,以便自动注入它们。

策略响应

目前为止,我们只考察了返回简单布尔值的策略方法。然而,有时你可能希望返回一个更详细的响应,包括错误信息。为此,你可以从策略方法返回一个 php Illuminate\Auth\Access\Response 实例:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * 确定用户是否可以更新给定的帖子。
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::deny('您不拥有该篇文章。');
  12. }

当从策略返回一个授权响应时,php Gate::allows 方法仍然会返回一个简单的布尔值;然而,您可以使用 php Gate::inspect 方法来获取拦截器返回的完整授权响应:

  1. use Illuminate\Support\Facades\Gate;
  2. $response = Gate::inspect('update', $post);
  3. if ($response->allowed()) {
  4. // 该操作已授权...
  5. } else {
  6. echo $response->message();
  7. }

当使用 php Gate::authorize 方法时,如果操作未被授权,则会抛出 php AuthorizationException 异常,授权响应提供的错误信息将传播到 HTTP 响应:

  1. Gate::authorize('update', $post);
  2. // 该操作已授权...

自定义 HTTP 响应状态
当通过策略方法拒绝一个操作时,会返回一个 php 403 HTTP 响应;然而,有时返回一个替代的 HTTP 状态码可能很有用。你可以使用 php Illuminate\Auth\Access\Response 类的 php denyWithStatus 静态构造函数来自定义失败授权检查返回的 HTTP 状态码:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * 确定用户是否可以更新给定的帖子。
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::denyWithStatus(404);
  12. }

由于通过 php 404 响应隐藏资源是 Web 应用程序中的一种常见模式,因此提供了 php denyAsNotFound 方法以方便使用:

  1. use App\Models\Post;
  2. use App\Models\User;
  3. use Illuminate\Auth\Access\Response;
  4. /**
  5. * 确定用户是否可以更新给定的帖子。
  6. */
  7. public function update(User $user, Post $post): Response
  8. {
  9. return $user->id === $post->user_id
  10. ? Response::allow()
  11. : Response::denyAsNotFound();
  12. }

无模型方法

某些策略方法只接收当前认证用户的实例。这种情况在授权 php create 操作时最为常见。例如,如果你正在创建一个博客,你可能希望确定用户是否有权创建任何帖子。在这些情况下,你的策略方法应该只期望接收一个用户实例:

  1. /**
  2. * 确定给定用户是否可以创建帖子。
  3. */
  4. public function create(User $user): bool
  5. {
  6. return $user->role == 'writer';
  7. }

访客用户

默认情况下,如果传入的 HTTP 请求不是由认证用户发起的,所有拦截器和策略会自动返回 php false。然而,你可以通过声明一个「可选」类型提示或在用户参数定义中提供一个 php null 默认值,来允许这些授权检查传递给你的拦截器和策略:

  1. <?php
  2. namespace App\Policies;
  3. use App\Models\Post;
  4. use App\Models\User;
  5. class PostPolicy
  6. {
  7. /**
  8. * 确定用户是否可以更新给定的帖子。
  9. */
  10. public function update(?User $user, Post $post): bool
  11. {
  12. return $user?->id === $post->user_id;
  13. }
  14. }

策略过滤器

对于特定用户,你可能想要在给定策略中授权所有操作。为此,请在策略上定义一个 php before 方法。php before 方法将在策略上的任何其他方法之前执行,让你有机会在实际调用目标策略方法之前授权操作。此功能最常用于授权应用程序管理员执行任何操作:

  1. use App\Models\User;
  2. /**
  3. * 执行预授权检查。
  4. */
  5. public function before(User $user, string $ability): bool|null
  6. {
  7. if ($user->isAdministrator()) {
  8. return true;
  9. }
  10. return null;
  11. }

如果你想要拒绝特定类型用户的所有授权检查,则可以从 php before 方法返回 php false。如果返回 php null,授权检查将通过策略方法执行。


[!警告]
如果策略类不包含与正在检查的权限名称匹配的方法,则不会调用该类的 php before 方法。

使用策略授权操作

通过用户模型

Laravel 应用程序中包含的 php App\Models\User 模型提供了两个有用的方法来授权操作:php canphp cannotphp canphp cannot 方法接收你想要授权的操作名称和相关模型。例如,让我们确定用户是否有权更新给定的 php App\Models\Post 模型。通常,这将在控制器方法中完成:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. class PostController extends Controller
  8. {
  9. /**
  10. * 更新指定的帖子。
  11. */
  12. public function update(Request $request, Post $post): RedirectResponse
  13. {
  14. if ($request->user()->cannot('update', $post)) {
  15. abort(403);
  16. }
  17. // 更新帖子...
  18. return redirect('/posts');
  19. }
  20. }

如果为给定模型注册了策略,php can 方法将自动调用适当的策略并返回布尔结果。如果没有为模型注册策略,php can 方法将尝试调用与给定操作名称匹配的基于闭包的拦截器。

不需要模型的操作
请记住,某些操作可能对应于策略方法不需要模型实例,如 php create。在这些情况下,你可以将类名传递给 php can 方法,类名将用于确定授权操作时要使用的策略:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. class PostController extends Controller
  8. {
  9. /**
  10. * 创建帖子。
  11. */
  12. public function store(Request $request): RedirectResponse
  13. {
  14. if ($request->user()->cannot('create', Post::class)) {
  15. abort(403);
  16. }
  17. // 创建该帖子...
  18. return redirect('/posts');
  19. }
  20. }

通过 php ### Gate 门面
除了为 php App\Models\User 模型提供的实用方法外,你始终可以通过 php Gate 门面的 php authorize 方法来授权操作。

php can 方法类似,此方法接收你想要授权的操作名称和相关模型。如果操作未被授权,php authorize 方法将抛出 php Illuminate\Auth\Access\AuthorizationException 异常,Laravel 异常处理器会自动将其转换为带有 403 状态码的 HTTP 响应:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. use Illuminate\Support\Facades\Gate;
  8. class PostController extends Controller
  9. {
  10. /**
  11. * 更新给定的博客文章。
  12. *
  13. * @throws \Illuminate\Auth\Access\AuthorizationException
  14. */
  15. public function update(Request $request, Post $post): RedirectResponse
  16. {
  17. Gate::authorize('update', $post);
  18. // 当前用户可以更新该博客文章...
  19. return redirect('/posts');
  20. }
  21. }

不需要模型的操作
如前所述,某些策略方法(如 php create)不需要模型实例。在这些情况下,你应该将类名传递给 php authorize 方法,类名将用于确定授权操作时要使用的策略:

  1. use App\Models\Post;
  2. use Illuminate\Http\RedirectResponse;
  3. use Illuminate\Http\Request;
  4. use Illuminate\Support\Facades\Gate;
  5. /**
  6. * 创建一篇新的博客文章。
  7. *
  8. * @throws \Illuminate\Auth\Access\AuthorizationException
  9. */
  10. public function create(Request $request): RedirectResponse
  11. {
  12. Gate::authorize('create', Post::class);
  13. // 当前用户可以创建博客文章...
  14. return redirect('/posts');
  15. }

通过中间件

Laravel 包含一个中间件,可以在传入请求到达您的路由或控制器之前授权操作。默认情况下,php Illuminate\Auth\Middleware\Authorize 中间件可以通过 php can 中间件别名附加到路由,该别名由 Laravel 自动注册。让我们探索一个使用 php can 中间件授权用户可以更新帖子的示例:

  1. use App\Models\Post;
  2. Route::put('/post/{post}', function (Post $post) {
  3. // 当前用户可以更新该帖子...
  4. })->middleware('can:update,post');

在这个例子中,我们向 php can 中间件传递了两个参数。第一个是我们想要授权的操作名称,第二个是我们想要传递给策略方法的路由参数。在这种情况下,由于我们使用了隐式模型绑定,一个 php App\Models\Post 模型将被传递给策略方法。如果用户未被授权执行给定操作,中间件将返回一个带有 403 状态码的 HTTP 响应。

为了方便起见,你还可以使用 php can 方法将 php can 中间件附加到路由:

  1. use App\Models\Post;
  2. Route::put('/post/{post}', function (Post $post) {
  3. // 当前用户可以更新该帖子...
  4. })->can('update', 'post');

不需要模型的操作
同样,某些策略方法(如 php create)不需要模型实例。在这些情况下,你可以将类名传递给中间件。类名将用于确定授权操作时要使用的策略:

  1. Route::post('/post', function () {
  2. // 当前用户可以创建帖子...
  3. })->middleware('can:create,App\Models\Post');

在字符串中间件定义中指定整个类名可能会变得繁琐,因此,你可以选择使用 php can 方法将 php can 中间件附加到路由:

  1. use App\Models\Post;
  2. Route::post('/post', function () {
  3. // 当前用户可以创建帖子...
  4. })->can('create', Post::class);

通过 Blade 模板

在编写 Blade 模板时,你可能希望仅在用户有权执行给定操作时显示页面的某部分。例如,你可能希望仅在用户实际上可以更新博客帖子时显示更新表单。在这种情况下,你可以使用 php @canphp @cannot 指令:

  1. @can('update', $post)
  2. <!-- 当前用户可以更新帖子... -->
  3. @elsecan('create', App\Models\Post::class)
  4. <!-- 当前用户可以创建新的帖子... -->
  5. @else
  6. <!-- ... -->
  7. @endcan
  8. @cannot('update', $post)
  9. <!-- 当前用户无法更新帖子... -->
  10. @elsecannot('create', App\Models\Post::class)
  11. <!-- 当前用户不能创建新帖子... -->
  12. @endcannot

这些指令是编写 php @ifphp @unless 语句的便捷快捷方式,上面的 php @canphp @cannot 语句等同于以下语句:

  1. @if (Auth::user()->can('update', $post))
  2. <!-- 当前用户可以更新帖子... -->
  3. @endif
  4. @unless (Auth::user()->can('update', $post))
  5. <!-- 当前用户无法更新帖子... -->
  6. @endunless

你还可以确定用户是否有权从给定操作数组中执行任何操作,为此,请使用 php @canany 指令:

  1. @canany(['update', 'view', 'delete'], $post)
  2. <!-- 当前用户可以更新、查看或删除帖子... -->
  3. @elsecanany(['create'], \App\Models\Post::class)
  4. <!-- 当前用户可以创建帖子... -->
  5. @endcanany

不需要模型的操作
与大多数其他授权方法一样,如果操作不需要模型实例,你可以将类名传递给 php @canphp @cannot 指令:

  1. @can('create', App\Models\Post::class)
  2. <!-- 当前用户可以创建帖子... -->
  3. @endcan
  4. @cannot('create', App\Models\Post::class)
  5. <!-- 当前用户不能创建帖子... -->
  6. @endcannot

提供额外上下文

在使用策略授权操作时,你可以将数组作为各种授权函数和辅助函数的第二个参数传递。数组中的第一个元素将用于确定应调用哪个策略,而数组中的其余元素将作为参数传递给策略方法,并可以在做出授权决策时用于额外的上下文。例如,考虑以下包含额外 php $category 参数的 php PostPolicy 方法定义:

  1. /**
  2. * 确定用户是否可以更新给定的帖子。
  3. */
  4. public function update(User $user, Post $post, int $category): bool
  5. {
  6. return $user->id === $post->user_id &&
  7. $user->canUpdateCategory($category);
  8. }

当尝试确定认证用户是否可以更新给定的帖子时,我们可以这样调用策略方法:

  1. /**
  2. * 更新给定的博客文章。
  3. *
  4. * @throws \Illuminate\Auth\Access\AuthorizationException
  5. */
  6. public function update(Request $request, Post $post): RedirectResponse
  7. {
  8. Gate::authorize('update', [$post, $request->category]);
  9. // 当前用户可以更新博客文章...
  10. return redirect('/posts');
  11. }

授权与 Inertia

虽然授权必须总是在服务器上处理,但提供前端应用程序授权数据以正确渲染应用程序的 UI 通常很方便。Laravel 没有定义将授权信息暴露给 Inertia 驱动的前端所需的约定。

然而,如果你使用的是 Laravel 的基于 Inertia 的 入门套件,你的应用程序已经包含一个 php HandleInertiaRequests 中间件。在这个中间件的 php share 方法中,你可以返回将提供给应用程序中所有 Inertia 页面的共享数据,这些共享数据可以作为为用户定义授权信息的便捷位置:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use App\Models\Post;
  4. use Illuminate\Http\Request;
  5. use Inertia\Middleware;
  6. class HandleInertiaRequests extends Middleware
  7. {
  8. // ...
  9. /**
  10. * 定义默认情况下共享的属性。
  11. *
  12. * @return array<string, mixed>
  13. */
  14. public function share(Request $request)
  15. {
  16. return [
  17. ...parent::share($request),
  18. 'auth' => [
  19. 'user' => $request->user(),
  20. 'permissions' => [
  21. 'post' => [
  22. 'create' => $request->user()->can('create', Post::class),
  23. ],
  24. ],
  25. ],
  26. ];
  27. }
  28. }