国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Laravel 5.8 中文文檔手冊(cè) / Passport OAuth 認(rèn)證

Passport OAuth 認(rèn)證

Passport OAuth 認(rèn)證


Laravel Passport

介紹

在 Laravel 中,實(shí)現(xiàn)基于傳統(tǒng)表單的登陸和授權(quán)已經(jīng)非常簡(jiǎn)單,但是如何滿足 API 場(chǎng)景下的授權(quán)需求呢?在 API 場(chǎng)景里通常通過令牌來實(shí)現(xiàn)用戶授權(quán),而非維護(hù)請(qǐng)求之間的 Session 狀態(tài)。在 Laravel 項(xiàng)目中使用 Passport 可以輕而易舉地實(shí)現(xiàn) API 授權(quán)認(rèn)證,Passport 可以在幾分鐘之內(nèi)為你的應(yīng)用程序提供完整的 OAuth2 服務(wù)端實(shí)現(xiàn)。Passport 是基于由 Andy Millington 和 Simon Hamp 維護(hù)的 League OAuth2 server 建立的。

{note} 本文檔假定你已熟悉 OAuth2 。如果你并不了解 OAuth2 ,閱讀之前請(qǐng)先熟悉下 OAuth2 的 常用術(shù)語 和特性。

安裝

在開始之前,請(qǐng)通過 Composer 包管理器安裝 Passport:

composer require laravel/passport

Passport 服務(wù)提供器使用框架注冊(cè)自己的數(shù)據(jù)庫遷移目錄,因此在注冊(cè)提供器后,就應(yīng)該運(yùn)行 Passport 的遷移命令來自動(dòng)創(chuàng)建存儲(chǔ)客戶端和令牌的數(shù)據(jù)表:

php artisan migrate

接下來,運(yùn)行 passport:install 命令來創(chuàng)建生成安全訪問令牌時(shí)所需的加密密鑰,同時(shí),這條命令也會(huì)創(chuàng)建用于生成訪問令牌的「?jìng)€(gè)人訪問」客戶端和「密碼授權(quán)」客戶端:

php artisan passport:install

上面命令執(zhí)行后,請(qǐng)將 Laravel\Passport\HasApiTokens Trait 添加到 App\User 模型中,這個(gè) Trait 會(huì)給你的模型提供一些輔助函數(shù),用于檢查已認(rèn)證用戶的令牌和使用范圍:

<?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 方法中調(diào)用 Passport::routes 函數(shù)。這個(gè)函數(shù)會(huì)注冊(cè)發(fā)出訪問令牌并撤銷訪問令牌、客戶端和個(gè)人訪問令牌所必需的路由:

<?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{ 
    /**
     * 應(yīng)用程序的策略映射。
     *
     * @var array
     */   
     protected $policies = [  
           'App\Model' => 'App\Policies\ModelPolicy',  
            ];   
     /**
     * 注冊(cè)任何認(rèn)證/授權(quán)服務(wù)。
     *
     * @return void
     */   
     public function boot()
         {   
              $this->registerPolicies();        
              Passport::routes();    
         }
     }

最后,將配置文件 config/auth.php 中授權(quán)看守器 guardsapidriver 選項(xiàng)改為 passport。此調(diào)整會(huì)讓你的應(yīng)用程序在在驗(yàn)證傳入的 API 的請(qǐng)求時(shí)使用 Passport 的 TokenGuard 來處理:

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

自定義遷移

如果你不打算使用 Passport 的默認(rèn)遷移,你應(yīng)該在 AppServiceProviderregister 方法中調(diào)用 Passport::ignoreMigrations 方法。 你可以用這個(gè)命令 php artisan vendor:publish --tag=passport-migrations 導(dǎo)出默認(rèn)遷移。

默認(rèn)情況下,Passport 使用字段 「user_id」標(biāo)識(shí)用戶。如果想使用不同的字段標(biāo)識(shí)用戶 (例如:uuid),可以修改默認(rèn)的 Passport 遷移文件。

前端快速上手

{note} 為了使用 Passport 的 Vue 組件,你必須使用 Vue JavaScript 框架。這些組件也使用了 Bootstrap CSS 框架。然而,如果你不打算使用這些工具,這些組件對(duì)于你自己的前端組件編寫也十分有價(jià)值。

Passport 提供了一系列 JSON API ,你可以用它們來允許你的用戶創(chuàng)建客戶端和個(gè)人訪問令牌。然而,編寫與這些 API 交互的前端代碼可能是很占用時(shí)間的。因此,Passport 也包括了預(yù)編譯的 Vue 組件,你可以直接使用或?qū)⑵渥鳛槟阕约旱那岸藚⒖肌?/p>

要使用 Passport 的 Vue 組件,使用 vendor:publish Artisan 命令:

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

被發(fā)布的組件將會(huì)被放到 resources/js/components 目錄下。當(dāng)組件被發(fā)布后,你應(yīng)該在你的 resources/js/app.js 文件中注冊(cè)它們:

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 之前,在注冊(cè)組件時(shí)添加 “.default” 會(huì)導(dǎo)致控制臺(tái)錯(cuò)誤。有關(guān)此更改的解釋,請(qǐng)參閱 Laravel Mix v4.0.0 發(fā)布說明.

在注冊(cè)了組件后,請(qǐng)確保運(yùn)行 npm run dev 來重新編譯你的資源。 當(dāng)你重編譯你的資源后,你可以將組件放到你應(yīng)用的模板中以開始創(chuàng)建客戶端和個(gè)人訪問令牌:

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

部署 Passport

第一次在你的生產(chǎn)環(huán)境部署 Passport 時(shí),你大概需要運(yùn)行 passport:keys 命令。這個(gè)命令生成 Passport 生成訪問令牌所需的密鑰。生成的密鑰一般情況下不應(yīng)放在版本控制中:

php artisan passport:keys

可以使用 Passport::loadKeysFrom 方法來自定義 Passport 密鑰的加載路徑:

/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @return void
 */
 public function boot(){
     $this->registerPolicies();    
     Passport::routes();    
     Passport::loadKeysFrom('/secret-keys/oauth');
    }

配置

令牌的有效期

默認(rèn)情況下,Passport 發(fā)放的訪問令牌是有一年有效期的。但是如果你想自定義訪問令牌的有效期,可以使用 tokensExpireInrefreshTokensExpireIn 方法。上述兩個(gè)方法同樣需要在 AuthServiceProviderboot 方法中調(diào)用:

/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @return void
 */
 public function boot(){
     $this->registerPolicies();    
     Passport::routes();    
     Passport::tokensExpireIn(now()->addDays(15));    
     Passport::refreshTokensExpireIn(now()->addDays(30));
   }

覆蓋默認(rèn)模型

可以自由擴(kuò)展 Passport 使用的模型,通過 Passport 類自定義模型覆蓋默認(rèn)模型:

use App\Models\Passport\Client;
use App\Models\Passport\AuthCode;
use App\Models\Passport\TokenModel;
use App\Models\Passport\PersonalAccessClient;
/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @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 中必不可少的部分就是授權(quán)碼。當(dāng)使用授權(quán)碼時(shí),客戶端應(yīng)用程序會(huì)將用戶重定向到你的服務(wù)器,他們將批準(zhǔn)或拒絕向客戶端發(fā)出訪問令牌的請(qǐng)求。

管理客戶端

首先,構(gòu)建需要與應(yīng)用程序 API 交互的應(yīng)用程序,開發(fā)人員將需要通過創(chuàng)建一個(gè)「客戶端」來注冊(cè)自己的應(yīng)用程序。一般來說,這包括在用戶批準(zhǔn)其授權(quán)請(qǐng)求后,提供其應(yīng)用程序的名稱和應(yīng)用程序可以重定向到的 URL。

The passport:client 命令

創(chuàng)建客戶端最簡(jiǎn)單的方式是使用 Artisan 命令 passport:client,你可以使用此命令創(chuàng)建自己的客戶端,用于測(cè)試你的 OAuth2 的功能。在你執(zhí)行 client 命令時(shí),Passport 會(huì)提示你輸入有關(guān)客戶端的信息,最終會(huì)給你提供客戶端的 ID 和 密鑰:

php artisan passport:client

Redirect URLs

當(dāng)有多個(gè)重定向 URL 白名單時(shí),可以在 passport:client 命令提示輸入 URL 時(shí),使用逗號(hào)分隔來指定:

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

{note} 任何包含逗號(hào)的 URL 都必須進(jìn)行編碼。

JSON API

考慮到你的用戶無法使用 client 命令,Passport 為此提供了可用于創(chuàng)建「客戶端」的 JSON API。這樣你就不用再花時(shí)間編寫控制器來創(chuàng)建、更新和刪除客戶端。

然而,你仍舊需要基于 Passport 的 JSON API 開發(fā)一套前端界面,為你的用戶提供管理客戶端的面板。下面我們會(huì)列出所有用于管理客戶端的 API,為了方便起見,我們使用 Axios 來演示對(duì)端口發(fā)出 HTTP 請(qǐng)求。

這個(gè) JSON API 由 webauth 兩個(gè)中間件保護(hù),所以只能從應(yīng)用程序中調(diào)用,不能從外部調(diào)用。

{tip} 如果你不想自己實(shí)現(xiàn)整個(gè)客戶端管理的前端界面,可以使用  前端快速上手 在幾分鐘內(nèi)組建一套功能齊全的前端界面。

GET /oauth/clients

此路由會(huì)返回認(rèn)證用戶的所有客戶端。主要用途是列出所有用戶的客戶端,以便他們可以編輯或刪除它們:

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

POST /oauth/clients

此路由用于創(chuàng)建新客戶端。它需要兩個(gè)參數(shù):客戶端的名稱 name 和授權(quán)后回調(diào)的 URL redirect。在批準(zhǔn)或拒絕授權(quán)請(qǐng)求后,用戶會(huì)被重定向到 redirect 參數(shù)提供的鏈接。

當(dāng)客戶端創(chuàng)建后,會(huì)返回客戶端的 ID 和密鑰??蛻舳丝梢允褂眠@兩個(gè)值從你的授權(quán)服務(wù)請(qǐng)求訪問令牌 (Access token) 。該路由會(huì)返回新的客戶端實(shí)例:

const data = {
    name: 'Client Name',
    redirect: 'http://example.com/callback'};
    axios.post('/oauth/clients', data)
        .then(response => {
                console.log(response.data); 
           })
         .catch (response => {
                // 在response里列出錯(cuò)誤詳情...    
             });

PUT /oauth/clients/{client-id}

此路由用于更新客戶端信息。它需要兩個(gè)參數(shù):客戶端的名稱 name 和授權(quán)后回調(diào)的 URL redirect。在批準(zhǔn)或拒絕授權(quán)請(qǐng)求后,用戶會(huì)被重定向 redirect 到這個(gè)鏈接。此路由會(huì)返回更新后的客戶端實(shí)例:

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里列出錯(cuò)誤詳情...    
            });

DELETE /oauth/clients/{client-id}

此路由用于刪除客戶端 (client):

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

請(qǐng)求令牌

授權(quán)時(shí)的重定向

客戶端創(chuàng)建之后,開發(fā)者會(huì)使用此客戶端的 ID 和密鑰來請(qǐng)求授權(quán)代碼,并從應(yīng)用程序訪問令牌。首先,接入應(yīng)用的用戶向你應(yīng)用程序的 /oauth/authorize 路由發(fā)出重定向請(qǐng)求,示例如下:

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 已經(jīng)在 Passport::routes 方法中定義。你不需要手動(dòng)定義此路由。

批準(zhǔn)請(qǐng)求

接收到授權(quán)請(qǐng)求時(shí),Passport 會(huì)自動(dòng)向用戶顯示一個(gè)模版頁面,允許用戶批準(zhǔn)或拒絕授權(quán)請(qǐng)求。如果用戶批準(zhǔn)請(qǐng)求,他們會(huì)被重定向回接入的應(yīng)用程序指定的 redirect_uri。redirect_uri 必須和客戶端創(chuàng)建時(shí)指定的 redirect 鏈接完全一致。

如果你想自定義授權(quán)確認(rèn)頁面,可以使用 Artisan 命令 vendor:publish 發(fā)布 Passport 的視圖。發(fā)布后的視圖文件存放在 resources/views/vendor/passport

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

將授權(quán)碼轉(zhuǎn)換為訪問令牌

用戶批準(zhǔn)授權(quán)請(qǐng)求后,會(huì)被重定向回接入的應(yīng)用程序。然后接入應(yīng)用應(yīng)該將通過 POST 請(qǐng)求向你的應(yīng)用程序申請(qǐng)?jiān)L問令牌。請(qǐng)求應(yīng)該包括當(dāng)用戶批準(zhǔn)授權(quán)請(qǐng)求時(shí)由應(yīng)用程序發(fā)出的授權(quán)碼。在下面的例子中,我們使用 Guzzle HTTP 庫來實(shí)現(xiàn)這次 POST 請(qǐng)求:

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 響應(yīng)中會(huì)包含 access_token 、refresh_tokenexpires_in 屬性。expires_in 屬性包含訪問令牌的有效期(單位:秒)。

{tip} 像 /oauth/authorize 路由一樣,/oauth/token 路由在 Passport::routes 方法中定義了,你沒必要手動(dòng)去定義它。默認(rèn)情況下,此路由使用 “ThrottleRequests” 中間件的設(shè)置進(jìn)行限流。

刷新令牌

如果你的應(yīng)用程序發(fā)放了短期的訪問令牌,用戶將需要通過在發(fā)出訪問令牌時(shí)提供給他們的刷新令牌來刷新其訪問令牌。在下面的例子中,我們使用 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 會(huì)返回一個(gè) JSON 響應(yīng),其中包含 access_token 、refresh_tokenexpires_in 屬性。expires_in 屬性包含訪問令牌的有效時(shí)間(單位:秒)。

密碼授權(quán)令牌

OAuth2 密碼授權(quán)機(jī)制可以讓你自己的客戶端(如移動(dòng)應(yīng)用程序)使用郵箱地址或者用戶名和密碼獲取訪問令牌。如此一來你就可以安全地向自己的客戶端發(fā)出訪問令牌,而不需要遍歷整個(gè) OAuth2 授權(quán)代碼重定向流程

創(chuàng)建密碼授權(quán)客戶端

在應(yīng)用程序通過密碼授權(quán)機(jī)制來發(fā)布令牌之前,在 passport:client 命令后加上 --password 參數(shù)來創(chuàng)建密碼授權(quán)的客戶端。如果你已經(jīng)運(yùn)行了 passport:install 命令,則不需要再運(yùn)行此命令:

php artisan passport:client --password

請(qǐng)求令牌

創(chuàng)建密碼授權(quán)的客戶端后,就可以使用用戶的電子郵件地址和密碼向 /oauth/token 路由發(fā)出 POST 請(qǐng)求來獲取訪問令牌。而該路由已經(jīng)由 Passport::routes 方法注冊(cè),因此不需要手動(dòng)定義它。如果請(qǐng)求成功,會(huì)在服務(wù)端返回的 JSON 響應(yīng)中收到一個(gè) access_tokenrefresh_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} 默認(rèn)情況下,訪問令牌是長(zhǎng)期有效的。你可以根據(jù)需要 配置訪問令牌的有效時(shí)間 。

請(qǐng)求所有作用域

使用密碼授權(quán)機(jī)制時(shí),可以通過請(qǐng)求 scope 參數(shù) * 來授權(quán)應(yīng)用程序支持的所有范圍的令牌。如果你的請(qǐng)求中包含 scope 為 * 的參數(shù),令牌實(shí)例上的 can 方法會(huì)始終返回 true。這種作用域的授權(quán)只能分配給使用 password 授權(quán)時(shí)發(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' => '*',
        ],
      ]);

自定義用戶名字段

當(dāng)使用密碼授權(quán)時(shí),Passport 默認(rèn)使用 email 作為「用戶名」。但是,你可以通過在模型上定義一個(gè) 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;    
      /**
     * 通過用戶名找到對(duì)應(yīng)的用戶信息
     *
     * @param  string  $username
     * @return \App\User
     */   
      public function findForPassport($username) 
         {    
             return $this->where('username', $username)->first(); 
         }
      }

隱式授權(quán)令牌

隱式授權(quán)類似于授權(quán)碼授權(quán),但是它只將令牌返回給客戶端而不交換授權(quán)碼。這種授權(quán)最常用于無法安全存儲(chǔ)客戶端憑據(jù)的 JavaScript 或移動(dòng)應(yīng)用程序。通過調(diào)用 AuthServiceProvider 中的 enableImplicitGrant 方法來啟用這種授權(quán):

/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @return void
 */
 public function boot(){
     $this->registerPolicies();    
     Passport::routes();    
     Passport::enableImplicitGrant();
    }

調(diào)用上面方法開啟授權(quán)后,開發(fā)者可以使用他們的客戶端 ID 從應(yīng)用程序請(qǐng)求訪問令牌。接入的應(yīng)用程序應(yīng)該向你的應(yīng)用程序的 /oauth/authorize 路由發(fā)出重定向請(qǐng)求,如下所示:

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 路由已經(jīng)在 Passport::routes 方法中定義好,所以無需再次手動(dòng)定義此路由。

客戶端憑據(jù)授權(quán)令牌

客戶端憑據(jù)授權(quán)適用于機(jī)器到機(jī)器的認(rèn)證。例如,你可以在通過 API 執(zhí)行維護(hù)任務(wù)中使用此授權(quán)。

在客戶端憑據(jù)授權(quán)之前,需要先創(chuàng)建一個(gè)客戶端憑據(jù)授權(quán)的客戶端,你可以使用 passport:client 命令的 --client 參數(shù)來創(chuàng)建:

php artisan passport:client --client

接下來,要使用這種授權(quán),你首先需要在 app/Http/Kernel.php$routeMiddleware 變量中添加新的中間件:

use Laravel\Passport\Http\Middleware\CheckClientCredentials;
protected $routeMiddleware = [
    'client' => CheckClientCredentials::class,
  ];

然后,在路由上追加這個(gè)中間件:

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

若要將對(duì)路由的訪問限制在某個(gè)作用域內(nèi),可在將 client 中間件附加到路由時(shí)提供以逗號(hào)分隔的所需作用域列表:

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

獲取令牌

通過向 oauth/token 接口發(fā)出請(qǐng)求來獲取令牌:

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

個(gè)人訪問令牌

有時(shí)候,用戶要在不經(jīng)過傳統(tǒng)的授權(quán)碼重定向流程的情況下向自己發(fā)出訪問令牌。允許用戶通過應(yīng)用程序用戶界面對(duì)自己發(fā)出令牌,有助于用戶體驗(yàn)?zāi)愕?API,或者也可以將其作為一種更簡(jiǎn)單的發(fā)布訪問令牌的方式。

{note} 個(gè)人訪問令牌是永久有效的,就算使用了 tokensExpireInrefreshTokensExpireIn 方法也不會(huì)修改它的生命周期。

創(chuàng)建個(gè)人訪問客戶端

在你的應(yīng)用程序發(fā)布個(gè)人訪問令牌之前,你需要在 passport:client 命令后帶上 --personal 參數(shù)來創(chuàng)建對(duì)應(yīng)的客戶端。如果你已經(jīng)運(yùn)行了 passport:install 命令,則無需再運(yùn)行此命令:

php artisan passport:client --personal

如果你已經(jīng)創(chuàng)建了個(gè)人訪問客戶端,你可以通過調(diào)用 AuthServiceProvider 中的 personalAccessClientId 方法來啟用:

/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @return void
 */
 public function boot(){ 
    $this->registerPolicies();    
    Passport::routes();    
    Passport::personalAccessClientId('client-id');
  }

管理個(gè)人訪問令牌

創(chuàng)建個(gè)人訪問客戶端后,你可以使用 User 模型實(shí)例上的 createToken 方法來為給定用戶發(fā)布令牌。createToken 方法接受令牌的名稱作為其第一個(gè)參數(shù)和可選的 作用域 數(shù)組作為其第二個(gè)參數(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 中也有用來管理個(gè)人訪問令牌的 JSON API,你可以將其與自己的前端配對(duì),為用戶提供管理個(gè)人訪問令牌的儀表板。下面我們會(huì)介紹用于管理個(gè)人訪問令牌的所有 API 接口。方便起見,我們使用  Axios 來演示對(duì) API 的接口發(fā)出 HTTP 請(qǐng)求。

JSON API 由 webauth 中間件保護(hù);因此,只能從您自己的應(yīng)用程序中調(diào)用它。無法從外部源調(diào)用它。

{tip} 如果你不想實(shí)現(xiàn)自己的個(gè)人訪問令牌管理的前端界面,可以根據(jù)  前端快速上手 在幾分鐘內(nèi)組建功能齊全的前端界面。

GET /oauth/scopes

此路由會(huì)返回應(yīng)用程序中定義的所有 作用域。你可以使用此路由列出用戶可能分配給個(gè)人訪問令牌的范圍:

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

GET /oauth/personal-access-tokens

此路由返回認(rèn)證用戶創(chuàng)建的所有個(gè)人訪問令牌。這主要用于列出所有用戶的令牌,以便他們可以編輯或刪除它們:

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

POST /oauth/personal-access-tokens

此路由用于創(chuàng)建新的個(gè)人訪問令牌。它需要兩個(gè)數(shù)據(jù):令牌的 namescpoe

const data = {
    name: 'Token Name',
    scopes: []};
    axios.post('/oauth/personal-access-tokens', data)
        .then(response => {
            console.log(response.data.accessToken);    
        })    
        .catch (response => { 
               // 列出響應(yīng)中錯(cuò)誤...    
        });

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

此路由可用于刪除個(gè)人訪問令牌:

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

路由保護(hù)

通過中間件

Passport 包含一個(gè) 驗(yàn)證保護(hù)機(jī)制 可以驗(yàn)證請(qǐng)求中傳入的訪問令牌。配置 api 的看守器使用 passport 驅(qū)動(dòng)程序后,只需要在需要有效訪問令牌的任何路由上指定 auth:api 中間件:

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

傳遞訪問令牌

當(dāng)調(diào)用 Passport 保護(hù)下的路由時(shí),接入的 API 應(yīng)用需要將訪問令牌作為 Bearer 令牌放在請(qǐng)求頭 Authorization 中。例如,使用 Guzzle HTTP 庫時(shí):

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

令牌作用域

作用域可以讓 API 客戶端在請(qǐng)求賬戶授權(quán)時(shí)請(qǐng)求特定的權(quán)限。例如,如果你正在構(gòu)建電子商務(wù)應(yīng)用程序,并不是所有接入的 API 應(yīng)用都需要下訂單的功能。你可以讓接入的 API 應(yīng)用只被允許授權(quán)訪問訂單發(fā)貨狀態(tài)。換句話說,作用域允許應(yīng)用程序的用戶限制第三方應(yīng)用程序執(zhí)行的操作。

定義作用域

你可以在 AuthServiceProviderboot 方法中使用 Passport::tokensCan 方法來定義 API 的作用域。tokensCan 方法接受一個(gè)包含作用域名稱和描述的數(shù)組作為參數(shù)。作用域描述將會(huì)在授權(quán)確認(rèn)頁中直接展示給用戶,你可以將其定義為任何你需要的內(nèi)容:

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

默認(rèn)作用域

如果客戶端沒有請(qǐng)求任何特定的范圍,你可以在 AuthServiceProviderboot 方法中使用 Passport::setDefaultScope 方法來定義默認(rèn)的作用域。

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

給令牌分配作用域

請(qǐng)求授權(quán)碼

使用授權(quán)碼請(qǐng)求訪問令牌時(shí),接入的應(yīng)用需為 scope 參數(shù)指定所需作用域。 scope 參數(shù)包含多個(gè)作用域時(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ā)個(gè)人訪問令牌

使用 User 模型的 createToken 方法發(fā)放個(gè)人訪問令牌時(shí),可以將所需作用域的數(shù)組作為第二個(gè)參數(shù)傳給此方法:

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

檢查作用域

Passport 包含兩個(gè)中間件,可用于驗(yàn)證傳入的請(qǐng)求是否包含訪問指定作用域的令牌。 使用之前,需要將下面的中間件添加到 app/Http/Kernel.php 文件的 $routeMiddleware 屬性中:

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

檢查所有作用域

路由可以使用 scopes 中間件來檢查當(dāng)前請(qǐng)求是否擁有指定的 所有 作用域:

Route::get('/orders', function () { 
   // Access token has both "check-status" and "place-orders" scopes...
   })->middleware('scopes:check-status,place-orders');

檢查任意作用域

路由可以使用 scope 中間件來檢查當(dāng)前請(qǐng)求是否擁有指定的 任意 作用域:

Route::get('/orders', function () {
    // 訪問令牌具有 "check-status" 或 "place-orders" 作用域...
  })->middleware('scope:check-status,place-orders');

檢查令牌實(shí)例上的作用域

就算含有訪問令牌驗(yàn)證的請(qǐng)求已經(jīng)通過應(yīng)用程序的驗(yàn)證,你仍然可以使用當(dāng)前授權(quán) User 實(shí)例上的 tokenCan 方法來驗(yàn)證令牌是否擁有指定的作用域:

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

附加作用域方法

scopeIds 方法將返回所有已定義 ID / 名稱的數(shù)組:

Laravel\Passport\Passport::scopeIds();

scopes 方法將返回一個(gè) 包含所有已定義作用域數(shù)組的 Laravel\Passport\Scope 實(shí)例:

Laravel\Passport\Passport::scopes();

scopesFor 方法將返回與給定 ID / 名稱匹配的 Laravel\Passport\Scope 實(shí)例數(shù)組:

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

你可以使用 hasScope 方法確定是否已定義給定作用域:

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

使用 JavaScript 接入 API

在構(gòu)建 API 時(shí), 如果能通過 JavaScript 應(yīng)用接入自己的 API 將會(huì)給開發(fā)過程帶來極大的便利。這種 API 開發(fā)方法允許你使用自己的應(yīng)用程序的 API 和別人共享的 API 。你的 Web 應(yīng)用程序、移動(dòng)應(yīng)用程序、第三方應(yīng)用程序以及可能在各種軟件包管理器上發(fā)布的任何 SDK 都可能會(huì)使用相同的 API 。

通常,如果要在 JavaScript 應(yīng)用程序中使用 API ,需要手動(dòng)向應(yīng)用程序發(fā)送訪問令牌,并將其傳遞給應(yīng)用程序。但是, Passport 有一個(gè)可以處理這個(gè)問題的中間件。將 CreateFreshApiToken 中間件添加到 app/Http/Kernel.php 文件中的 web 中間件組就可以了:

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

{note} 你應(yīng)該確保在您的中間件堆棧中 CreateFreshApiToken 中間件之前列出了 EncryptCookies 中間件。

這個(gè) Passport 中間件將在你所有的對(duì)外請(qǐng)求中添加一個(gè) laravel_token cookie 。該 cookie 將包含一個(gè)加密后的 JWT , Passport 將用來驗(yàn)證來自 JavaScript 應(yīng)用程序的 API 請(qǐng)求。至此,你可以在不明確傳遞訪問令牌的情況下向應(yīng)用程序的 API 發(fā)出請(qǐng)求:

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

自定義 Cookie 名稱

如果需要,你可以在 AuthServiceProviderboot 方法中使用 Passport::cookie 方法來自定義 laravel_token cookie 的名稱。

/**
 * 注冊(cè)認(rèn)證 / 授權(quán)服務(wù)
 *
 * @return void
 */
 public function boot(){
     $this->registerPolicies();    
     Passport::routes();    
     Passport::cookie('custom_name');
   }

CSRF 保護(hù)

當(dāng)使用這種授權(quán)方法時(shí),默認(rèn)的 Laravel JavaScript 腳手架會(huì)讓 Axios 發(fā)送 X-CSRF-TOKENX-Requested-With 請(qǐng)求頭。另外,你必須確保 HTML meta 標(biāo)簽 標(biāo)簽 中包含了 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ā)出訪問令牌和刷新令牌時(shí)觸發(fā)事件。你可以在應(yīng)用程序 的 EventServiceProvider 中為這些事件追加監(jiān)聽器,并在監(jiān)聽器中撤銷或修改其他令牌:

/**
 * 應(yīng)用程序事件監(jiān)聽映射
 *
 * @var array
 */
 protected $listen = [
     'Laravel\Passport\Events\AccessTokenCreated' => [
             'App\Listeners\RevokeOldTokens', 
               ],    
      'Laravel\Passport\Events\RefreshTokenCreated' => [ 
             'App\Listeners\PruneOldTokens',   
             ],
         ];

測(cè)試

Passport 的 actingAs 方法可以指定當(dāng)前已認(rèn)證用戶及其作用域。actingAs 方法的第一個(gè)參數(shù)是用戶實(shí)例,第二個(gè)參數(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);
     }
本文章首發(fā)在 LearnKu.com 網(wǎng)站上。