# Passport OAuth 認證
在 Laravel 中,實現基于傳統表單的登陸和授權已經非常簡單,但是如何滿足 API 場景下的授權需求呢?在 API 場景里通常通過令牌來實現用戶授權,而非維護請求之間的 Session 狀態。在 Laravel 項目中使用 Passport 可以輕而易舉地實現 API 授權認證,Passport 可以在幾分鐘之內為你的應用程序提供完整的 OAuth2 服務端實現。Passport 是基于由 Alex Bilbie 維護的[League OAuth2 server](https://github.com/thephpleague/oauth2-server)建立的。
> {note} 本文檔假定你已熟悉 OAuth2 。如果你并不了解 OAuth2 ,閱讀之前請先熟悉下 OAuth2 的常用術語和功能。
## 安裝
在開始之前, 請通過 Composer 包管理器安裝 Passport:
~~~php
composer require laravel/passport
~~~
Passport 服務提供器使用框架注冊自己的數據庫遷移目錄,因此在注冊提供器后,就應該運行 Passport 的遷移命令來自動創建存儲客戶端和令牌的數據表:
~~~php
php artisan migrate
~~~
> {note} 如果你不打算使用 Passport 的默認遷移,你應該在`AppServiceProvider`的`register`方法中調用`Passport::ignoreMigrations`方法。 你可以用這個命令`php artisan vendor:publish --tag=passport-migrations`導出默認遷移。
接下來,運行`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;
}
~~~
接下來,在`AuthServiceProvider`的`boot`方法中調用`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`中授權看守器`guards`的`api`的`driver`選項改為`passport`。此調整會讓你的應用程序在在驗證傳入的 API 的請求時使用 Passport 的`TokenGuard`來處理:
~~~php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
~~~
### 前端快速上手
> {note} 為了使用 Passport 的 Vue 組件,你必須使用[Vue](https://vuejs.org/)JavaScript 框架。這些組件也使用了 Bootstrap CSS 框架。然而,如果你不打算使用這些工具,這些組件對于你自己的前端組件編寫也十分有價值。
Passport 提供了一系列 JSON API ,你可以用它們來允許你的用戶創建客戶端和個人訪問令牌。然而,編寫與這些 API 交互的前端代碼可能是很占用時間的。因此,Passport 也包括了預編譯的[Vue](https://vuejs.org/)組件,你可以直接使用或將其作為你自己的前端參考。
要使用 Passport 的 Vue 組件,使用`vendor:publish`Artisan 命令:
~~~php
php artisan vendor:publish --tag=passport-components
~~~
被發布的組件將會被放到`resources/assets/js/components`目錄下。當組件被發布后,你應該在你的`resources/assets/js/app.js`文件中注冊它們:
~~~php
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`來重新編譯你的資源。 當你重編譯你的資源后,你可以將組件放到你應用的模板中以開始創建客戶端和個人訪問令牌:
~~~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 發放的訪問令牌是有一年有效期的。但是如果你想自定義訪問令牌的有效期,可以使用`tokensExpireIn`和`refreshTokensExpireIn`方法。上述兩個方法同樣需要在`AuthServiceProvider`的`boot`方法中調用:
~~~php
/**
* 注冊認證 / 授權服務
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
}
~~~
## 發放訪問令牌
熟悉 OAuth2 的開發者一定知道, OAuth2 中必不可少的部分就是授權碼。當使用授權碼時,客戶端應用程序會將用戶重定向到你的服務器,他們將批準或拒絕向客戶端發出訪問令牌的請求。
### 管理客戶端
首先,構建需要與應用程序 API 交互的應用程序,開發人員將需要通過創建一個「客戶端」來注冊自己的應用程序。一般來說,這包括在用戶批準其授權請求后,提供其應用程序的名稱和應用程序可以重定向到的 URL。
#### `passport:client`命令
創建客戶端最簡單的方式是使用 Artisan 命令`passport:client`,你可以使用此命令創建自己的客戶端,用于測試你的 OAuth2 的功能。在你執行`client`命令時,Passport 會提示你輸入有關客戶端的信息,最終會給你提供客戶端的 ID 和 密鑰:
~~~php
php artisan passport:client
~~~
#### JSON API
考慮到你的用戶無法使用 client 命令,Passport 為此提供了可用于創建「客戶端」的 JSON API。這樣你就不用再花時間編寫控制器來創建、更新和刪除客戶端。
然而,你仍舊需要基于 Passport 的 JSON API 開發一套前端界面,為你的用戶提供管理客戶端的面板。下面我們會列出所有用于管理客戶端的 API,為了方便起見,我們使用[Axios](https://github.com/mzabriskie/axios)來演示對端口發出 HTTP 請求。
> {tip} 如果你不想自己實現整個客戶端管理的前端界面,可以使用[前端快速上手](https://laravel-china.org/docs/laravel/5.7/passport/2270#frontend-quickstart)在幾分鐘內組建一套功能齊全的前端界面。
#### `GET /oauth/clients`
此路由會返回認證用戶的所有客戶端。主要用途是列出所有用戶的客戶端,以便他們可以編輯或刪除它們:
~~~php
axios.get('/oauth/clients')
.then(response => {
console.log(response.data);
});
~~~
#### `POST /oauth/clients`
此路由用于創建新客戶端。它需要兩個參數:客戶端的名稱`name`和授權后回調的URL`redirect`。在批準或拒絕授權請求后,用戶會被重定向到`redirect`參數提供的鏈接。
當客戶端創建后,會返回客戶端的 ID 和密鑰。客戶端可以使用這兩個值從你的授權服務請求訪問令牌 (Access token) 。該路由會返回新的客戶端實例:
~~~php
const data = {
name: 'Client Name',
redirect: 'http://example.com/callback'
};
axios.post('/oauth/clients', data)
.then(response => {
console.log(response.data);
})
.catch (response => {
//在response里列出錯誤詳情...
});
~~~
#### `PUT /oauth/clients/{client-id}`
此路由用于更新客戶端信息。它需要兩個參數:客戶端的名稱`name`和授權后回調的URL`redirect`。在批準或拒絕授權請求后,用戶會被重定向`redirect`到這個鏈接。此路由會返回更新后的客戶端實例:
~~~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 => {
//在response里列出錯誤詳情...
});
~~~
#### `DELETE /oauth/clients/{client-id}`
此路由用于刪除客戶端(client):
~~~php
axios.delete('/oauth/clients/' + clientId)
.then(response => {
//
});
~~~
### 請求令牌
#### 授權時的重定向
客戶端創建之后,開發者會使用此客戶端的 ID 和密鑰來請求授權代碼,并從應用程序訪問令牌。首先,接入應用的用戶向你應用程序的`/oauth/authorize`路由發出重定向請求,示例如下:
~~~php
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);
});
~~~
> {tip} 注意,路由`/oauth/authorize`已經在`Passport::routes`方法中定義。你不需要手動定義此路由。
#### 批準請求
接收到授權請求時,Passport 會自動向用戶顯示一個模版頁面,允許用戶批準或拒絕授權請求。如果用戶批準請求,他們會被重定向回接入的應用程序指定的`redirect_uri`。`redirect_uri`必須和客戶端創建時指定的`redirect`鏈接完全一致。
如果你想自定義授權確認頁面,可以使用 Artisan 命令`vendor:publish`發布 Passport 的視圖。發布后的視圖文件存放在`resources/views/vendor/passport`:
~~~php
php artisan vendor:publish --tag=passport-views
~~~
#### 將授權碼轉換為訪問令牌
用戶批準授權請求后,會被重定向回接入的應用程序。然后接入應用應該將通過`POST`請求向你的應用程序申請訪問令牌。請求應該包括當用戶批準授權請求時由應用程序發出的授權碼。在下面的例子中,我們使用 Guzzle HTTP 庫來實現這次`POST`請求:
~~~php
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`返回的 JSON 響應中會包含`access_token`、`refresh_token`和`expires_in`屬性。`expires_in`屬性包含訪問令牌的有效期(單位:秒)。
> {tip}`/oauth/authorize`路由一樣,`/oauth/token`路由在`Passport::routes`方法中定義了,你沒必要手動去定義它。
### 刷新令牌
如果你的應用程序發放了短期的訪問令牌,用戶將需要通過在發出訪問令牌時提供給他們的刷新令牌來刷新其訪問令牌。在下面的例子中,我們使用 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`會返回一個 JSON 響應,其中包含`access_token`、`refresh_token`和`expires_in`屬性。`expires_in`屬性包含訪問令牌的有效時間(單位:秒)。
## 密碼授權令牌
OAuth2 密碼授權機制可以讓你自己的客戶端(如移動應用程序)使用郵箱地址或者用戶名和密碼獲取訪問令牌。如此一來你就可以安全地向自己的客戶端發出訪問令牌,而不需要遍歷整個 OAuth2 授權代碼重定向流程
### 創建密碼授權客戶端
在應用程序通過密碼授權機制來發布令牌之前,在`passport:client`命令后加上`--password`參數來創建密碼授權的客戶端。如果你已經運行了`passport:install`命令,則不需要再運行此命令:
~~~php
php artisan passport:client --password
~~~
### 請求令牌
創建密碼授權的客戶端后,就可以使用用戶的電子郵件地址和密碼向`/oauth/token`路由發出`POST`請求來獲取訪問令牌。而該路由已經由`Passport::routes`方法注冊,因此不需要手動定義它。如果請求成功,會在服務端返回的 JSON 響應中收到一個`access_token`和`refresh_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);
~~~
> {tip} 默認情況下,訪問令牌是長期有效的。你可以根據需要[配置訪問令牌的有效時間](https://laravel-china.org/docs/laravel/5.7/passport/2270#configuration)。
### 請求所有作用域
使用密碼授權機制時,可以通過請求 scope 參數`*`來授權應用程序支持的所有范圍的令牌。如果你的請求中包含 scope 為`*`的參數,令牌實例上的`can`方法會始終返回`true`。這種作用域的授權只能分配給使用`password`授權時發出的令牌:
~~~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' => '*',
],
]);
~~~
## 隱式授權令牌
隱式授權類似于授權碼授權,但是它只將令牌返回給客戶端而不交換授權碼。這種授權最常用于無法安全存儲客戶端憑據的 JavaScript 或移動應用程序。通過調用`AuthServiceProvider`中的`enableImplicitGrant`方法來啟用這種授權:
~~~php
/**
* 注冊任何身份驗證/授權服務。
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::enableImplicitGrant();
}
~~~
調用上面方法開啟授權后,開發者可以使用他們的客戶端 ID 從應用程序請求訪問令牌。接入的應用程序應該向你的應用程序的`/oauth/authorize`路由發出重定向請求,如下所示:
~~~php
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);
});
~~~
> {tip} 注意,`/oauth/authorize`路由已經在`Passport::routes`方法中定義好,所以無需再次手動定義此路由。
## 客戶端憑據授權令牌
客戶端憑據授權適用于機器到機器的認證。例如,你可以在通過 API 執行維護任務中使用此授權。要使用這種授權,你首先需要在`app/Http/Kernel.php`的`$routeMiddleware`變量中添加新的中間件:
~~~php
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
protected $routeMiddleware = [
'client' => CheckClientCredentials::class,
];
~~~
然后在路由上追加這個中間件:
~~~php
Route::get('/user', function(Request $request) {
...
})->middleware('client');
~~~
接下來通過向`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'];
~~~
## 個人訪問令牌
有時候,用戶要在不經過傳統的授權碼重定向流程的情況下向自己發出訪問令牌。允許用戶通過應用程序用戶界面對自己發出令牌,有助于用戶體驗你的 API,或者也可以將其作為一種更簡單的發布訪問令牌的方式。
> {note} 個人訪問令牌是永久有效的,就算使用了`tokensExpireIn`和`refreshTokensExpireIn`方法也不會修改它的生命周期。
### 創建個人訪問客戶端
在你的應用程序發布個人訪問令牌之前,你需要在`passport:client`命令后帶上`--personal`參數來創建對應的客戶端。如果你已經運行了`passport:install`命令,則無需再運行此命令:
~~~php
php artisan passport:client --personal
~~~
### 管理個人訪問令牌
創建個人訪問客戶端后,你可以使用`User`模型實例上的`createToken`方法來為給定用戶發布令牌。`createToken`方法接受令牌的名稱作為其第一個參數和可選的[作用域](https://laravel-china.org/docs/laravel/5.7/passport/2270#token-scopes)數組作為其第二個參數:
~~~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](https://github.com/mzabriskie/axios)來演示對 API 的接口發出 HTTP 請求。
> {tip} 如果你不想實現自己的個人訪問令牌管理的前端界面,可以根據[前端快速上手](https://laravel-china.org/docs/laravel/5.7/passport/2270#frontend-quickstart)在幾分鐘內組建功能齊全的前端界面。
#### `GET /oauth/scopes`
此路由會返回應用程序中定義的所有[作用域](https://laravel-china.org/docs/laravel/5.7/passport/2270#token-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`和`scpoe`:
~~~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 包含一個[驗證保護機制](https://laravel-china.org/docs/laravel/5.7/authentication#adding-custom-guards)可以驗證請求中傳入的訪問令牌。配置`api`的看守器使用`passport`驅動程序后,只需要在需要有效訪問令牌的任何路由上指定`auth:api`中間件:
~~~php
Route::get('/user', function () {
//
})->middleware('auth:api');
~~~
### 傳遞訪問令牌
當調用 Passport 保護下的路由時,接入的 API 應用需要將訪問令牌作為`Bearer`令牌放在請求頭`Authorization`中。例如,使用 Guzzle HTTP 庫時:
~~~php
$response = $client->request('GET', '/api/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
~~~
## 令牌作用域Token Scopes
### 定義作用域
作用域可以讓 API 客戶端在請求賬戶授權時請求特定的權限。例如,如果你正在構建電子商務應用程序,并不是所有接入的 API 應用都需要下訂單的功能。你可以讓接入的 API 應用只被允許授權訪問訂單發貨狀態。換句話說,作用域允許應用程序的用戶限制第三方應用程序執行的操作。
你可以在`AuthServiceProvider`的`boot`方法中使用`Passport::tokensCan`方法來定義 API 的作用域。`tokensCan`方法接受一個包含作用域名稱和描述的數組作為參數。作用域描述將會在授權確認頁中直接展示給用戶,你可以將其定義為任何你需要的內容:
~~~php
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
~~~
### 給令牌分配作用域
#### 請求授權碼
使用授權碼請求訪問令牌時,接入的應用需為`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')) {
//
}
});
~~~
## 使用 JavaScript 接入 API
在構建 API 時, 如果能通過 JavaScript 應用接入自己的 API 將會給開發過程帶來極大的便利。這種 API 開發方法允許你使用自己的應用程序的 API 和別人共享的 API 。你的 Web 應用程序、移動應用程序、第三方應用程序以及可能在各種軟件包管理器上發布的任何 SDK 都可能會使用相同的 API 。
通常,如果要在 JavaScript 應用程序中使用 API ,需要手動向應用程序發送訪問令牌,并將其傳遞給應用程序。但是, Passport 有一個可以處理這個問題的中間件。將`CreateFreshApiToken`中間件添加到`app/Http/Kernel.php`文件中的`web`中間件組就可以了:
~~~php
'web' => [
// 其他中間件...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
~~~
這個 Passport 中間件將在你所有的對外請求中添加一個`laravel_token`cookie 。該 cookie 將包含一個加密后的 JWT , Passport將用來驗證來自 JavaScript 應用程序的 API 請求。至此,你可以在不明確傳遞訪問令牌的情況下向應用程序的 API 發出請求:
~~~php
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
~~~
當使用上面的授權方法時, Axios 會自動帶上`X-CSRF-TOKEN`請求頭傳遞。另外,默認的 Laravel JavaScript 腳手架會讓 Axios 發送`X-Requested-With`請求頭。另外,你必須確保[HTML meta 標簽](https://laravel-china.org/docs/laravel/5.7/csrf#csrf-x-csrf-token)標簽 中包含了 CSRF 令牌:
~~~php
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
};
~~~
> {note} 如果你用了其他的 JavaScript 框架,應確保配置了每次對外請求都會帶有`X-CSRF-TOKEN`和`X-Requested-With`請求頭。
## 事件Events
Passport 在發出訪問令牌和刷新令牌時觸發事件。你可以在應用程序 的`EventServiceProvider`中為這些事件追加監聽器,并在監聽器中撤銷或修改其他令牌:
~~~php
/**
* 應用程序事件監聽映射The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],
'Laravel\Passport\Events\RefreshTokenCreated' => [
'App\Listeners\PruneOldTokens',
],
];
~~~
## 測試
Passport 的`actingAs`方法可以指定當前已認證用戶及其作用域。`actingAs`方法的第一個參數是用戶實例,第二個參數是用戶令牌作用域數組:
~~~php
public function testServerCreation()
{
Passport::actingAs(
factory(User::class)->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(200);
}
~~~