API 认证(Passport)

简介

在 Laravel 中,实现基于传统表单的认证已经很简单了,但是如何满足 API 场景下的需求呢?API 通常使用令牌来实现用户认证,而非维护请求之间的 Session 状态。在 Laravel 中可以使用 Passport 轻松实现 API 认证,Passport 可以在几分钟之内为应用提供完整的 OAuth2 服务端实现。Passport 是基于 Andy Millington 和 Simon Hamp 维护的 League OAuth2 server 建立的。

本文档假定您已熟悉 OAuth2 。如果您并不了解 OAuth2 ,阅读之前请先熟悉下 OAuth2 的常用术语和功能。

安装

首先,通过 Composer 包管理器安装 Passport:

composer require laravel/passport

Passport 服务提供者会使用框架注册自己的数据库迁移目录,因此在注册提供者后应该执行数据库迁移。Passport 数据库迁移会创建应用用来存储客户端和访问令牌的数据表:

php artisan migrate

接下来,运行 passport:install 命令创建生成安全访问令牌时所需的加密密钥。同时,该命令也会创建用于生成访问令牌的「个人访问」和「密码授权」客户端:

php artisan passport:install

执行上面命令后,将 Laravel\Passport\HasApiTokens Trait 添加到 App\User 模型中。此 Trait 会为模型提供一些辅助方法,用于检查认证用户的令牌和作用域:

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 方法。此方法会注册发放及撤销访问令牌、客户端和个人访问令牌所必需的路由:

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 配置文件中,将 driver 选项的 api 认证看守器改为 passport。此调整会让应用在验证传入的 API 请求时使用 Passport 的 TokenGuard 来处理:

'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 默认迁移。

前端快速上手

如果想要使用 Passport 的 Vue 组件,那么必须使用 Vue JavaScript 框架。这些组件也用到了 Bootstrap CSS 框架。当然,您也可以不使用上面的这些工具,但在实现自己的前端时,这些组件仍然有很高的参考价值。

Passport 自带了一些可以让您的用户创建客户端和个人访问令牌的 JSON API。然而,编写一些前端代码来与这些 API 交互很花时间。因此,Passport 也包含了预先构建的 Vue 组件,您可以直接使用或者基于这些代码实现自己的。

使用 Artisan 命令 vendor:publish 发布 Passport 的 Vue 组件:

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

已发布的组件会被放在 resources/js/components 目录中。组件发布后,可以在 resources/js/app.js 文件中注册它们:

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

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

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

组件注册后,运行 npm run dev 来重新编译资源。编译资源完成,可以将这些组件放到应用的模板中,然后开始创建客户端和个人访问令牌:

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

部署 Passport

当第一次将 Passport 部署到生产服务器上时,需要运行 passport:keys 命令。该命令会生成 Passport 需要用来生成访问令牌的加密密钥。生成的密钥通常不保存在版本控制中:

php artisan passport:keys

如有必要,还可以定义 Passport 密钥的加载路径。使用 Passport::loadKeysFrom 方法:

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

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

配置

令牌有效期

默认情况下,Passport 会发放一个一年后过期的长期访问令牌。如果要配置一个更久/更短的令牌有效期,可以使用 tokensExpireInrefreshTokensExpireIn 方法。这些方法应该在 AuthServiceProviderboot 方法中调用:

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

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

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

重写默认模型

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

use App\Models\Passport\Client;
use App\Models\Passport\AuthCode;
use App\Models\Passport\TokenModel;
use App\Models\Passport\PersonalAccessClient;

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

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

发放访问令牌

将 OAuth2 与授权码一起使用是大多数开发者熟悉 OAuth2 的方式。当使用客户端授权码时,客户端应用会将用户重定向到您的服务器,他们将批准或拒绝向客户端发放访问令牌的请求。

管理客户端

首先,为了构建与应用 API 交互的应用,开发者需要通过创建一个「客户端」来注册自己的应用。通常来说,这包括应用的名称和用户批准授权请求后重定向的 URL。

passport:client 命令

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

php artisan passport:client
重定向 URL

如果要为客户端添加多个重定向 URL 白名单,可以在 passport:client 命令提示 URL 的时候使用逗号分隔的列表来指定它们:

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

所有包含逗号的 URL 必须被编码。

JSON API

由于您的用户无法使用 client 命令,因此 Passport 提供了可用于创建客户端的 JSON API。这样您就不用花时间手动编写控制器来创建、更新和删除客户端了。

但是,您将需要基于 Passport 的 JSON API 开发配套的前端,为用户提供管理客户端的仪表盘。下面,我们会查看所有管理客户端的 API。为了方便起见,我们将使用 Axios 来演示对端口进行 HTTP 请求。

JSON API 使用 webauth 中间件作为看守器;因此,只能在您自己的应用中调用。不能从外部调用。

如果您不想自己实现整个客户端管理的前端界面,可以使用 前端快速上手 在几分钟内创建功能齐全的前端界面。

GET /oauth/clients

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

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

POST /oauth/clients

此路由用于创建新的客户端。它需要两个数据:客户端的 nameredirect URL。当用户批准或拒绝请求后,用户会被重定向到 redirect URL。

当客户端创建后,会发放客户端 ID 和客户端密钥。这些值会在从应用请求访问令牌时使用。该路由会返回一个新的客户端实例:

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}

此路由用于更新客户端。它需要两个数据:客户端的 nameredirect URL。当用户批准或拒绝请求后,用户会被重定向到 redirect URL。该路由会返回一个更新后的客户端实例:

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}

此路由用于删除客户端:

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

请求令牌

重定向到认证

客户端创建后,开发者可以从应用中使用客户端 ID 和密钥来请求授权码和访问令牌。首先,接入应用应该向您的应用的 /oauth/authorize 路由发起重定向请求,如下:

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

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

注意,/oauth/authorize 路由已经在 Passport::routes 方法中定义。您不需要手动定义该路由。

同意请求

当接收到授权请求时,Passport 会自动向用户显示一个模板,允许用户批准或拒绝授权请求。如果用户批准请求,他们会被重定向到接入应用指定的 redirect_uriredirect_uri 必须和客户端创建时指定的 redirect URL 匹配。

如果要自定义授权确认页面,可以使用 Artisan 命令 vendor:publish 发布 Passport 的视图。发布后的视图位于 resources/views/vendor/passport

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

将授权码转换为访问令牌

如果用户批准授权请求,用户会被重定向到传入应用。然后传入应用应该发起一个 POST 请求到您的应用请求访问令牌。该请求应该包括用户批准授权请求时应用发出的授权码。在本例中,我们会使用 Guzzle HTTP 库来发起 POST 请求:

Route::get('/callback', function (Request $request) {
    $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 属性包含了访问令牌过期前的秒数。

/oauth/authorize 路由一样,/oauth/token 路由也在 Passport::routes 方法中定义了。不需要在再手动定义该路由。默认情况下,该路由使用 ThrottleRequests 中间件设置的节流限制。

刷新令牌

如果应用发放了短期的访问令牌,用户将需要通过访问令牌发放时提供给他们的刷新令牌来刷新访问令牌。在本例中,我们会使用 Guzzle HTTP 库来刷新令牌:

$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 artisan passport:client --password

请求令牌

创建密码授权客户端后,就可以带上用户的电子邮箱地址和密码向 /oauth/token 路由发起 POST 请求来请求访问令牌。注意,该路由已经通过 Passport::routes 方法注册了,因此这里不用再手动定义了。如果请求成功,会从服务器接收到一个包含 access_tokenrefresh_token 的 JSON 响应:

$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);

注意,访问令牌默认是长期的。不过,可以根据需要自由 配置访问令牌的最大有效期

请求所有作用域

当使用密码授权时,可能希望为应用支持的所有作用域授权令牌。可以通过请求 * 作用域实现此功能。如果请求 * 作用域,令牌实例上的 can 方法将始终返回 true。这种作用域只能使用 password 授权指定给发放的令牌:

$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' => '*',
    ],
]);

隐式授权令牌

隐式授权和授权码授权类似;但是,它会将令牌返回给客户端而不交换授权码。这种授权常用于不能安全存储客户端凭据的 JavaScript 或移动端应用。要启动该授权,可以在 AuthServiceProvider 中调用 enableImplicitGrant 方法:

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::enableImplicitGrant();
}

启用隐式授权后,开发者可以从应用使用客户端 ID 来请求访问令牌。接入应用应该向应用的 /oauth/authorize 路由发起重定向请求,像这样:

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

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

注意,/oauth/authorize 路由已经通过 Passport::routes 方法定义了。不用再手动定义该路由。

客户端凭据授权令牌

客户端凭据授权适用于机器到机器的授权。例如,可以在一个计划任务中使用使用该授权,计划任务通过 API 执行维护任务。

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

php artisan passport:client --client

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

use Laravel\Passport\Http\Middleware\CheckClientCredentials;

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

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

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

要使用具体作用域限制访问的路由,可以在添加 client 中间件到路由时提供一个逗号分隔的所需作用域列表:

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

获取令牌

使用该授权类型获取令牌,可以对 oauth/token 端口发起请求:

$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'];

个人访问令牌

有时,用户可能想在不经历通常的验证码重定向流程的情况下发放访问令牌给他们自己。允许用户通过应用界面对自己发放令牌,在让用户测试 API 或将其作为一种更简单的发放访问令牌时会很有用。

个人访问令牌是永久有效的。就算使用了 tokensExpireInrefreshTokensExpireIn 方法它们的有效期也不改变。

创建个人访问令牌

在应用发放个人访问令牌之前,需要创建一个个人访问客户端。可以使用 passport:client 命令带上 --personal 选项来完成此操作。如果已经运行了 passport:install 命令,那么不需要再次运行此命令:

php artisan passport:client --personal

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

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

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

管理个人访问令牌

创建个人访问客户端后,可以在 User 模型实例上使用 createToken 方法为给定用户发放令牌。createToken 方法接收令牌名称作为其第一个参数,以及一个可选的 作用域 数组作为其第二个参数:

$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 中间件作为看守器;因此,只能在您自己的应用中调用。不能从外部调用。

如果您不想自己实现整个客户端管理的前端界面,可以使用 前端快速上手 在几分钟内创建功能齐全的前端界面。

GET /oauth/scopes

此路由会返回应用中定义的所有 作用域。可以使用此路由列出用户指定给个人访问令牌的作用域:

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

GET /oauth/personal-access-tokens

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

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

POST /oauth/personal-access-tokens

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

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}

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

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

保护路由

通过中间件

Passport 包含的 认证看守器 会验证传入请求的访问令牌。配置 api 看守器使用 passport 驱动后,只需要在需要有效访问令牌的任何路由上指定 auth:api 中间件:

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

传递访问令牌

当调用 Passport 保护的路由时,接入的 API 应用应该将访问令牌作为 Bearer 令牌放在请求头 Authorization 中。例如,使用 Guzzle HTTP 库时:

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

令牌作用域

作用域可以让 API 客户端请求访问用户授权时请求指定的一组权限。例如,如果您正在构建电子商务应用,并不是所有接入的 API 应用都需要创建订单的功能。您可以让接入的 API 应用只请求授权访问订单发货状态。换句话说,作用域允许应用的用户限制第三方应用执行的操作。

定义作用域

可以在 AuthServiceProviderboot 方法中使用 Passport::tokensCan 方法定义 API 的作用域。tokensCan 接收一个作用域名称和描述的数组。作用域描述可以是授权批准页面希望展示给用户的任何内容:

use Laravel\Passport\Passport;

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

为令牌指定作用域

当请求授权码时

当使用授权码请求访问令牌时,接入的应用应该将想要的作用域作为 scope 的字符串参数。scope 参数应该是一个空格分隔的作用域列表:

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 方法发放个人访问令牌时,应该将想要的作用域数组作为第二个参数传递给该方法:

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

检查作用域

Passport 包含两个中间件,用于验证传入的请求是否使用指定了给定作用域的令牌授权了。要实现此功能,将如下中间件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 属性:

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

检查所有作用域

scopes 中间件可以指定给路由来验证传入的请求的访问令牌有 所有 列出的作用域:

Route::get('/orders', function () {
    // 访问令牌有「check-status」和「place-orders」两个作用域
})->middleware('scopes:check-status,place-orders');

检查任意作用域

scope 中间件可以指定给路由来验证传入的请求的访问令牌有至少一个列出的作用域:

Route::get('/orders', function () {
    // 访问令牌有「check-status」或「place-orders」其中一个作用域
})->middleware('scope:check-status,place-orders');

检查令牌实例的作用域

当一个有访问令牌授权的请求进入应用后,仍然可以在认证的 User 实例的上使用 tokenCan 方法来检查该令牌是否有给定的作用域:

use Illuminate\Http\Request;

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

其它作用域方法

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

Laravel\Passport\Passport::scopeIds();

scopes 方法会返回一个所有定义的作用域的 Laravel\Passport\Scope 实例的数组

Laravel\Passport\Passport::scopes();

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

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

可以使用 hasScope 方法判断给定的作用域是否被定义:

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 中间键组即可:

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

应该确保 EncryptCookies 中间件在中间件栈中列在 CreateFreshApiToken 中间件的前面。

Passport 的此中间件会将 laravel_token Cookie 添加到传出的请求中。该 Cookie 包含了一个加密的 JWT,Passport 会用它来认证来自 JavaScript 应用的 API 请求。现在,可以向应用的 API 发起请求而不用显示传递访问令牌了:

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

自定义 Cookie 名

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

/**
 * 注册任何应用认证/授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    Passport::cookie('custom_name');
}

CSRF 保护

当使用认证的此方法时,默认的 Laravel JavaScript 脚手架指示 Axios 始终发送 X-CSRF-TOKENX-Requested-With 头信息。但是,应该确保在 HTML meta 标签 中包括了 CSRF 令牌:

window.axios.defaults.headers.common = {
    'X-Requested-With': 'XMLHttpRequest',
};

事件

Passport 在发放访问令牌和刷新令牌时触发了事件。可能使用这些事件来修改或撤销数据库中的其它访问令牌。可以在 EventServiceProvider 中为这些事件添加监听器:

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

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

测试

Passport 的 actingAs 方法可用于指定当前认证用户和其作用域。传递给 actingAs 方法的第一个参数是用户实例,第二个参数是一个指定给用户令牌的作用域数组:

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);
}