Lzh on GitHub

简介

除了提供内置的身份验证服务之外,Laravel 还提供了一种简单的方法,用于授权用户对特定资源执行操作。例如,即便用户已通过身份验证,他们也可能没有权限更新或删除应用程序管理的某些 Eloquent 模型或数据库记录。Laravel 的授权功能为管理这类权限检查提供了一种简单且有条理的方式。

Laravel 提供了两种主要的授权方式:Gates(权限网关)Policies(策略)。可以将 Gates 和 Policies 类比为路由和控制器。Gates 提供基于闭包的简单授权方式,而 Policies 则像控制器一样,将逻辑围绕特定模型或资源进行分组。在本节文档中,我们将先介绍 Gates,然后再探讨 Policies。

在构建应用程序时,你无需在 Gates 与 Policies 之间做排他性选择。大多数应用程序通常会同时使用 Gates 和 Policies,这完全没问题!Gates 最适合用于与任何模型或资源无关的操作,例如查看管理员控制面板。相比之下,当你希望为特定模型或资源授权某个操作时,应使用 Policies。

权限网关(Gates)

编写权限网关

Gates(权限网关)是学习 Laravel 授权功能基础的绝佳方式;然而,在构建健壮的 Laravel 应用程序时,你应考虑使用 Policies(策略)来组织你的授权规则。

Gates 本质上是闭包函数,用于判断用户是否被授权执行特定操作。通常,Gates 会在 App\Providers\AppServiceProvider 类的 boot 方法中使用 Gate Facade 定义。Gates 始终会接收一个用户实例作为第一个参数,并且可以选择性地接收其他参数,例如相关的 Eloquent 模型。

下面的示例中,我们定义了一个 Gate,用于判断用户是否可以更新给定的 App\Models\Post 模型。这个 Gate 会通过比较用户的 id 与创建该帖子的用户的 user_id 来实现判断:

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}

像控制器一样,Gates 也可以使用类回调数组的方式定义:

use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Gate::define('update-post', [PostPolicy::class, 'update']);
}

授权操作

要使用 Gates 对操作进行授权,你可以使用 Gate Facade 提供的 allowsdenies 方法。需要注意的是,你无需手动传入当前认证的用户,Laravel 会自动将用户传入 Gate 闭包中。通常,在应用的控制器中执行需要授权的操作之前,会调用这些 Gate 授权方法:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    /**
     * 更新指定的帖子。
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        if (! Gate::allows('update-post', $post)) {
            abort(403);
        }

        // 执行帖子更新...

        return redirect('/posts');
    }
}

如果你想判断某个非当前认证用户是否被授权执行操作,可以使用 Gate 的 forUser 方法:

if (Gate::forUser($user)->allows('update-post', $post)) {
    // 该用户可以更新帖子...
}

if (Gate::forUser($user)->denies('update-post', $post)) {
    // 该用户不能更新帖子...
}

你还可以一次性授权多个操作,使用 anynone 方法:

if (Gate::any(['update-post', 'delete-post'], $post)) {
    // 用户可以更新或删除帖子...
}

if (Gate::none(['update-post', 'delete-post'], $post)) {
    // 用户不能更新或删除帖子...
}

授权或抛出异常

如果你希望在授权失败时自动抛出 Illuminate\Auth\Access\AuthorizationException 异常,可以使用 Gate 的 authorize 方法。Laravel 会自动将该异常转换为 HTTP 403 响应:

Gate::authorize('update-post', $post);

// 操作已被授权...

提供额外上下文

用于授权的 Gate 方法(allows, denies, check, any, none, authorize, can, cannot)以及 Blade 授权指令(@can, @cannot, @canany)都可以接收数组作为第二个参数。数组中的元素会作为参数传递给 Gate 闭包,可用于在授权决策时提供额外上下文:

use App\Models\Category;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('create-post', function (User $user, Category $category, bool $pinned) {
    if (! $user->canPublishToGroup($category->group)) {
        return false;
    } elseif ($pinned && ! $user->canPinPosts()) {
        return false;
    }

    return true;
});

if (Gate::check('create-post', [$category, $pinned])) {
    // 用户可以创建帖子...
}

权限网关响应

到目前为止,我们讨论的 Gates 都只是返回简单的布尔值。然而,有时你可能希望返回更详细的响应,包括错误信息。为此,你可以从 Gate 返回一个 Illuminate\Auth\Access\Response 实例:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
        ? Response::allow()
        : Response::deny('你必须是管理员。');
});

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

$response = Gate::inspect('edit-settings');

if ($response->allowed()) {
    // 操作已被授权...
} else {
    echo $response->message();
}

当使用 Gate::authorize 方法时,如果操作未被授权,它会抛出 AuthorizationException,授权响应中提供的错误信息会被自动传递到 HTTP 响应中:

Gate::authorize('edit-settings');

// 操作已被授权...

自定义 HTTP 响应状态

当通过 Gate 拒绝操作时,默认返回 403 HTTP 响应。然而,有时返回其他 HTTP 状态码可能更合适。你可以使用 Illuminate\Auth\Access\Response 的静态构造方法 denyWithStatus 来自定义授权失败时的 HTTP 状态码:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
        ? Response::allow()
        : Response::denyWithStatus(404);
});

由于在 Web 应用中,通过 404 隐藏资源是一个常见模式,denyAsNotFound 方法提供了简便的实现方式:

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
        ? Response::allow()
        : Response::denyAsNotFound();
});

拦截权限检查

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

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::before(function (User $user, string $ability) {
    if ($user->isAdministrator()) {
        return true;
    }
});

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

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

use App\Models\User;

Gate::after(function (User $user, string $ability, bool|null $result, mixed $arguments) {
    if ($user->isAdministrator()) {
        return true;
    }
});

after 闭包返回的值不会覆盖授权检查的结果,除非 gate 或 policy 返回的是 null。

内联授权

有时,你可能希望判断当前已认证用户是否有权执行某个操作,但又不想为此专门编写一个对应的 Gate。Laravel 提供了 Gate::allowIfGate::denyIf 方法,让你可以进行这种“内联”的授权检查。内联授权不会执行任何已定义的 “before” 或 “after” 授权钩子:

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::allowIf(fn (User $user) => $user->isAdministrator());

Gate::denyIf(fn (User $user) => $user->banned());

如果该操作未获授权,或者当前没有任何用户通过认证,Laravel 会自动抛出 Illuminate\Auth\Access\AuthorizationException 异常。 Laravel 的异常处理器会将 AuthorizationException 自动转换为 HTTP 403 响应。

创建策略(Policies)

生成策略

“策略”(Policy)是用于围绕某个特定模型或资源组织授权逻辑的类。例如,如果你的应用是一个博客,你可能会有一个 App\Models\Post 模型,以及一个对应的 App\Policies\PostPolicy,用来授权用户执行创建或更新文章等操作。

你可以使用 make:policy Artisan 命令生成一个策略类。生成的策略会放在 app/Policies 目录下。如果你的应用中没有该目录,Laravel 会自动为你创建:

php artisan make:policy PostPolicy

默认情况下,make:policy 命令会生成一个空的策略类。如果你希望在生成时包含与查看、创建、更新和删除资源相关的示例策略方法,你可以在执行命令时加上 --model 选项:

php artisan make:policy PostPolicy --model=Post

注册策略

策略自动发现(Policy Discovery) 默认情况下,只要模型和策略遵循 Laravel 的标准命名约定,Laravel 就会自动发现对应的策略。具体来说,策略类必须位于模型目录所在位置的同级或上级的 Policies 目录中。举例来说,模型可以放在 app/Models 目录,而策略可以放在 app/Policies 目录。在这种结构下,Laravel 会依次检查 app/Models/Policiesapp/Policies 目录以寻找策略。

此外,策略类名必须与模型类名一致,并以 Policy 作为后缀。例如 User 模型就对应 UserPolicy 策略类。

如果你希望自定义策略发现逻辑,可以使用 Gate::guessPolicyNamesUsing 来注册自定义策略查找回调。一般来说,你应在应用的 AppServiceProviderboot 方法中调用该方法:

use Illuminate\Support\Facades\Gate;
 
Gate::guessPolicyNamesUsing(function (string $modelClass) {
    // 根据给定模型返回策略类名...
});

手动注册策略(Manually Registering Policies)

你也可以使用 Gate facade 手动注册模型与策略之间的对应关系,同样在 AppServiceProviderboot 方法中进行:

use App\Models\Order;
use App\Policies\OrderPolicy;
use Illuminate\Support\Facades\Gate;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Gate::policy(Order::class, OrderPolicy::class);
}

使用属性(Attribute)声明策略

你也可以在模型类上使用 UsePolicy 属性,明确指定该模型使用的策略:

<?php
 
namespace App\Models;
 
use App\Policies\OrderPolicy;
use Illuminate\Database\Eloquent\Attributes\UsePolicy;
use Illuminate\Database\Eloquent\Model;
 
#[UsePolicy(OrderPolicy::class)]
class Order extends Model
{
    //
}

这种方式更加直观,也避免了在 AppServiceProvider 中额外注册。

编写策略

策略方法

一旦策略类(Policy)被注册之后,你就可以为每个需要授权的动作添加对应的方法。 例如,我们可以在 PostPolicy 中定义一个 update 方法,用来判断某个 App\Models\User 是否有权限更新某个 App\Models\Post 实例。

update 方法会接收一个 User 实例和一个 Post 实例作为参数,并返回 truefalse 来表示该用户是否被允许更新指定的文章。在下面的示例中,我们通过比较用户的 id 和文章的 user_id 来判断权限:

<?php
 
namespace App\Policies;
 
use App\Models\Post;
use App\Models\User;
 
class PostPolicy
{
    /**
     * 判断指定文章是否可由该用户更新。
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

你可以继续根据需要在策略中定义更多方法,用来授权不同的操作。例如,你可以定义 viewdelete 方法来控制与 Post 相关的不同权限。当然,你完全可以自由命名策略方法,不一定要使用框架默认的方法名。

如果你在通过 Artisan 命令生成策略时使用了 --model 选项,那么生成的策略类会自动包含 viewAnyviewcreateupdatedeleterestoreforceDelete 这些常用权限方法。

所有策略类都是通过 Laravel 的服务容器进行解析的,因此你可以在策略类的构造函数中使用类型提示来自动注入任何你需要的依赖。

策略响应

到目前为止,我们只讨论了返回简单布尔值的策略方法。然而,有时你可能希望返回更详细的授权结果,例如附带错误消息。此时,你可以在策略方法中返回一个 Illuminate\Auth\Access\Response 实例:

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * 判断用户是否可更新指定文章。
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
        ? Response::allow()
        : Response::deny('你不是这篇文章的所有者。');
}

当策略方法返回授权响应时,Gate::allows 依然只会返回一个简单的布尔值;但如果你需要获取完整的授权响应,可以使用 Gate::inspect

use Illuminate\Support\Facades\Gate;

$response = Gate::inspect('update', $post);

if ($response->allowed()) {
    // 操作被授权…
} else {
    echo $response->message();
}

当使用 Gate::authorize 方法时,如果操作未被授权,它会抛出 AuthorizationException。此时,策略返回的错误消息会自动包含在最终的 HTTP 响应中:

Gate::authorize('update', $post);

// 操作已授权…

自定义 HTTP 响应状态码

当策略方法拒绝授权时,Laravel 默认会返回 403 HTTP 状态码。但有时你可能希望返回其他状态码。你可以通过 denyWithStatus 修改拒绝授权时的状态码:

use App\Models\Post;
use App\Models<User;
use Illuminate\Auth\Access\Response;

/**
 * 判断用户是否可更新指定文章。
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
        ? Response::allow()
        : Response::denyWithStatus(404);
}

由于在 Web 应用中,通过返回 404 隐藏资源是一种常见模式,Laravel 提供了 denyAsNotFound 这个更简洁的写法:

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

/**
 * 判断用户是否可更新指定文章。
 */
public function update(User $user, Post $post): Response
{
    return $user->id === $post->user_id
        ? Response::allow()
        : Response::denyAsNotFound();
}

这种方式让策略定义更加简洁清晰,也更符合常见的 Web 设计习惯。

无模型方法

有些策略方法只会接收到当前已认证用户的实例。这种情况最常见于对 create(创建) 动作进行授权时。 例如,在构建一个博客系统时,你可能需要判断某个用户是否有权限创建文章。在这种场景下,策略方法只需要接收一个 User 实例即可:

/**
 * 判断指定用户是否可以创建文章。
 */
public function create(User $user): bool
{
    return $user->role == 'writer';
}

这里的逻辑用于判断用户是否具备创建文章的角色权限。

访客用户

默认情况下,如果传入的 HTTP 请求不是由已认证用户发起的,那么所有的 Gate 和 Policy 都会自动返回 false。 但是,如果你希望这些授权检查在未认证用户的情况下也能继续传递到 Gate 或 Policy 中进行判断,你可以通过以下方式实现:

  • 为用户参数声明「可选」类型提示(?User
  • 或者为用户参数提供一个 null 默认值

示例代码:

<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * 判断指定用户是否能够更新该文章。
     */
    public function update(?User $user, Post $post): bool
    {
        return $user?->id === $post->user_id;
    }
}

在上述示例中,如果用户未登录,$user 将为 null,而 ?-> 安全访问运算符确保不会抛出错误。这样你可以灵活地处理未认证用户的授权逻辑。

策略过滤器

对于某些用户,你可能希望在特定的 policy 中授权其执行所有操作。 为此,你可以在 policy 中定义一个 before 方法before 方法会在 policy 中的任何其他方法之前执行,这让你有机会在正式调用目标授权方法前提前进行放行或拦截。

这一特性最常用于允许应用程序管理员执行任何操作:

use App\Models\User;

/**
 * 授权前置检查。
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true;
    }

    return null;
}

说明:

  • 若你希望对某类用户 拒绝所有授权检查,可以从 before 方法返回 false
  • 若返回 null,则授权检查会继续传递到 policy 中对应的具体方法。
before 方法 只有在 policy 类中存在与当前能力(ability)同名的方法时才会被调用

使用策略授权操作

通过用户模型

你在 Laravel 应用中使用的 App\Models\User 模型自带两个非常实用的授权方法: cancannot

这两个方法用于判断用户是否有权执行某个操作。它们接收要检查的“动作名称”(ability)以及相关的模型实例。例如,我们来判断当前用户是否有权限更新某个 App\Models\Post 模型。通常在控制器中会这样使用:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * 更新指定文章。
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        if ($request->user()->cannot('update', $post)) {
            abort(403);
        }

        // 更新文章...

        return redirect('/posts');
    }
}

如果该模型已经注册过对应的 policy,那么 can 方法会自动调用 policy 中对应的方法并返回布尔结果。 如果没有注册 policy,则 can 方法会尝试调用与动作名称匹配的闭包 Gate。

不需要模型实例的动作

有些动作(例如 create)对应的 policy 方法不需要模型实例。 这种情况下,你可以向 can 方法传递 类名 而不是模型对象。Laravel 会使用该类名决定应该使用哪个 policy 来执行授权检查:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * 创建文章。
     */
    public function store(Request $request): RedirectResponse
    {
        if ($request->user()->cannot('create', Post::class)) {
            abort(403);
        }

        // 创建文章...

        return redirect('/posts');
    }
}

这样就能优雅地处理不依赖特定模型实例的授权逻辑。

通过 Gate Facade

除了 App\Models\User 模型中提供的那些便捷授权方法之外,你也可以随时通过 Gate facade 的 authorize 方法来执行授权检查。

can 方法类似,authorize 接收要授权的“动作名称”以及相关的模型实例。如果当前用户无权执行该操作,authorize 会抛出 Illuminate\Auth\Access\AuthorizationException 异常,Laravel 的异常处理器会自动将该异常转换为一个 403 HTTP 响应

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    /**
     * 更新指定的博客文章。
     *
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        Gate::authorize('update', $post);

        // 当前用户有权限更新文章...

        return redirect('/posts');
    }
}

不需要模型实例的动作

前面提到过,有些 policy 方法(例如 create)并不依赖具体的模型实例。 在这种情况下,应当向 authorize 方法传递 类名,Laravel 会根据类名决定应用哪个 policy:

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

/**
 * 创建新的博客文章。
 *
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function create(Request $request): RedirectResponse
{
    Gate::authorize('create', Post::class);

    // 当前用户有权限创建文章...

    return redirect('/posts');
}

这样即可优雅地处理不需要特定模型实例的授权逻辑。

通过中间件

Laravel 内置了一个中间件,可在请求到达路由或控制器之前对动作进行授权。默认情况下,Illuminate\Auth\Middleware\Authorize 中间件可以通过 can 别名附加到路由上,这个别名在 Laravel 中已自动注册。下面是一个使用 can 中间件来授权用户更新文章的示例:

use App\Models\Post;

Route::put('/post/{post}', function (Post $post) {
    // 当前用户可以更新这篇文章...
})->middleware('can:update,post');

在这个示例中,我们给 can 中间件传递了两个参数:

  • 第一个参数是要授权的动作名称
  • 第二个参数是要传递给 policy 方法的路由参数

由于使用了隐式模型绑定,App\Models\Post 实例会自动传递给 policy 方法。如果用户无权执行指定操作,中间件会返回 HTTP 403 状态码


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

use App\Models\Post;

Route::put('/post/{post}', function (Post $post) {
    // 当前用户可以更新这篇文章...
})->can('update', 'post');

不需要模型实例的动作

有些 policy 方法(如 create)不依赖具体模型实例。这种情况下,可以传递 类名 给中间件,Laravel 会根据类名判断使用哪个 policy:

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

直接在字符串中指定完整类名可能比较繁琐,因此你也可以使用 can 方法:

use App\Models\Post;

Route::post('/post', function () {
    // 当前用户可以创建文章...
})->can('create', Post::class);

通过 Blade 模板

在编写 Blade 模板时,你可能希望只有在用户有权限执行某个动作时才显示页面的某部分内容。例如,你可能希望只有用户可以更新某篇文章时才显示更新表单。在这种情况下,可以使用 @can@cannot 指令:

@can('update', $post)
    <!-- 当前用户可以更新这篇文章 -->
@elsecan('create', App\Models\Post::class)
    <!-- 当前用户可以创建新文章 -->
@else
    <!-- 其他情况 -->
@endcan

@cannot('update', $post)
    <!-- 当前用户无法更新这篇文章 -->
@elsecannot('create', App\Models\Post::class)
    <!-- 当前用户无法创建新文章 -->
@endcannot

这些指令是对 @if@unless 的便捷封装。上面的 @can@cannot 指令等价于以下写法:

@if (Auth::user()->can('update', $post))
    <!-- 当前用户可以更新这篇文章 -->
@endif

@unless (Auth::user()->can('update', $post))
    <!-- 当前用户无法更新这篇文章 -->
@endunless

你也可以判断用户是否有权限执行给定动作数组中的任意一个动作,使用 @canany 指令:

@canany(['update', 'view', 'delete'], $post)
    <!-- 当前用户可以更新、查看或删除这篇文章 -->
@elsecanany(['create'], \App\Models\Post::class)
    <!-- 当前用户可以创建文章 -->
@endcanany

不需要模型实例的动作

像其他授权方法一样,如果动作不依赖具体模型实例,你可以传递类名给 @can@cannot 指令:

@can('create', App\Models\Post::class)
    <!-- 当前用户可以创建文章 -->
@endcan

@cannot('create', App\Models\Post::class)
    <!-- 当前用户无法创建文章 -->
@endcannot

提供额外上下文

在使用策略(Policies)进行动作授权时,你可以将一个数组作为各类授权函数或辅助方法的第二个参数。数组的第一个元素用于确定应该调用哪个策略(Policy),而数组的其余元素将作为参数传递给策略方法,可用于在做授权决策时提供额外的上下文。例如,考虑以下 PostPolicy 方法定义,它包含一个额外的 $category 参数:

/**
 * 判断给定的文章是否可以被用户更新。
 */
public function update(User $user, Post $post, int $category): bool
{
    return $user->id === $post->user_id &&
           $user->canUpdateCategory($category);
}

当尝试判断认证用户是否可以更新某篇文章时,我们可以这样调用该策略方法:

/**
 * 更新指定的博客文章。
 *
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update', [$post, $request->category]);

    // 当前用户可以更新该博客文章...

    return redirect('/posts');
}

这个机制允许你在授权决策中传递额外的上下文信息,而不仅仅局限于模型实例。

授权与 Inertia

虽然授权必须始终在服务器端处理,但通常将授权信息提供给前端应用以便正确渲染 UI 会非常方便。Laravel 并未规定向基于 Inertia 的前端暴露授权信息的固定规范。

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

<?php

namespace App\Http\Middleware;

use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    // ...

    /**
     * 定义默认共享的 props。
     *
     * @return array<string, mixed>
     */
    public function share(Request $request)
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
                'permissions' => [
                    'post' => [
                        'create' => $request->user()->can('create', Post::class),
                    ],
                ],
            ],
        ];
    }
}

通过这种方式,你可以在前端轻松访问用户的授权信息,从而根据用户权限动态渲染页面元素,例如显示或隐藏按钮、菜单项等。