Skip to content
虚位以待
赞助商赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

Laravel Passport

介绍

Laravel 已经使通过传统登录表单进行身份验证变得简单,但对于 API 呢?API 通常使用令牌来验证用户,并且在请求之间不维护会话状态。Laravel 使用 Laravel Passport 使 API 身份验证变得轻而易举,Passport 为您的 Laravel 应用程序提供了完整的 OAuth2 服务器实现,只需几分钟即可完成。Passport 构建在由 Andy Millington 和 Simon Hamp 维护的 League OAuth2 服务器 之上。

NOTE

本文档假设您已经熟悉 OAuth2。如果您对 OAuth2 一无所知,请考虑在继续之前熟悉 OAuth2 的一般术语和功能。

安装

要开始,请通过 Composer 包管理器安装 Passport:

php
composer require laravel/passport:7.5.1

Passport 服务提供者会将其自己的数据库迁移目录注册到框架中,因此在安装包后,您应该迁移数据库。Passport 迁移将创建您的应用程序需要的表来存储客户端和访问令牌:

php
php artisan migrate

接下来,您应该运行 passport:install 命令。此命令将创建生成安全访问令牌所需的加密密钥。此外,该命令将创建“个人访问”和“密码授权”客户端,这些客户端将用于生成访问令牌:

php
php artisan passport:install

运行此命令后,将 Laravel\Passport\HasApiTokens trait 添加到您的 App\User 模型中。此 trait 将为您的模型提供一些辅助方法,允许您检查已验证用户的令牌和范围:

php
<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

接下来,您应该在 AuthServiceProviderboot 方法中调用 Passport::routes 方法。此方法将注册颁发访问令牌和撤销访问令牌、客户端和个人访问令牌所需的路由:

php
<?php

namespace App\Providers;

use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 应用程序的策略映射。
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * 注册任何身份验证/授权服务。
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

最后,在您的 config/auth.php 配置文件中,您应该将 api 身份验证守卫的 driver 选项设置为 passport。这将指示您的应用程序在验证传入的 API 请求时使用 Passport 的 TokenGuard

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

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

迁移自定义

如果您不打算使用 Passport 的默认迁移,您应该在 AppServiceProviderregister 方法中调用 Passport::ignoreMigrations 方法。您可以使用 php artisan vendor:publish --tag=passport-migrations 导出默认迁移。

默认情况下,Passport 使用整数列来存储 user_id。如果您的应用程序使用不同的列类型来标识用户(例如:UUID),您应该在发布后修改默认的 Passport 迁移。

前端快速入门

NOTE

为了使用 Passport Vue 组件,您必须使用 Vue JavaScript 框架。这些组件还使用 Bootstrap CSS 框架。然而,即使您不使用这些工具,这些组件也可以作为您自己前端实现的有价值的参考。

Passport 附带一个 JSON API,您可以使用它来允许用户创建客户端和个人访问令牌。然而,编写一个与这些 API 交互的前端可能会耗费时间。因此,Passport 还包括预构建的 Vue 组件,您可以将其用作示例实现或您自己实现的起点。

要发布 Passport Vue 组件,请使用 vendor:publish Artisan 命令:

php
php artisan vendor:publish --tag=passport-components

发布的组件将放置在您的 resources/js/components 目录中。组件发布后,您应该在 resources/js/app.js 文件中注册它们:

php
Vue.component(
    'passport-clients',
    require('./components/passport/Clients.vue').default
);

Vue.component(
    'passport-authorized-clients',
    require('./components/passport/AuthorizedClients.vue').default
);

Vue.component(
    'passport-personal-access-tokens',
    require('./components/passport/PersonalAccessTokens.vue').default
);

NOTE

在 Laravel v5.7.19 之前,注册组件时附加 .default 会导致控制台错误。对此更改的解释可以在 Laravel Mix v4.0.0 发布说明 中找到。

注册组件后,请确保运行 npm run dev 以重新编译您的资产。重新编译资产后,您可以将组件放入应用程序的模板之一,以开始创建客户端和个人访问令牌:

php
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>

部署 Passport

首次将 Passport 部署到生产服务器时,您可能需要运行 passport:keys 命令。此命令生成 Passport 生成访问令牌所需的加密密钥。生成的密钥通常不保存在源代码控制中:

php
php artisan passport:keys

如有必要,您可以定义 Passport 的密钥应从何处加载。您可以使用 Passport::loadKeysFrom 方法来实现这一点:

php
/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::loadKeysFrom('/secret-keys/oauth');
}

此外,您可以使用 php artisan vendor:publish --tag=passport-config 发布 Passport 的配置文件,然后可以选择从环境变量加载加密密钥:

php
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"

PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"

配置

令牌有效期

默认情况下,Passport 颁发的访问令牌有效期为一年。如果您希望配置更长/更短的令牌有效期,可以使用 tokensExpireInrefreshTokensExpireInpersonalAccessTokensExpireIn 方法。这些方法应在 AuthServiceProviderboot 方法中调用:

php
/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::tokensExpireIn(now()->addDays(15));

    Passport::refreshTokensExpireIn(now()->addDays(30));

    Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}

覆盖默认模型

您可以自由扩展 Passport 内部使用的模型。然后,您可以通过 Passport 类指示 Passport 使用您的自定义模型:

php
use App\Models\Passport\Client;
use App\Models\Passport\Token;
use App\Models\Passport\AuthCode;
use App\Models\Passport\PersonalAccessClient;

/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::useTokenModel(Token::class);
    Passport::useClientModel(Client::class);
    Passport::useAuthCodeModel(AuthCode::class);
    Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}

颁发访问令牌

使用授权码的 OAuth2 是大多数开发人员熟悉的 OAuth2 方式。使用授权码时,客户端应用程序将用户重定向到您的服务器,用户将在那里批准或拒绝向客户端颁发访问令牌的请求。

管理客户端

首先,构建需要与您的应用程序的 API 交互的应用程序的开发人员需要通过创建“客户端”来注册他们的应用程序。通常,这包括提供他们应用程序的名称和一个 URL,您的应用程序可以在用户批准他们的授权请求后重定向到该 URL。

passport:client 命令

创建客户端的最简单方法是使用 passport:client Artisan 命令。此命令可用于为测试您的 OAuth2 功能创建您自己的客户端。当您运行 client 命令时,Passport 将提示您提供有关客户端的更多信息,并为您提供客户端 ID 和密钥:

php
php artisan passport:client

重定向 URL

如果您希望为客户端白名单多个重定向 URL,可以在 passport:client 命令提示您输入 URL 时使用逗号分隔的列表指定它们:

php
http://example.com/callback,http://examplefoo.com/callback

NOTE

任何包含逗号的 URL 必须进行编码。

JSON API

由于您的用户将无法使用 client 命令,Passport 提供了一个 JSON API,您可以使用它来创建客户端。这为您节省了手动编写控制器以创建、更新和删除客户端的麻烦。

但是,您需要将 Passport 的 JSON API 与您自己的前端配对,以为您的用户提供一个仪表板来管理他们的客户端。下面,我们将回顾管理客户端的所有 API 端点。为了方便起见,我们将使用 Axios 来演示向端点发出 HTTP 请求。

JSON API 由 webauth 中间件保护;因此,它只能从您自己的应用程序调用。无法从外部来源调用。

NOTE

如果您不想自己实现整个客户端管理前端,可以使用 前端快速入门 在几分钟内拥有一个功能齐全的前端。

GET /oauth/clients

此路由返回已验证用户的所有客户端。这主要用于列出用户的所有客户端,以便他们可以编辑或删除它们:

php
axios.get('/oauth/clients')
    .then(response => {
        console.log(response.data);
    });

POST /oauth/clients

此路由用于创建新客户端。它需要两条数据:客户端的 name 和一个 redirect URL。redirect URL 是用户在批准或拒绝授权请求后将被重定向到的地方。

创建客户端时,将为其分配一个客户端 ID 和客户端密钥。这些值将在请求访问令牌时使用。客户端创建路由将返回新的客户端实例:

php
const data = {
    name: 'Client Name',
    redirect: 'http://example.com/callback'
};

axios.post('/oauth/clients', data)
    .then(response => {
        console.log(response.data);
    })
    .catch (response => {
        // 列出响应中的错误...
    });

PUT /oauth/clients/{client-id}

此路由用于更新客户端。它需要两条数据:客户端的 name 和一个 redirect URL。redirect URL 是用户在批准或拒绝授权请求后将被重定向到的地方。该路由将返回更新后的客户端实例:

php
const data = {
    name: 'New Client Name',
    redirect: 'http://example.com/callback'
};

axios.put('/oauth/clients/' + clientId, data)
    .then(response => {
        console.log(response.data);
    })
    .catch (response => {
        // 列出响应中的错误...
    });

DELETE /oauth/clients/{client-id}

此路由用于删除客户端:

php
axios.delete('/oauth/clients/' + clientId)
    .then(response => {
        //
    });

请求令牌

重定向以进行授权

创建客户端后,开发人员可以使用他们的客户端 ID 和密钥从您的应用程序请求授权码和访问令牌。首先,消费应用程序应向您的应用程序的 /oauth/authorize 路由发出重定向请求,如下所示:

php
Route::get('/redirect', function (Request $request) {
    $request->session()->put('state', $state = Str::random(40));

    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'code',
        'scope' => '',
        'state' => $state,
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

NOTE

请记住,/oauth/authorize 路由已由 Passport::routes 方法定义。您无需手动定义此路由。

批准请求

在接收授权请求时,Passport 将自动向用户显示一个模板,允许他们批准或拒绝授权请求。如果他们批准请求,他们将被重定向回消费应用程序指定的 redirect_uriredirect_uri 必须与创建客户端时指定的 redirect URL 匹配。

如果您想自定义授权批准屏幕,可以使用 vendor:publish Artisan 命令发布 Passport 的视图。发布的视图将放置在 resources/views/vendor/passport 中:

php
php artisan vendor:publish --tag=passport-views

有时您可能希望跳过授权提示,例如在授权第一方客户端时。您可以通过在客户端模型上定义 skipsAuthorization 方法来实现这一点。如果 skipsAuthorization 返回 true,则客户端将被批准,用户将立即被重定向回 redirect_uri

php
<?php

namespace App\Models\Passport;

use Laravel\Passport\Client as BaseClient;

class Client extends BaseClient
{
    /**
     * 确定客户端是否应跳过授权提示。
     *
     * @return bool
     */
    public function skipsAuthorization()
    {
        return $this->firstParty();
    }
}

将授权码转换为访问令牌

如果用户批准授权请求,他们将被重定向回消费应用程序。消费者应首先验证 state 参数与重定向前存储的值是否匹配。如果状态参数匹配,消费者应向您的应用程序发出 POST 请求以请求访问令牌。请求应包括用户批准授权请求时您的应用程序颁发的授权码。在此示例中,我们将使用 Guzzle HTTP 库发出 POST 请求:

php
Route::get('/callback', function (Request $request) {
    $state = $request->session()->pull('state');

    throw_unless(
        strlen($state) > 0 && $state === $request->state,
        InvalidArgumentException::class
    );

    $http = new GuzzleHttp\Client;

    $response = $http->post('http://your-app.com/oauth/token', [
        'form_params' => [
            'grant_type' => 'authorization_code',
            'client_id' => 'client-id',
            'client_secret' => 'client-secret',
            'redirect_uri' => 'http://example.com/callback',
            'code' => $request->code,
        ],
    ]);

    return json_decode((string) $response->getBody(), true);
});

/oauth/token 路由将返回一个包含 access_tokenrefresh_tokenexpires_in 属性的 JSON 响应。expires_in 属性包含访问令牌过期的秒数。

NOTE

/oauth/authorize 路由一样,/oauth/token 路由由 Passport::routes 方法为您定义。无需手动定义此路由。默认情况下,此路由使用 ThrottleRequests 中间件的设置进行限流。

刷新令牌

如果您的应用程序颁发短期访问令牌,用户将需要通过颁发访问令牌时提供给他们的刷新令牌来刷新他们的访问令牌。在此示例中,我们将使用 Guzzle HTTP 库刷新令牌:

php
$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'refresh_token',
        'refresh_token' => 'the-refresh-token',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'scope' => '',
    ],
]);

return json_decode((string) $response->getBody(), true);

/oauth/token 路由将返回一个包含 access_tokenrefresh_tokenexpires_in 属性的 JSON 响应。expires_in 属性包含访问令牌过期的秒数。

密码授权令牌

OAuth2 密码授权允许您的其他第一方客户端(例如移动应用程序)使用电子邮件地址/用户名和密码获取访问令牌。这允许您安全地向第一方客户端颁发访问令牌,而无需用户通过整个 OAuth2 授权码重定向流程。

创建密码授权客户端

在您的应用程序可以通过密码授权颁发令牌之前,您需要创建一个密码授权客户端。您可以使用 passport:client 命令和 --password 选项来完成此操作。如果您已经运行了 passport:install 命令,则无需运行此命令:

php
php artisan passport:client --password

请求令牌

创建密码授权客户端后,您可以通过向 /oauth/token 路由发出 POST 请求并提供用户的电子邮件地址和密码来请求访问令牌。请记住,此路由已由 Passport::routes 方法注册,因此无需手动定义。如果请求成功,您将收到服务器返回的 JSON 响应中的 access_tokenrefresh_token

php
$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => 'taylor@laravel.com',
        'password' => 'my-password',
        'scope' => '',
    ],
]);

return json_decode((string) $response->getBody(), true);

NOTE

请记住,访问令牌默认是长期有效的。但是,如果需要,您可以自由配置最大访问令牌有效期

请求所有范围

使用密码授权或客户端凭证授权时,您可能希望为应用程序支持的所有范围授权令牌。您可以通过请求 * 范围来实现这一点。如果您请求 * 范围,令牌实例上的 can 方法将始终返回 true。此范围只能分配给使用 passwordclient_credentials 授权颁发的令牌:

php
$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => 'taylor@laravel.com',
        'password' => 'my-password',
        'scope' => '*',
    ],
]);

自定义用户名字段

使用密码授权进行身份验证时,Passport 将使用模型的 email 属性作为“用户名”。但是,您可以通过在模型上定义 findForPassport 方法来自定义此行为:

php
<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
     * 查找给定用户名的用户实例。
     *
     * @param  string  $username
     * @return \App\User
     */
    public function findForPassport($username)
    {
        return $this->where('username', $username)->first();
    }
}

自定义密码验证

使用密码授权进行身份验证时,Passport 将使用模型的 password 属性来验证给定的密码。如果您的模型没有 password 属性或您希望自定义密码验证逻辑,可以在模型上定义 validateForPassportPasswordGrant 方法:

php
<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Support\Facades\Hash;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
    * 验证用户的 Passport 密码授权密码。
    *
    * @param  string $password
    * @return bool
    */
    public function validateForPassportPasswordGrant($password)
    {
        return Hash::check($password, $this->password);
    }
}

隐式授权令牌

隐式授权类似于授权码授权;然而,令牌在不交换授权码的情况下返回给客户端。此授权最常用于 JavaScript 或移动应用程序,其中客户端凭证无法安全存储。要启用此授权,请在 AuthServiceProvider 中调用 enableImplicitGrant 方法:

php
/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::enableImplicitGrant();
}

启用授权后,开发人员可以使用他们的客户端 ID 从您的应用程序请求访问令牌。消费应用程序应向您的应用程序的 /oauth/authorize 路由发出重定向请求,如下所示:

php
Route::get('/redirect', function (Request $request) {
    $request->session()->put('state', $state = Str::random(40));

    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'token',
        'scope' => '',
        'state' => $state,
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

NOTE

请记住,/oauth/authorize 路由已由 Passport::routes 方法定义。您无需手动定义此路由。

客户端凭证授权令牌

客户端凭证授权适用于机器对机器的身份验证。例如,您可以在计划任务中使用此授权,该任务通过 API 执行维护任务。

在您的应用程序可以通过客户端凭证授权颁发令牌之前,您需要创建一个客户端凭证授权客户端。您可以使用 passport:client 命令的 --client 选项来完成此操作:

php
php artisan passport:client --client

接下来,要使用此授权类型,您需要将 CheckClientCredentials 中间件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 属性中:

php
use Laravel\Passport\Http\Middleware\CheckClientCredentials;

protected $routeMiddleware = [
    'client' => CheckClientCredentials::class,
];

然后,将中间件附加到路由:

php
Route::get('/orders', function (Request $request) {
    ...
})->middleware('client');

要将对路由的访问限制为特定范围,您可以在将 client 中间件附加到路由时提供所需范围的逗号分隔列表:

php
Route::get('/orders', function (Request $request) {
    ...
})->middleware('client:check-status,your-scope');

检索令牌

要使用此授权类型检索令牌,请向 oauth/token 端点发出请求:

php
$guzzle = new GuzzleHttp\Client;

$response = $guzzle->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'client_credentials',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'scope' => 'your-scope',
    ],
]);

return json_decode((string) $response->getBody(), true)['access_token'];

个人访问令牌

有时,您的用户可能希望在不通过典型的授权码重定向流程的情况下向自己颁发访问令牌。允许用户通过应用程序的 UI 向自己颁发令牌可以帮助用户试验您的 API,或者可以作为颁发访问令牌的一种更简单的方法。

创建个人访问客户端

在您的应用程序可以颁发个人访问令牌之前,您需要创建一个个人访问客户端。您可以使用 passport:client 命令和 --personal 选项来完成此操作。如果您已经运行了 passport:install 命令,则无需运行此命令:

php
php artisan passport:client --personal

如果您已经定义了个人访问客户端,可以使用 personalAccessClientId 方法指示 Passport 使用它。通常,此方法应在 AuthServiceProviderboot 方法中调用:

php
/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::personalAccessClientId('client-id');
}

管理个人访问令牌

创建个人访问客户端后,您可以使用 User 模型实例上的 createToken 方法为给定用户颁发令牌。createToken 方法接受令牌名称作为第一个参数,并接受一个可选的范围数组作为第二个参数:

php
$user = App\User::find(1);

// 创建没有范围的令牌...
$token = $user->createToken('Token Name')->accessToken;

// 创建具有范围的令牌...
$token = $user->createToken('My Token', ['place-orders'])->accessToken;

JSON API

Passport 还包括一个用于管理个人访问令牌的 JSON API。您可以将其与您自己的前端配对,以为您的用户提供一个仪表板来管理个人访问令牌。下面,我们将回顾管理个人访问令牌的所有 API 端点。为了方便起见,我们将使用 Axios 来演示向端点发出 HTTP 请求。

JSON API 由 webauth 中间件保护;因此,它只能从您自己的应用程序调用。无法从外部来源调用。

NOTE

如果您不想自己实现个人访问令牌前端,可以使用 前端快速入门 在几分钟内拥有一个功能齐全的前端。

GET /oauth/scopes

此路由返回为您的应用程序定义的所有范围。您可以使用此路由列出用户可以分配给个人访问令牌的范围:

php
axios.get('/oauth/scopes')
    .then(response => {
        console.log(response.data);
    });

GET /oauth/personal-access-tokens

此路由返回已验证用户创建的所有个人访问令牌。这主要用于列出用户的所有令牌,以便他们可以编辑或删除它们:

php
axios.get('/oauth/personal-access-tokens')
    .then(response => {
        console.log(response.data);
    });

POST /oauth/personal-access-tokens

此路由创建新的个人访问令牌。它需要两条数据:令牌的 name 和应分配给令牌的 scopes

php
const data = {
    name: 'Token Name',
    scopes: []
};

axios.post('/oauth/personal-access-tokens', data)
    .then(response => {
        console.log(response.data.accessToken);
    })
    .catch (response => {
        // 列出响应中的错误...
    });

DELETE /oauth/personal-access-tokens/{token-id}

此路由可用于删除个人访问令牌:

php
axios.delete('/oauth/personal-access-tokens/' + tokenId);

保护路由

通过中间件

Passport 包含一个身份验证守卫,它将在传入请求上验证访问令牌。一旦您将 api 守卫配置为使用 passport 驱动程序,您只需在任何需要有效访问令牌的路由上指定 auth:api 中间件:

php
Route::get('/user', function () {
    //
})->middleware('auth:api');

传递访问令牌

在调用由 Passport 保护的路由时,您的应用程序的 API 消费者应在请求的 Authorization 标头中将其访问令牌指定为 Bearer 令牌。例如,使用 Guzzle HTTP 库时:

php
$response = $client->request('GET', '/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer '.$accessToken,
    ],
]);

令牌范围

范围允许您的 API 客户端在请求授权访问帐户时请求特定的权限集。例如,如果您正在构建一个电子商务应用程序,并非所有 API 消费者都需要下订单的能力。相反,您可以允许消费者仅请求授权访问订单发货状态。换句话说,范围允许您的应用程序用户限制第三方应用程序可以代表他们执行的操作。

定义范围

您可以在 AuthServiceProviderboot 方法中使用 Passport::tokensCan 方法定义 API 的范围。tokensCan 方法接受一个范围名称和范围描述的数组。范围描述可以是您希望的任何内容,并将显示在授权批准屏幕上:

php
use Laravel\Passport\Passport;

Passport::tokensCan([
    'place-orders' => 'Place orders',
    'check-status' => 'Check order status',
]);

默认范围

如果客户端未请求任何特定范围,您可以配置您的 Passport 服务器以使用 setDefaultScope 方法将默认范围附加到令牌。通常,您应该在 AuthServiceProviderboot 方法中调用此方法:

php
use Laravel\Passport\Passport;

Passport::setDefaultScope([
    'check-status',
    'place-orders',
]);

为令牌分配范围

请求授权码时

使用授权码授权请求访问令牌时,消费者应将其所需的范围指定为 scope 查询字符串参数。scope 参数应为范围的空格分隔列表:

php
Route::get('/redirect', function () {
    $query = http_build_query([
        'client_id' => 'client-id',
        'redirect_uri' => 'http://example.com/callback',
        'response_type' => 'code',
        'scope' => 'place-orders check-status',
    ]);

    return redirect('http://your-app.com/oauth/authorize?'.$query);
});

颁发个人访问令牌时

如果您使用 User 模型的 createToken 方法颁发个人访问令牌,可以将所需范围的数组作为方法的第二个参数传递:

php
$token = $user->createToken('My Token', ['place-orders'])->accessToken;

检查范围

Passport 包含两个中间件,可用于验证传入请求是否使用已授予给定范围的令牌进行身份验证。要开始,请将以下中间件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 属性中:

php
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

检查所有范围

可以将 scopes 中间件分配给路由,以验证传入请求的访问令牌是否具有所有列出的范围:

php
Route::get('/orders', function () {
    // 访问令牌具有 "check-status" 和 "place-orders" 范围...
})->middleware('scopes:check-status,place-orders');

检查任何范围

可以将 scope 中间件分配给路由,以验证传入请求的访问令牌是否具有至少一个列出的范围:

php
Route::get('/orders', function () {
    // 访问令牌具有 "check-status" 或 "place-orders" 范围...
})->middleware('scope:check-status,place-orders');

在令牌实例上检查范围

一旦访问令牌验证的请求进入您的应用程序,您仍然可以使用已验证 User 实例上的 tokenCan 方法检查令牌是否具有给定范围:

php
use Illuminate\Http\Request;

Route::get('/orders', function (Request $request) {
    if ($request->user()->tokenCan('place-orders')) {
        //
    }
});

其他范围方法

scopeIds 方法将返回所有定义的 ID/名称的数组:

php
Laravel\Passport\Passport::scopeIds();

scopes 方法将返回所有定义的范围作为 Laravel\Passport\Scope 实例的数组:

php
Laravel\Passport\Passport::scopes();

scopesFor 方法将返回与给定 ID/名称匹配的 Laravel\Passport\Scope 实例的数组:

php
Laravel\Passport\Passport::scopesFor(['place-orders', 'check-status']);

您可以使用 hasScope 方法确定是否已定义给定范围:

php
Laravel\Passport\Passport::hasScope('place-orders');

使用 JavaScript 消费 API

构建 API 时,能够从 JavaScript 应用程序消费自己的 API 非常有用。这种 API 开发方法允许您的应用程序消费与您与世界共享的相同 API。相同的 API 可以被您的 Web 应用程序、移动应用程序、第三方应用程序以及您可能在各种包管理器上发布的任何 SDK 消费。

通常,如果您想从 JavaScript 应用程序消费您的 API,您需要手动将访问令牌发送到应用程序,并在每个请求中传递给您的应用程序。然而,Passport 包含一个中间件,可以为您处理这一点。您只需将 CreateFreshApiToken 中间件添加到 app/Http/Kernel.php 文件的 web 中间件组中:

php
'web' => [
    // 其他中间件...
    \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],

NOTE

您应确保 EncryptCookies 中间件在中间件堆栈中列在 CreateFreshApiToken 中间件之前。

此 Passport 中间件将向您的传出响应附加一个 laravel_token cookie。此 cookie 包含一个加密的 JWT,Passport 将使用它来验证来自 JavaScript 应用程序的 API 请求。现在,您可以在不显式传递访问令牌的情况下向应用程序的 API 发出请求:

php
axios.get('/api/user')
    .then(response => {
        console.log(response.data);
    });

如有需要,您可以使用 Passport::cookie 方法自定义 laravel_token cookie 的名称。通常,此方法应在 AuthServiceProviderboot 方法中调用:

php
/**
 * 注册任何身份验证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::cookie('custom_name');
}

CSRF 保护

使用此身份验证方法时,默认的 Laravel JavaScript 脚手架指示 Axios 始终发送 X-CSRF-TOKENX-Requested-With 标头。然而,您应确保在 HTML 元标记 中包含您的 CSRF 令牌:

php
// 在您的应用程序布局中...
<meta name="csrf-token" content="{{ csrf_token() }}">

// Laravel 的 JavaScript 脚手架...
window.axios.defaults.headers.common = {
    'X-Requested-With': 'XMLHttpRequest',
};

事件

Passport 在颁发访问令牌和刷新令牌时会触发事件。您可以使用这些事件来修剪或撤销数据库中的其他访问令牌。您可以在应用程序的 EventServiceProvider 中附加这些事件的监听器:

php
/**
 * 应用程序的事件监听器映射。
 *
 * @var array
 */
protected $listen = [
    'Laravel\Passport\Events\AccessTokenCreated' => [
        'App\Listeners\RevokeOldTokens',
    ],

    'Laravel\Passport\Events\RefreshTokenCreated' => [
        'App\Listeners\PruneOldTokens',
    ],
];

测试

Passport 的 actingAs 方法可用于指定当前已验证的用户及其范围。传递给 actingAs 方法的第一个参数是用户实例,第二个参数是应授予用户令牌的范围数组:

php
use App\User;
use Laravel\Passport\Passport;

public function testServerCreation()
{
    Passport::actingAs(
        factory(User::class)->create(),
        ['create-servers']
    );

    $response = $this->post('/api/create-server');

    $response->assertStatus(201);
}