Laravel 圣殿

Introduction

Laravel 圣殿 为 SPA(单页应用程序)、移动应用程序和简单的基于令牌的 API 提供轻量级身份验证系统。 Sanctum 允许您应用程序的每个用户为其帐户生成多个 API 令牌。这些令牌可能被授予能力/范围,这些能力/范围指定允许令牌执行哪些操作。

怎么运行的

Laravel Sanctum 的存在是为了解决两个不同的问题。在深入研究图书馆之前,让我们先讨论一下。

API令牌

首先,Sanctum 是一个简单的包,您可以使用它向您的用户发布 API 令牌,而无需 OAuth 的复杂性。此功能的灵感来自 GitHub 和其他发布“个人访问令牌”的应用程序。例如,假设您的应用程序的“帐户设置”有一个屏幕,用户可以在其中为其帐户生成 API 令牌。您可以使用 Sanctum 来生成和管理这些令牌。这些令牌通常有很长的到期时间(年),但可以随时由用户手动撤销。

Laravel Sanctum 通过将用户 API 令牌存储在单个数据库表中并通过Authorization 标头应包含有效的 API 令牌。

SPA认证

其次,Sanctum 的存在是为了提供一种简单的方法来验证需要与 Laravel 支持的 API 进行通信的单页应用程序 (SPA)。这些 SPA 可能存在于与 Laravel 应用程序相同的存储库中,或者可能是一个完全独立的存储库,例如使用 Vue CLI 或 Next.js 应用程序创建的 SPA。

对于此功能,Sanctum 不使用任何类型的代币。相反,Sanctum 使用 Laravel 内置的基于 cookie 的会话身份验证服务。通常,Sanctum 使用 Laravel 的web authentication guard 来完成这个。这提供了 CSRF 保护、会话身份验证以及防止通过 XSS 泄露身份验证凭据的好处。

当传入请求来自您自己的 SPA 前端时,Sanctum 只会尝试使用 cookie 进行身份验证。当 Sanctum 检查传入的 HTTP 请求时,它将首先检查身份验证 cookie,如果不存在,Sanctum 将检查Authorization 有效 API 令牌的标头。

Note
将 Sanctum 仅用于 API 令牌身份验证或仅用于 SPA 身份验证是完全可以的。仅仅因为您使用 Sanctum 并不意味着您必须使用它提供的两种功能。

Installation

Note
Laravel 的最新版本已经包含了 Laravel Sanctum。但是,如果您的应用程序的composer.json 文件不包括laravel/sanctum,您可以按照下面的安装说明进行操作。

你可以通过 Composer 包管理器安装 Laravel Sanctum:

composer require laravel/sanctum

接下来,您应该使用以下方法发布 Sanctum 配置和迁移文件vendor:publish 工匠命令。这sanctum 配置文件将放置在您的应用程序的config 目录:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

最后,您应该运行数据库迁移。 Sanctum 将创建一个数据库表来存储 API 令牌:

php artisan migrate

接下来,如果您计划使用 Sanctum 来验证 SPA,您应该将 Sanctum 的中间件添加到您的api 应用程序中的中间件组app/Http/Kernel.php 文件:

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

迁移定制

如果你不打算使用 Sanctum 的默认迁移,你应该调用Sanctum::ignoreMigrations 中的方法register 你的方法App\Providers\AppServiceProvider 班级。您可以通过执行以下命令导出默认迁移:php artisan vendor:publish --tag=sanctum-migrations

Configuration

覆盖默认模型

虽然通常不需要,但您可以自由扩展PersonalAccessToken Sanctum 内部使用的模型:

use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    // ...
}

然后,您可以通过以下方式指示 Sanctum 使用您的自定义模型usePersonalAccessTokenModel Sanctum提供的方法。通常,您应该在boot 您的应用程序服务提供商之一的方法:

use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}

API 令牌认证

Note
您不应使用 API 令牌来验证您自己的第一方 SPA。相反,使用 Sanctum 的内置SPA 身份验证功能.

发行 API 令牌

Sanctum 允许您发布 API 令牌/个人访问令牌,这些令牌可用于对您的应用程序的 API 请求进行身份验证。使用 API 令牌发出请求时,令牌应包含在Authorization 标头作为Bearer 令牌。

要开始为用户发行令牌,您的用户模型应该使用Laravel\Sanctum\HasApiTokens 特征:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

要发行令牌,您可以使用createToken 方法。这createToken 方法返回一个Laravel\Sanctum\NewAccessToken 实例。 API 令牌在存储到数据库之前使用 SHA-256 哈希算法进行哈希处理,但您可以使用plainTextToken 的财产NewAccessToken 实例。您应该在创建令牌后立即向用户显示此值:

use Illuminate\Http\Request;

Route::post('/tokens/create', function (Request $request) {
    $token = $request->user()->createToken($request->token_name);

    return ['token' => $token->plainTextToken];
});

您可以使用访问所有用户的令牌tokens 雄辩的关系由HasApiTokens 特征:

foreach ($user->tokens as $token) {
    // ...
}

令牌能力

Sanctum 允许您将“能力”分配给代币。能力与 OAuth 的“范围”具有相似的目的。您可以将一组字符串能力作为第二个参数传递给createToken 方法:

return $user->createToken('token-name', ['server:update'])->plainTextToken;

在处理由 Sanctum 验证的传入请求时,您可以使用tokenCan 方法:

if ($user->tokenCan('server:update')) {
    // ...
}

通证能力中间件

Sanctum 还包括两个中间件,可用于验证传入请求是否已使用已授予给定能力的令牌进行身份验证。首先,将以下中间件添加到$middlewareAliases 您的应用程序的属性app/Http/Kernel.php 文件:

'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,

abilities 可以将中间件分配给路由以验证传入请求的令牌是否具有列出的所有功能:

Route::get('/orders', function () {
    // Token has both "check-status" and "place-orders" abilities...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);

ability 可以将中间件分配给路由以验证传入请求的令牌是否具有最后一个 列出的能力:

Route::get('/orders', function () {
    // Token has the "check-status" or "place-orders" ability...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);

第一方 UI 发起的请求

为了方便起见,tokenCan 方法总是会返回true 如果传入的经过身份验证的请求来自您的第一方 SPA,并且您正在使用 Sanctum 的内置SPA认证.

但是,这并不一定意味着您的应用程序必须允许用户执行该操作。通常,您的应用程序的授权政策 将确定令牌是否已被授予执行能力的权限,并检查用户实例本身是否应被允许执行该操作。

例如,如果我们想象一个管理服务器的应用程序,这可能意味着检查令牌是否有权更新服务器and 服务器属于用户:

return $request->user()->id === $server->user_id &&
       $request->user()->tokenCan('server:update')

起初,允许tokenCan 方法被调用并总是返回true 对于第一方 UI 发起的请求可能看起来很奇怪;但是,能够始终假定 API 令牌可用并且可以通过tokenCan 方法。通过采用这种方法,您可以随时调用tokenCan 应用程序授权策略中的方法,而不必担心请求是从应用程序的 UI 触发的还是由 API 的第三方消费者之一发起的。

保护路线

为了保护路由以便所有传入请求都必须经过身份验证,您应该附加sanctum 身份验证保护到您的受保护路线routes/web.phproutes/api.php 路由文件。这个守卫将确保传入的请求被验证为有状态的、cookie 验证的请求,或者如果请求来自第三方,则包含有效的 API 令牌标头。

您可能想知道为什么我们建议您对应用程序中的路由进行身份验证routes/web.php 文件使用sanctum 警卫。请记住,Sanctum 将首先尝试使用 Laravel 的典型会话身份验证 cookie 来验证传入的请求。如果该 cookie 不存在,则 Sanctum 将尝试使用请求中的令牌对请求进行身份验证Authorization标头。此外,使用 Sanctum 验证所有请求可确保我们可以始终调用tokenCan 当前经过身份验证的用户实例上的方法:

use Illuminate\Http\Request;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

撤销令牌

您可以通过使用从数据库中删除它们来“撤销”令牌tokens 提供的关系Laravel\Sanctum\HasApiTokens 特征:

// Revoke all tokens...
$user->tokens()->delete();

// Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

令牌过期

默认情况下,Sanctum 令牌永不过期,只能通过撤销令牌.但是,如果您想为应用程序的 API 令牌配置过期时间,您可以通过expiration 在您的应用程序中定义的配置选项sanctum 配置文件。此配置选项定义了发出的令牌被视为过期之前的分钟数:

'expiration' => 525600,

如果您为您的应用程序配置了令牌过期时间,您可能还希望安排任务 修剪应用程序的过期令牌。值得庆幸的是,Sanctum 包含一个sanctum:prune-expired 您可以用来完成此操作的 Artisan 命令。例如,您可以配置一个计划任务来删除所有已过期至少 24 小时的过期令牌数据库记录:

$schedule->command('sanctum:prune-expired --hours=24')->daily();

SPA认证

Sanctum 还提供了一种简单的方法来验证需要与 Laravel 支持的 API 进行通信的单页应用程序 (SPA)。这些 SPA 可能存在于与 Laravel 应用程序相同的存储库中,也可能是一个完全独立的存储库。

对于此功能,Sanctum 不使用任何类型的代币。相反,Sanctum 使用 Laravel 内置的基于 cookie 的会话身份验证服务。这种身份验证方法提供了 CSRF 保护、会话身份验证以及防止通过 XSS 泄露身份验证凭据的好处。

Warning
为了进行身份验证,您的 SPA 和 API 必须共享相同的顶级域。但是,它们可能位于不同的子域中。此外,您应确保发送Accept: application/json 标题与您的请求。

Configuration

配置您的第一方域

首先,您应该配置您的 SPA 将从哪些域发出请求。您可以使用stateful 你的配置选项sanctum 配置文件。此配置设置确定在向您的 API 发出请求时哪些域将使用 Laravel 会话 cookie 维护“有状态”身份验证。

Warning
如果您通过包含端口 (127.0.0.1:8000),您应该确保在域中包含端口号。

圣所中间件

接下来,您应该将 Sanctum 的中间件添加到您的api 你的中间件组app/Http/Kernel.php 文件。这个中间件负责确保来自 SPA 的传入请求可以使用 Laravel 的会话 cookie 进行身份验证,同时仍然允许来自第三方或移动应用程序的请求使用 API 令牌进行身份验证:

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

CORS 和 Cookie

如果您无法通过在单独子域上执行的 SPA 对您的应用程序进行身份验证,则可能是您的 CORS(跨源资源共享)或会话 cookie 设置配置错误。

您应该确保您的应用程序的 CORS 配置返回Access-Control-Allow-Credentials 标头值为True.这可以通过设置supports_credentials 应用程序中的选项config/cors.php 配置文件到true.

此外,您应该启用withCredentials 应用程序全局选项axios 实例。通常,这应该在您的resources/js/bootstrap.js 文件。如果您不使用 Axios 从您的前端发出 HTTP 请求,您应该在您自己的 HTTP 客户端上执行等效配置:

axios.defaults.withCredentials = true;

最后,您应该确保应用程序的会话 cookie 域配置支持根域的任何子域。您可以通过在域前加上前缀来完成此操作. 在你的应用程序中config/session.php 配置文件:

'domain' => '.domain.com',

Authenticating

CSRF保护

要验证您的 SPA,您的 SPA 的“登录”页面应首先向/sanctum/csrf-cookie 为应用程序初始化 CSRF 保护的端点:

axios.get('/sanctum/csrf-cookie').then(response => {
    // Login...
});

在此请求期间,Laravel 将设置一个XSRF-TOKEN 包含当前 CSRF 令牌的 cookie。然后应该将此令牌传递给X-XSRF-TOKEN后续请求的标头,一些 HTTP 客户端库(如 Axios 和 Angular HttpClient)会自动为您完成。如果您的 JavaScript HTTP 库没有为您设置值,您将需要手动设置X-XSRF-TOKEN 标头以匹配的值XSRF-TOKEN 此路由设置的 cookie。

在登录

一旦 CSRF 保护被初始化,你应该做一个POST 请求你的 Laravel 应用程序/login 路线。这/login 路线可能是手动实施 或者使用像这样的无头身份验证包Laravel 强化.

如果登录请求成功,您将通过身份验证,随后对您应用程序路由的请求将通过 Laravel 应用程序向您的客户端发出的会话 cookie 自动进行身份验证。此外,由于您的申请已经向/sanctum/csrf-cookie 路由,只要您的 JavaScript HTTP 客户端发送XSRF-TOKEN 饼干在X-XSRF-TOKEN 标头。

当然,如果你的用户会话由于缺乏活动而过期,后续对 Laravel 应用程序的请求可能会收到 401 或 419 HTTP 错误响应。在这种情况下,您应该将用户重定向到 SPA 的登录页面。

Warning
您可以自由编写自己的/login 端点;但是,您应该确保它使用标准对用户进行身份验证,Laravel 提供的基于会话的身份验证服务.通常,这意味着使用web 身份验证卫士。

保护路线

为了保护路由以便所有传入请求都必须经过身份验证,您应该附加sanctum 您的 API 路由的身份验证保护routes/api.php 文件。该守卫将确保传入请求被验证为来自您的 SPA 的有状态验证请求,或者如果请求来自第三方,则包含有效的 API 令牌标头:

use Illuminate\Http\Request;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

授权私人广播频道

如果您的 SPA 需要通过身份验证私人/在线广播频道,你应该把Broadcast::routes 你的方法调用routes/api.php 文件:

Broadcast::routes(['middleware' => ['auth:sanctum']]);

接下来,为了让 Pusher 的授权请求成功,你需要提供一个自定义的 Pusherauthorizer 初始化时Laravel 回声.这允许您的应用程序将 Pusher 配置为使用axios 实例是为跨域请求正确配置:

window.Echo = new Echo({
    broadcaster: "pusher",
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    encrypted: true,
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    authorizer: (channel, options) => {
        return {
            authorize: (socketId, callback) => {
                axios.post('/api/broadcasting/auth', {
                    socket_id: socketId,
                    channel_name: channel.name
                })
                .then(response => {
                    callback(false, response.data);
                })
                .catch(error => {
                    callback(true, error);
                });
            }
        };
    },
})

移动应用认证

您还可以使用 Sanctum 令牌来验证您的移动应用程序对 API 的请求。验证移动应用程序请求的过程类似于验证第三方 API 请求;但是,您将如何发布 API 令牌存在细微差别。

发行 API 令牌

首先,创建一个接受用户电子邮件/用户名、密码和设备名称的路由,然后用这些凭据交换新的 Sanctum 令牌。为该端点提供的“设备名称”仅供参考,可以是您希望的任何值。一般来说,设备名称值应该是用户可以识别的名称,例如“Nuno's iPhone 12”。

通常,您将从移动应用程序的“登录”屏幕向令牌端点发出请求。端点将返回纯文本 API 令牌,然后可以将其存储在移动设备上并用于发出其他 API 请求:

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

Route::post('/sanctum/token', function (Request $request) {
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device_name' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

    return $user->createToken($request->device_name)->plainTextToken;
});

当移动应用程序使用令牌向您的应用程序发出 API 请求时,它应该在Authorization 标头作为Bearer 令牌。

Note
为移动应用程序发行令牌时,您还可以自由指定令牌能力.

保护路线

如前所述,您可以保护路由,以便所有传入请求都必须通过附加sanctum 路由的身份验证保护:

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

撤销令牌

要允许用户撤销颁发给移动设备的 API 令牌,您可以在 Web 应用程序 UI 的“帐户设置”部分中按名称列出它们以及“撤销”按钮。当用户点击“撤销”按钮时,就可以从数据库中删除令牌。请记住,您可以通过tokens提供的关系Laravel\Sanctum\HasApiTokens 特征:

// Revoke all tokens...
$user->tokens()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

Testing

在测试时,Sanctum::actingAs 方法可用于对用户进行身份验证并指定应向其令牌授予哪些能力:

use App\Models\User;
use Laravel\Sanctum\Sanctum;

public function test_task_list_can_be_retrieved(): void
{
    Sanctum::actingAs(
        User::factory()->create(),
        ['view-tasks']
    );

    $response = $this->get('/api/task');

    $response->assertOk();
}

如果您想授予令牌所有能力,您应该包括* 在提供给的能力列表中actingAs 方法:

Sanctum::actingAs(
    User::factory()->create(),
    ['*']
);
豫ICP备18041297号-2