Passport OAuth 認證
Passport OAuth 認證
Laravel Passport
介紹
在 Laravel 中,實現(xiàn)基于傳統(tǒng)表單的登陸和授權已經非常簡單,但是如何滿足 API 場景下的授權需求呢?在 API 場景里通常通過令牌來實現(xiàn)用戶授權,而非維護請求之間的 Session 狀態(tài)。在 Laravel 項目中使用 Passport 可以輕而易舉地實現(xiàn) API 授權認證,Passport 可以在幾分鐘之內為你的應用程序提供完整的 OAuth2 服務端實現(xiàn)。Passport 是基于由 Andy Millington 和 Simon Hamp 維護的 League OAuth2 server 建立的。
{note} 本文檔假定你已熟悉 OAuth2 。如果你并不了解 OAuth2 ,閱讀之前請先熟悉下 OAuth2 的 常用術語 和特性。
安裝
在開始之前,請通過 Composer 包管理器安裝 Passport:
composer require laravel/passport
Passport 服務提供器使用框架注冊自己的數(shù)據(jù)庫遷移目錄,因此在注冊提供器后,就應該運行 Passport 的遷移命令來自動創(chuàng)建存儲客戶端和令牌的數(shù)據(jù)表:
php artisan migrate
接下來,運行 passport:install
命令來創(chuàng)建生成安全訪問令牌時所需的加密密鑰,同時,這條命令也會創(chuàng)建用于生成訪問令牌的「個人訪問」客戶端和「密碼授權」客戶端:
php artisan passport:install
上面命令執(zhí)行后,請將 Laravel\Passport\HasApiTokens
Trait 添加到 App\User
模型中,這個 Trait 會給你的模型提供一些輔助函數(shù),用于檢查已認證用戶的令牌和使用范圍:
<?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
函數(shù)。這個函數(shù)會注冊發(fā)出訪問令牌并撤銷訪問令牌、客戶端和個人訪問令牌所必需的路由:
<?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
來處理:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],
自定義遷移
如果你不打算使用 Passport 的默認遷移,你應該在 AppServiceProvider
的 register
方法中調用 Passport::ignoreMigrations
方法。 你可以用這個命令 php artisan vendor:publish --tag=passport-migrations
導出默認遷移。
默認情況下,Passport 使用字段 「user_id」標識用戶。如果想使用不同的字段標識用戶 (例如:uuid),可以修改默認的 Passport 遷移文件。
前端快速上手
{note} 為了使用 Passport 的 Vue 組件,你必須使用 Vue JavaScript 框架。這些組件也使用了 Bootstrap CSS 框架。然而,如果你不打算使用這些工具,這些組件對于你自己的前端組件編寫也十分有價值。
Passport 提供了一系列 JSON API ,你可以用它們來允許你的用戶創(chuàng)建客戶端和個人訪問令牌。然而,編寫與這些 API 交互的前端代碼可能是很占用時間的。因此,Passport 也包括了預編譯的 Vue 組件,你可以直接使用或將其作為你自己的前端參考。
要使用 Passport 的 Vue 組件,使用 vendor:publish
Artisan 命令:
php artisan vendor:publish --tag=passport-components
被發(fā)布的組件將會被放到 resources/js/components
目錄下。當組件被發(fā)布后,你應該在你的 resources/js/app.js
文件中注冊它們:
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 發(fā)布說明.
在注冊了組件后,請確保運行 npm run dev
來重新編譯你的資源。 當你重編譯你的資源后,你可以將組件放到你應用的模板中以開始創(chuàng)建客戶端和個人訪問令牌:
<passport-clients></passport-clients> <passport-authorized-clients></passport-authorized-clients>< passport-personal-access-tokens></passport-personal-access-tokens>
部署 Passport
第一次在你的生產環(huán)境部署 Passport 時,你大概需要運行 passport:keys
命令。這個命令生成 Passport 生成訪問令牌所需的密鑰。生成的密鑰一般情況下不應放在版本控制中:
php artisan passport:keys
可以使用 Passport::loadKeysFrom
方法來自定義 Passport 密鑰的加載路徑:
/** * 注冊認證 / 授權服務 * * @return void */ public function boot(){ $this->registerPolicies(); Passport::routes(); Passport::loadKeysFrom('/secret-keys/oauth'); }
配置
令牌的有效期
默認情況下,Passport 發(fā)放的訪問令牌是有一年有效期的。但是如果你想自定義訪問令牌的有效期,可以使用 tokensExpireIn
和 refreshTokensExpireIn
方法。上述兩個方法同樣需要在 AuthServiceProvider
的 boot
方法中調用:
/** * 注冊認證 / 授權服務 * * @return void */ public function boot(){ $this->registerPolicies(); Passport::routes(); Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); }
覆蓋默認模型
可以自由擴展 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); }
發(fā)放訪問令牌
熟悉 OAuth2 的開發(fā)者一定知道, OAuth2 中必不可少的部分就是授權碼。當使用授權碼時,客戶端應用程序會將用戶重定向到你的服務器,他們將批準或拒絕向客戶端發(fā)出訪問令牌的請求。
管理客戶端
首先,構建需要與應用程序 API 交互的應用程序,開發(fā)人員將需要通過創(chuàng)建一個「客戶端」來注冊自己的應用程序。一般來說,這包括在用戶批準其授權請求后,提供其應用程序的名稱和應用程序可以重定向到的 URL。
The passport:client
命令
創(chuàng)建客戶端最簡單的方式是使用 Artisan 命令 passport:client
,你可以使用此命令創(chuàng)建自己的客戶端,用于測試你的 OAuth2 的功能。在你執(zhí)行 client
命令時,Passport 會提示你輸入有關客戶端的信息,最終會給你提供客戶端的 ID 和 密鑰:
php artisan passport:client
Redirect URLs
當有多個重定向 URL 白名單時,可以在 passport:client
命令提示輸入 URL 時,使用逗號分隔來指定:
http://example.com/callback,http://examplefoo.com/callback
{note} 任何包含逗號的 URL 都必須進行編碼。
JSON API
考慮到你的用戶無法使用 client 命令,Passport 為此提供了可用于創(chuàng)建「客戶端」的 JSON API。這樣你就不用再花時間編寫控制器來創(chuàng)建、更新和刪除客戶端。
然而,你仍舊需要基于 Passport 的 JSON API 開發(fā)一套前端界面,為你的用戶提供管理客戶端的面板。下面我們會列出所有用于管理客戶端的 API,為了方便起見,我們使用 Axios 來演示對端口發(fā)出 HTTP 請求。
這個 JSON API 由 web
和 auth
兩個中間件保護,所以只能從應用程序中調用,不能從外部調用。
{tip} 如果你不想自己實現(xiàn)整個客戶端管理的前端界面,可以使用 前端快速上手 在幾分鐘內組建一套功能齊全的前端界面。
GET /oauth/clients
此路由會返回認證用戶的所有客戶端。主要用途是列出所有用戶的客戶端,以便他們可以編輯或刪除它們:
axios.get('/oauth/clients') .then(response => { console.log(response.data); });
POST /oauth/clients
此路由用于創(chuàng)建新客戶端。它需要兩個參數(shù):客戶端的名稱 name
和授權后回調的 URL redirect
。在批準或拒絕授權請求后,用戶會被重定向到 redirect
參數(shù)提供的鏈接。
當客戶端創(chuàng)建后,會返回客戶端的 ID 和密鑰。客戶端可以使用這兩個值從你的授權服務請求訪問令牌 (Access token) 。該路由會返回新的客戶端實例:
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}
此路由用于更新客戶端信息。它需要兩個參數(shù):客戶端的名稱 name
和授權后回調的 URL redirect
。在批準或拒絕授權請求后,用戶會被重定向 redirect
到這個鏈接。此路由會返回更新后的客戶端實例:
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):
axios.delete('/oauth/clients/' + clientId) .then(response => { // });
請求令牌
授權時的重定向
客戶端創(chuàng)建之后,開發(fā)者會使用此客戶端的 ID 和密鑰來請求授權代碼,并從應用程序訪問令牌。首先,接入應用的用戶向你應用程序的 /oauth/authorize
路由發(fā)出重定向請求,示例如下:
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
必須和客戶端創(chuàng)建時指定的 redirect
鏈接完全一致。
如果你想自定義授權確認頁面,可以使用 Artisan 命令 vendor:publish
發(fā)布 Passport 的視圖。發(fā)布后的視圖文件存放在 resources/views/vendor/passport
:
php artisan vendor:publish --tag=passport-views
將授權碼轉換為訪問令牌
用戶批準授權請求后,會被重定向回接入的應用程序。然后接入應用應該將通過 POST
請求向你的應用程序申請訪問令牌。請求應該包括當用戶批準授權請求時由應用程序發(fā)出的授權碼。在下面的例子中,我們使用 Guzzle HTTP 庫來實現(xiàn)這次 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
返回的 JSON 響應中會包含 access_token
、refresh_token
和 expires_in
屬性。expires_in
屬性包含訪問令牌的有效期(單位:秒)。
{tip} 像
/oauth/authorize
路由一樣,/oauth/token
路由在Passport::routes
方法中定義了,你沒必要手動去定義它。默認情況下,此路由使用 “ThrottleRequests” 中間件的設置進行限流。
刷新令牌
如果你的應用程序發(fā)放了短期的訪問令牌,用戶將需要通過在發(fā)出訪問令牌時提供給他們的刷新令牌來刷新其訪問令牌。在下面的例子中,我們使用 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
會返回一個 JSON 響應,其中包含 access_token
、refresh_token
和 expires_in
屬性。expires_in
屬性包含訪問令牌的有效時間(單位:秒)。
密碼授權令牌
OAuth2 密碼授權機制可以讓你自己的客戶端(如移動應用程序)使用郵箱地址或者用戶名和密碼獲取訪問令牌。如此一來你就可以安全地向自己的客戶端發(fā)出訪問令牌,而不需要遍歷整個 OAuth2 授權代碼重定向流程
創(chuàng)建密碼授權客戶端
在應用程序通過密碼授權機制來發(fā)布令牌之前,在 passport:client
命令后加上 --password
參數(shù)來創(chuàng)建密碼授權的客戶端。如果你已經運行了 passport:install
命令,則不需要再運行此命令:
php artisan passport:client --password
請求令牌
創(chuàng)建密碼授權的客戶端后,就可以使用用戶的電子郵件地址和密碼向 /oauth/token
路由發(fā)出 POST
請求來獲取訪問令牌。而該路由已經由 Passport::routes
方法注冊,因此不需要手動定義它。如果請求成功,會在服務端返回的 JSON 響應中收到一個 access_token
和 refresh_token
:
$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} 默認情況下,訪問令牌是長期有效的。你可以根據(jù)需要 配置訪問令牌的有效時間 。
請求所有作用域
使用密碼授權機制時,可以通過請求 scope 參數(shù) *
來授權應用程序支持的所有范圍的令牌。如果你的請求中包含 scope 為 *
的參數(shù),令牌實例上的 can
方法會始終返回 true
。這種作用域的授權只能分配給使用 password
授權時發(fā)出的令牌:
$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 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(); } }
隱式授權令牌
隱式授權類似于授權碼授權,但是它只將令牌返回給客戶端而不交換授權碼。這種授權最常用于無法安全存儲客戶端憑據(jù)的 JavaScript 或移動應用程序。通過調用 AuthServiceProvider
中的 enableImplicitGrant
方法來啟用這種授權:
/** * 注冊認證 / 授權服務 * * @return void */ public function boot(){ $this->registerPolicies(); Passport::routes(); Passport::enableImplicitGrant(); }
調用上面方法開啟授權后,開發(fā)者可以使用他們的客戶端 ID 從應用程序請求訪問令牌。接入的應用程序應該向你的應用程序的 /oauth/authorize
路由發(fā)出重定向請求,如下所示:
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
方法中定義好,所以無需再次手動定義此路由。
客戶端憑據(jù)授權令牌
客戶端憑據(jù)授權適用于機器到機器的認證。例如,你可以在通過 API 執(zhí)行維護任務中使用此授權。
在客戶端憑據(jù)授權之前,需要先創(chuàng)建一個客戶端憑據(jù)授權的客戶端,你可以使用 passport:client
命令的 --client
參數(shù)來創(chuàng)建:
php artisan passport:client --client
接下來,要使用這種授權,你首先需要在 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
接口發(fā)出請求來獲取令牌:
$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'];
個人訪問令牌
有時候,用戶要在不經過傳統(tǒng)的授權碼重定向流程的情況下向自己發(fā)出訪問令牌。允許用戶通過應用程序用戶界面對自己發(fā)出令牌,有助于用戶體驗你的 API,或者也可以將其作為一種更簡單的發(fā)布訪問令牌的方式。
{note} 個人訪問令牌是永久有效的,就算使用了
tokensExpireIn
和refreshTokensExpireIn
方法也不會修改它的生命周期。
創(chuàng)建個人訪問客戶端
在你的應用程序發(fā)布個人訪問令牌之前,你需要在 passport:client
命令后帶上 --personal
參數(shù)來創(chuàng)建對應的客戶端。如果你已經運行了 passport:install
命令,則無需再運行此命令:
php artisan passport:client --personal
如果你已經創(chuàng)建了個人訪問客戶端,你可以通過調用 AuthServiceProvider
中的 personalAccessClientId
方法來啟用:
/** * 注冊認證 / 授權服務 * * @return void */ public function boot(){ $this->registerPolicies(); Passport::routes(); Passport::personalAccessClientId('client-id'); }
管理個人訪問令牌
創(chuàng)建個人訪問客戶端后,你可以使用 User
模型實例上的 createToken
方法來為給定用戶發(fā)布令牌。createToken
方法接受令牌的名稱作為其第一個參數(shù)和可選的 作用域 數(shù)組作為其第二個參數(shù):
$user = App\User::find(1); // 創(chuàng)建沒有作用域的訪問令牌... $token = $user->createToken('Token Name')->accessToken; // 創(chuàng)建有作用域的訪問令牌... $token = $user->createToken('My Token', ['place-orders'])->accessToken;
JSON API
Passport 中也有用來管理個人訪問令牌的 JSON API,你可以將其與自己的前端配對,為用戶提供管理個人訪問令牌的儀表板。下面我們會介紹用于管理個人訪問令牌的所有 API 接口。方便起見,我們使用 Axios 來演示對 API 的接口發(fā)出 HTTP 請求。
JSON API 由 web
和 auth
中間件保護;因此,只能從您自己的應用程序中調用它。無法從外部源調用它。
{tip} 如果你不想實現(xiàn)自己的個人訪問令牌管理的前端界面,可以根據(jù) 前端快速上手 在幾分鐘內組建功能齊全的前端界面。
GET /oauth/scopes
此路由會返回應用程序中定義的所有 作用域。你可以使用此路由列出用戶可能分配給個人訪問令牌的范圍:
axios.get('/oauth/scopes') .then(response => { console.log(response.data); });
GET /oauth/personal-access-tokens
此路由返回認證用戶創(chuàng)建的所有個人訪問令牌。這主要用于列出所有用戶的令牌,以便他們可以編輯或刪除它們:
axios.get('/oauth/personal-access-tokens') .then(response => { console.log(response.data); });
POST /oauth/personal-access-tokens
此路由用于創(chuàng)建新的個人訪問令牌。它需要兩個數(shù)據(jù):令牌的 name
和 scpoe
:
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 應用只被允許授權訪問訂單發(fā)貨狀態(tài)。換句話說,作用域允許應用程序的用戶限制第三方應用程序執(zhí)行的操作。
定義作用域
你可以在 AuthServiceProvider
的 boot
方法中使用 Passport::tokensCan
方法來定義 API 的作用域。tokensCan
方法接受一個包含作用域名稱和描述的數(shù)組作為參數(shù)。作用域描述將會在授權確認頁中直接展示給用戶,你可以將其定義為任何你需要的內容:
use Laravel\Passport\Passport; Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', ]);
默認作用域
如果客戶端沒有請求任何特定的范圍,你可以在 AuthServiceProvider
的 boot
方法中使用 Passport::setDefaultScope
方法來定義默認的作用域。
use Laravel\Passport\Passport;Passport::setDefaultScope([ 'check-status', 'place-orders', ]);
給令牌分配作用域
請求授權碼
使用授權碼請求訪問令牌時,接入的應用需為 scope
參數(shù)指定所需作用域。 scope
參數(shù)包含多個作用域時,名稱之間使用空格分割:
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);});
分發(fā)個人訪問令牌
使用 User
模型的 createToken
方法發(fā)放個人訪問令牌時,可以將所需作用域的數(shù)組作為第二個參數(shù)傳給此方法:
$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 () { // Access token has both "check-status" and "place-orders" scopes... })->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 / 名稱的數(shù)組:
Laravel\Passport\Passport::scopeIds();
scopes
方法將返回一個 包含所有已定義作用域數(shù)組的 Laravel\Passport\Scope
實例:
Laravel\Passport\Passport::scopes();
scopesFor
方法將返回與給定 ID / 名稱匹配的 Laravel\Passport\Scope
實例數(shù)組:
Laravel\Passport\Passport::scopesFor(['place-orders', 'check-status']);
你可以使用 hasScope
方法確定是否已定義給定作用域:
Laravel\Passport\Passport::hasScope('place-orders');
使用 JavaScript 接入 API
在構建 API 時, 如果能通過 JavaScript 應用接入自己的 API 將會給開發(fā)過程帶來極大的便利。這種 API 開發(fā)方法允許你使用自己的應用程序的 API 和別人共享的 API 。你的 Web 應用程序、移動應用程序、第三方應用程序以及可能在各種軟件包管理器上發(fā)布的任何 SDK 都可能會使用相同的 API 。
通常,如果要在 JavaScript 應用程序中使用 API ,需要手動向應用程序發(fā)送訪問令牌,并將其傳遞給應用程序。但是, Passport 有一個可以處理這個問題的中間件。將 CreateFreshApiToken
中間件添加到 app/Http/Kernel.php
文件中的 web
中間件組就可以了:
'web' => [ // 其他中間件... \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ],
{note} 你應該確保在您的中間件堆棧中
CreateFreshApiToken
中間件之前列出了EncryptCookies
中間件。
這個 Passport 中間件將在你所有的對外請求中添加一個 laravel_token
cookie 。該 cookie 將包含一個加密后的 JWT , Passport 將用來驗證來自 JavaScript 應用程序的 API 請求。至此,你可以在不明確傳遞訪問令牌的情況下向應用程序的 API 發(fā)出請求:
axios.get('/api/user') .then(response => { console.log(response.data); });
自定義 Cookie 名稱
如果需要,你可以在 AuthServiceProvider
的 boot
方法中使用 Passport::cookie
方法來自定義 laravel_token
cookie 的名稱。
/** * 注冊認證 / 授權服務 * * @return void */ public function boot(){ $this->registerPolicies(); Passport::routes(); Passport::cookie('custom_name'); }
CSRF 保護
當使用這種授權方法時,默認的 Laravel JavaScript 腳手架會讓 Axios 發(fā)送 X-CSRF-TOKEN
和 X-Requested-With
請求頭。另外,你必須確保 HTML meta 標簽 標簽 中包含了 CSRF 令牌:
// In your application layout... <meta name="csrf-token" content="{{ csrf_token() }}"> // Laravel's JavaScript scaffolding... window.axios.defaults.headers.common = { 'X-Requested-With': 'XMLHttpRequest', };
事件
Passport 在發(fā)出訪問令牌和刷新令牌時觸發(fā)事件。你可以在應用程序 的 EventServiceProvider
中為這些事件追加監(jiān)聽器,并在監(jiān)聽器中撤銷或修改其他令牌:
/** * 應用程序事件監(jiān)聽映射 * * @var array */ protected $listen = [ 'Laravel\Passport\Events\AccessTokenCreated' => [ 'App\Listeners\RevokeOldTokens', ], 'Laravel\Passport\Events\RefreshTokenCreated' => [ 'App\Listeners\PruneOldTokens', ], ];
測試
Passport 的 actingAs
方法可以指定當前已認證用戶及其作用域。actingAs
方法的第一個參數(shù)是用戶實例,第二個參數(shù)是用戶令牌作用域數(shù)組:
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); }