简介

除了提供内置的 authentication(身份验证)服务外,Laravel 还提供了一种可以很简单就进行使用的方法,来对用户与资源的授权关系进行管理。 它很安全,即使用户已经通过了「身份验证(authentication)」, 用户也可能无权对应用程序中重要的模型或数据库记录进行删除或更改。简单、条理化的系统性,是 Laravel 对授权管理的特性。

Laravel 主要提供了两种授权操作的方法: 拦截器和策略。 可以把拦截器(gates)和策略(policies)想象成路由和控制器。拦截器(Gates)提供了一种轻便的基于闭包函数的授权方法,像是路由。而策略(policies),就像是一个控制器,对特定模型或资源,进行分组管理的逻辑规则。 在本文档中,我们将首先探讨拦截器(gates),然后研究策略(policies)。

你在构建应用程序时,不用为是仅仅使用拦截器(gates)或是仅仅使用策略(policies)而担心,并不需要在两者中进行唯一选择。大多数的应用程序都同时包含两个方法,并且同时使用两者,能够更好的进行工作。拦截器(gates),更适用于没有与任何模型或资源有关的授权操作,例如查看管理员仪表盘。与之相反,当你希望为特定的模型或资源进行授权管理时,应该使用策略(policies) 方法。

拦截器 (Gates)

编写拦截器(Gates)

注意
通过理解拦截器(Gates),是一个很好的学习 Laravel 授权特性的基础知识的方法。同时,考虑到 Laravel 应用程序的健壮性,应该结合使用策略 policies 来组织授权规则。

拦截器(Gates)是用来确定用户是否有权执行给定操作的闭包函数。默认条件下,拦截器(Gates)的使用,是在php App\Providers\AuthServiceProvider类中的 php boot 函数里来规定php Gate规则。拦截器(Gates)始终接收用户实例为其第一个参数,并且可以选择性的接收其他参数,例如相关的 Eloquent 模型。

在下面的例子中,我们将定义一个拦截器(Gates),并通过调用php App\Models\Post类,来实现结合用户的 POST 请求,命中给定的规则。拦截器(Gates)将通过比较用户的php id,和 POST 请求中的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 将会自动把用户信息传递给拦截器(Gates)。以下是一个典型的,在控制器中使用拦截器(Gates)进行行为授权控制的例子:

  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. // 这个用户可以提交update...
  3. }
  4. if (Gate::forUser($user)->denies('update-post', $post)) {
  5. // 这个用户不可以提交update...
  6. }

你还可以通过php anyphp none方法来一次性授权多个行为:

  1. if (Gate::any(['update-post', 'delete-post'], $post)) {
  2. // 用户可以提交update或delete...
  3. }
  4. if (Gate::none(['update-post', 'delete-post'], $post)) {
  5. // 用户不可以提交update和delete...
  6. }

未通过授权时的抛出异常
php Illuminate\Auth\Access\AuthorizationException中准备了 HTTP 的 403 响应。你可以使用php Gate门面中的php authorize方法,来规定如果用户进行了未授权的行为时,触发php AuthorizationException实例 ,该实例会自动转换返回为 HTTP 的 403 响应:

  1. Gate::authorize('update-post', $post);
  2. // 行为已获授权...

上下文的值传递
能够用于拦截器(Gates)的授权方法,(php allowsphp deniesphp checkphp anyphp nonephp authorizephp canphp cannot) 和在前端进行的授权方法 Blade 指令 (php @canphp @cannotphp @canany) 在第 2 个参数中,可以接收数组。这些数组元素作为参数传递给拦截器(Gates),在做出授权决策时可用于其他上下文:

  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. // 用户可以请求create...
  14. }

拦截器响应

到目前为止,我们只学习了拦截器(Gates)中返回布尔值的简单操作。但是,有时你需要的返回可能更复杂,比如错误消息。所以,你可以尝试使用php Illuminate\Auth\Access\Response来构建你的拦截器(Gates):

  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('You must be an administrator.');
  8. });

你希望从拦截器(Gates)中返回响应时,使用php Gate::allows方法,将仅返回一个简单的布尔值;同时,你还可以使用php Gate::inspect方法来返回拦截器(Gates)中的所有响应值:

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

在使用php Gate::authorize方法时,如果操作未被授权,仍然会触发php AuthorizationException, 用户验证(authorization)响应提供的错误消息,将传递给 HTTP 响应:

  1. Gate::authorize('edit-settings');
  2. // 行为进行授权...

自定义HTTP响应状态
当一个操作通过 Gate 被拒绝时,返回一个php 403HTTP 响应;然而,有时返回一个可选的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. });

拦截 Gate 检查

有时,你可能希望将所有能力授予特定用户。你可以使用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闭包返回非空结果,则该结果将被视为授权检查的结果。

内联授权

有时,你可能希望确定当前经过身份验证的用户是否有权执行给定操作,而无需编写与该操作对应的专用拦截器。Laravel 允许你通过php Gate::allowIfphp Gate::denyIf方法执行这些类型的「内联」授权检查:

  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:policyArtisan 命令生成策略。生成的策略将放置在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 App\Providers\AuthServiceProvider包含一个php policies属性,它将 Eloquent 模型映射到其相应的策略。 注册策略将指示 Laravel 在授权针对给定 Eloquent 模型的操作时使用哪个策略:

  1. <?php
  2. namespace App\Providers;
  3. use App\Models\Post;
  4. use App\Policies\PostPolicy;
  5. use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
  6. use Illuminate\Support\Facades\Gate;
  7. class AuthServiceProvider extends ServiceProvider
  8. {
  9. /**
  10. * 应用程序的策略映射。
  11. *
  12. * @var array
  13. */
  14. protected $policies = [
  15. Post::class => PostPolicy::class,
  16. ];
  17. /**
  18. * 注册任何应用程序身份验证/授权服务。
  19. */
  20. public function boot(): void
  21. {
  22. // ...
  23. }
  24. }

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

如果要自定义策略的发现逻辑,可以使用php Gate::guessPolicyNamesUsing方法注册自定义策略发现回调。通常,应该从应用程序的php AuthServiceProviderphp boot方法调用此方法:

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

注意
php AuthServiceProvider中显式映射的任何策略将优先于任何可能自动发现的策略。

编写策略

策略方法

注册策略类后,可以为其授权的每个操作添加方法。例如,让我们在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是否与 Post 上的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 403HTTP 响应;然而,有时返回一个可选的 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. }

Guest 用户

默认情况下,如果传入的 HTTP 请求不是经过身份验证的用户发起的,那么所有的拦截器(gates)和策略(policies)会自动返回php false。但是,你可以通过声明一个「optional」类型提示或为用户参数定义提供一个php null默认值,从而允许这些授权检查通过你的拦截器(gates)和策略(policies):

  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方法将尝试调用基于 Gate 的闭包,该闭包将匹配给定的操作名称。

不需要指定模型的操作
请记住,某些操作可能对应着「不需要模型实例」的策略方法,比如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 App\Models\User模型提供了有用方法,Laravel 还给任何控制器提供了一个有用的 php authorize 方法,这些控制器要继承(php extendsphp App\Http\Controllers\Controller基类。

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. class PostController extends Controller
  8. {
  9. /**
  10. * 更新指定的博客文章
  11. *
  12. * @throws \Illuminate\Auth\Access\AuthorizationException
  13. */
  14. public function update(Request $request, Post $post): RedirectResponse
  15. {
  16. $this->authorize('update', $post);
  17. // 当前用户可以更新博客文章…
  18. return redirect('/posts');
  19. }
  20. }

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

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

授权资源控制器
如果你正在使用资源控制器,你可以在控制器的构造方法中使用php authorizeResource方法,该方法将把适当的php can中间件定义附加到资源控制器的方法上。

php authorizeResource方法的第一个参数是模型的类名,第二个参数是包含模型 ID 的 路由/请求参数的名称。你应该确保你的资源控制器是使用 php --model 标志创建的,这样它才具有所需的方法签名和类型提示。

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Post;
  5. use Illuminate\Http\Request;
  6. class PostController extends Controller
  7. {
  8. /**
  9. * 创建控制器实例
  10. */
  11. public function __construct()
  12. {
  13. $this->authorizeResource(Post::class, 'post');
  14. }
  15. }

以下控制器方法将映射到其相应的策略方法。当请求被路由到给定的控制器方法时,会在控制器方法执行之前自动调用相应的策略方法:

| 控制器方法 | 策略方法 | | —- | —- | | index | viewAny | | show | view | | create | create | | store | create | | edit | update | | update | update | | destroy | delete |

技巧
你可以使用带有php make:policy带有 php --model选项的命令,快速的为给定模型生成一个策略类:php php artisan make:policy PostPolicy --model=Post

通过中间件

Laravel 包含一个中间件,可以在传入的请求到达路由或控制器之前对操作进行授权。默认情况下,php Illuminate\Auth\Middleware\Authorize中间件会在php App\Http\Kernel中的php can键中被指定。让我们来看一个使用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 PostPolicy 方法定义,它包含一个额外的 php $category 参数:

  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. $this->authorize('update', [$post, $request->category]);
  9. // 当前用户可以更新博客文章...
  10. return redirect('/posts');
  11. }