laravel 結合JWT開發API

逍遙俠發表於2018-08-28

    最近一直在忙公司小程式,還有微信的一些H5開發,當然作為後端的我一直都只是寫寫簡單的邏輯。由於之前一直都是混編開發,心裡確實是累啊。所以也想可以提升下組內的技術水平,同時也是為了更好的分工,於是就想往前後端分離這種模式進行探索。

    雖知道接觸一門新的東西,是要付出巨大的努力的。為了可以讓以後自己少走點彎路,在專案進度符合預期的同時,專門抽了時間針對API開發進行了碎片的學習。本篇文章借鑑了很多同行的事例程式碼,同時也結合了自己的一點分析。希望各位道友能在其中有所感悟。

    談起API開發,我想第一個要解決的就是使用者的狀態問題吧,畢竟HTTP是屬於無狀態的協議,我們在構建API的時候不能像普通的應用與系統開發一樣,直接把使用者的資料存放session然後再進行登入的校驗。為此有大神就想出了JWT類似的解決方案,通過token來對使用者的授權以及狀態進行一個感知。

    在介紹 JWT 之前, 我們首先介紹一下, 傳統的伺服器端使用 session 對多使用者進行授權的方式. 當然在 session 之前還有 cookie 的方式來儲存使用者的授權資訊在客戶機(比如瀏覽器)上, 不過純 cookie 的方式過於不安全, 我們就把 cookie 跟 session 一起說.

之所以需要授權機制, 是因為 http 的無狀態性(stateless).

也就是說當一個使用者傳送一次請求, 請求中附帶賬戶名和密碼登入成功之後, 如果這個這個使用者再次傳送一次請求, 伺服器是不能知道這個使用者是已經登入過的, 這個時候伺服器就還需要使用者再次提供授權資訊, 也就是使用者名稱和密碼.

如果客戶端能更少的把自己的身份授權資訊在網路上傳輸, 那麼客戶端就能更大程度上避免自己的身份資訊被洩露.

而 session 和 token 都是為了解決此問題出現的.

session 原理概述

認證流程

  1. 當使用者使用使用者名稱和密碼登入之後, 伺服器就會生成一個 session 檔案, session 檔案中儲存著對這個使用者的授權資訊, 這個檔案可以儲存在硬碟/記憶體/資料庫中.
  2. 同時還要生成一個對應這個 session 檔案的 sessionid, 通過 sessionid 就能夠找到這個 session 檔案.
  3. 然後將 sessionid 傳送給客戶端, 客戶端就將 sessionid 儲存起來, 儲存的方式有很多種, 目前大多情況是通過 cookie 來儲存 sessionid.
  4. 儲存之後, 當客戶機以後再向伺服器傳送請求的時候, 請求攜帶上 sessionid, 這樣伺服器收到 sessionid 之後, 自己就會在服務區上查詢對應的 session 檔案, 如果查詢成功, 就會得到該使用者的授權資訊, 從而完成一次授權.

session 的出現解決了一部分的問題, 但隨著時間的推移和網際網路的發展, 一些缺陷也隨之暴露出來, 比如但不僅限於以下幾點

  • 隨著使用者量的增加, 每個使用者都需要在伺服器上建立一個 session 檔案, 這對伺服器造成了壓力
  • 對於伺服器壓力的分流問題, 如果一個使用者的 session 被儲存在某臺伺服器上, 那麼當這個使用者訪問伺服器時, 使用者就只能在這臺伺服器上完成授權, 其他的分流伺服器無法進行對這種請求進行分流
  • 同樣也是 session 儲存的問題, 當我們在一臺伺服器上成功登入, 如果我們想要另外的一臺別的域名的伺服器也能讓使用者不登入就能完成授權, 這個時候就會有很多麻煩

為了解決此類問題, token 應運而生了.

Token 原理概述

認證流程

  1. 客戶端傳送認證資訊(一般就是使用者名稱/密碼), 向伺服器傳送請求
  2. 伺服器驗證客戶端的認證資訊, 驗證成功之後, 伺服器向客戶端返回一個 加密的token(一般情況下就是一個字串)
  3. 客戶端儲存(cookie, session, app 中都可以儲存)這個 token, 在之後每次向伺服器傳送請求時, 都攜帶上這個 token
  4. 伺服器驗證這個 token 的合法性, 只要驗證通過, 伺服器就認為該請求是一個合法的請求

JWT 概述

token 只是一種思路, 一種解決使用者授權問題的思考方式, 基於這種思路, 針對不同的場景可以有很多種的實現. 而在眾多的實現中, JWT(JSON Web Token) 的實現最為流行.

JWT 這個標準提供了一系列如何建立具體 token 的方法, 這些緣故方法和規範可以讓我們建立 token 的過程變得更加合理和效率.

比如, 傳統的做法中, 伺服器會儲存生成的token, 當客戶端傳送來token時, 與伺服器的進行比對, 但是 jwt 的不需要在伺服器儲存任何 token, 而是使用一套加密/解密演算法 和 一個金鑰 來對使用者發來的token進行解密, 解密成功後就可以得到這個使用者的資訊.

這樣的做法同時也增加了多伺服器時的擴充套件性, 在傳統的 token 驗證中, 一旦使用者發來 token, 那麼必須要先找到儲存這個 token 的伺服器是哪臺伺服器, 然後由那一臺伺服器進行驗證使用者身份. 而 jwt 的存在, 只要每一臺伺服器都知道解密金鑰, 那麼每一臺伺服器都可以擁有驗證使用者身份的能力.

這樣一來, 伺服器就不再儲存任何使用者授權的資訊了, 也就解決了 session 曾出現的問題.


簡單介紹完了 JWT 之後, 接下來我們就簡單看一下在實際場景中 JWT 的應用.


Laravel

1. 使用 composer 安裝

# 建議使用1.0以上版本
composer require tymon/jwt-auth 1.*@rc
複製程式碼

2. 進行一些配置

這裡指的注意的是,有些文件會說要新增 Tymon\JWTAuth\Providers\LaravelServiceProvider::class,這隻在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本無需新增。

還有一些文件說要新增 Tymon\JWTAuth\Providers\JWTAuthServiceProvider 這是很久以前的 JWT 版本的(大概0.5.3 以前的版本)。

2.1 釋出配置檔案

# 這條命令會在 config 下增加一個 jwt.php 的配置檔案
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
複製程式碼

2.2 生成加密金鑰

# 這條命令會在 .env 檔案下生成一個加密金鑰,如:JWT_SECRET=foobar
php artisan jwt:secret
複製程式碼

2.3 更新你的模型

如果你使用預設的 User 表來生成 token,你需要在該模型下增加一段程式碼

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject    # 這裡別忘了加
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     * 獲取將儲存在JWT的中的識別符號token。
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     * 返回一個鍵值陣列,其中包含要新增到JWT中的任何自定義宣告
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}
複製程式碼

上面兩個方法,其實就是實現了JWTSubject藉口必須實現的方法。這個也是按照官方文件進行配置即可。

2.4 註冊兩個 Facade

這兩個 Facade 並不是必須的,但是使用它們會給你的程式碼編寫帶來一點便利。

config/app.php

'aliases' => [
        ...
        // 新增以下兩行
        'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
        'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
複製程式碼

如果你不使用這兩個 Facade,你可以使用輔助函式 auth()

auth() 是一個輔助函式,返回一個guard,暫時可以看成 Auth Facade。

關於Auth Facade。建議大家參考這篇文章,我在學習JWT的時候也是看了好幾遍才看懂的。

Laravel 輔助函式 auth 與 JWT 擴充套件詳解


2.5 修改 auth.php

config/auth.php

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

    'api' => [
        'driver' => 'jwt',      // 原來是 token 改成jwt
        'provider' => 'users',
    ],
],
複製程式碼

2.6 註冊一些路由

注意:在 Laravel 下,route/api.php 中的路由預設都有字首 api

Route::group([

    'prefix' => 'auth'

], function ($router) {

    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');

});
複製程式碼

2.7 建立 token 控制器

php artisan make:controller AuthController
複製程式碼

AuthController

值得注意的是 Laravel 這要用 auth('api')

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;

class AuthController extends Controller
{
    /**
     * Get a JWT via given credentials.
     * 該方法用於生成token
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth('api')->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     * 該方法通過token獲取對應的使用者資訊
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     * 重新整理token,如果開啟黑名單,以前的token便會失效。
     * 值得注意的是用上面的getToken再獲取一次Token並不算做重新整理,兩次獲得的Token是並行的,即兩個都可用。
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     * 該方法按照指定的格式返回輸出資訊
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }
}
複製程式碼

JWT Token 詳解

1. token 的獲取、使用、刪除和重新整理

  • 以下用 postman 演示,
  • Laravel 環境下寫在 api.php 中的路由預設有字首 api

1.1 獲取 token

laravel 結合JWT開發API

1.2 使用 token

有兩種使用方法:

  • 加到 url 中:?token=你的token
  • 加到 header 中,建議用這種,因為在 https 情況下更安全:Authorization:Bearer 你的token

laravel 結合JWT開發API

1.3 刪除 token

laravel 結合JWT開發API

刪除 token 後,token就會失效,無法再利用其獲取資料。


1.4 重新整理 token


laravel 結合JWT開發API

到此我們就已經可以成功地把JWT的開發例子演示完畢了,當然下面我會繼續的把一些常用的方法展示出來,另外也會附上JWT的一些資料結構的專門文章。

JWT實現原理

2. token 的建立

前面的 AuthController.php 中有兩行展現了這一種 token 的建立方法,即用使用者所給的賬號和密碼進行嘗試,密碼正確則用對應的 User 資訊返回一個 token

token 的建立方法不止這一種,接下來介紹 token 的三種建立方法:

  • 基於賬密引數
  • 基於 users 模型返回的例項
  • 基於 users 模型中的使用者主鍵 id

a) 基於賬密引數

這就是剛剛說的哪一種,貼出具體程式碼。

// 使用輔助函式
$credentials = request(['email', 'password']); 
$token = auth('api')->attempt($credentials)

// 使用 Facade
$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);
複製程式碼

b) 基於 users 模型返回的例項

// 使用輔助函式
$user = User::first();
$token = auth('api')->login($user);

// 使用 Facade
$user = User::first();
$token = JWTAuth::fromUser($credentials);
複製程式碼

c) 基於 users 模型中的主鍵 id

// 使用輔助函式
$token = auth('api')->tokenById(1);
複製程式碼

2.1 token 的解析

a) 解析 token 到物件

只有 Facade 需要這樣。

// 把請求傳送過來的直接解析到物件
JWTAuth::parseToken();
複製程式碼

b) 獲取 token 中的 user 資訊

// 輔助函式
$user = auth()->user();

// Facade
$user = JWTAuth::parseToken()->authenticate();
複製程式碼

c) 獲取 token

如果 token 被設定則會返回,否則會嘗試使用方法從請求中解析 token ,如果token未被設定或不能解析最終返回false。

// 輔助函式
$token = auth()->getToken();

// Facade
$token = JWTAuth::parseToken()->getToken();
複製程式碼

d) 如果是前端

直接 base64 解碼 token 的前兩段即可以知道所需的資訊。


3. 載荷的設定和獲取

a) 載荷設定

載荷資訊會在 token 解碼時得到,同時越大的陣列會生成越長的 token ,所以不建議放太多的資料。同時因為載荷是用 Base64Url 編碼,所以相當於明文,因此絕對不能放密碼等敏感資訊。

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];

// 輔助函式
$token = auth()->claims($customClaims)->attempt($credentials);

// Facade - 1
$token = JWTAuth::claims($customClaims)->attempt($credentials);
複製程式碼

b) 載荷解析

從請求中把載荷解析出來。可以去看擴充套件原始碼,裡面還有很多的方法。

// 輔助函式
$exp = auth()->payload()->get('exp');
$json = auth()->payload()->toJson();
$array = auth()->payload()->jsonSerialize();
$sub = $array['sub'];

// Facade - 1
$payload = JWTAuth::parseToken()->getPayload();
$payload->get('sub'); // = 123
$payload['jti']; // = 'asfe4fq434asdf'
$payload('exp') // = 123456
$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc

// Facade - 2
$exp = JWTAuth::parseToken()->getClaim('exp');
複製程式碼

4. token 的三個時間

一個 token 一般來說有三個時間屬性,其配置都在 config/jwt.php 內。

有效時間

有效時間指的的是你獲得 token 後,在多少時間內可以憑這個 token 去獲取內容,逾時無效。

// 單位:分鐘
'ttl' => env('JWT_TTL', 60)
複製程式碼

重新整理時間

重新整理時間指的是在這個時間內可以憑舊 token 換取一個新 token。例如 token 有效時間為 60 分鐘,重新整理時間為 20160 分鐘,在 60 分鐘內可以通過這個 token 獲取新 token,但是超過 60 分鐘是不可以的,然後你可以一直迴圈獲取,直到總時間超過 20160 分鐘,不能再獲取。

// 單位:分鐘
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160)
複製程式碼

寬限時間

寬限時間是為了解決併發請求的問題,假如寬限時間為 0s ,那麼在新舊 token 交接的時候,併發請求就會出錯,所以需要設定一個寬限時間,在寬限時間內,舊 token 仍然能夠正常使用

// 寬限時間需要開啟黑名單(預設是開啟的),黑名單保證過期token不可再用,最好開啟
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)

// 設定寬限時間,單位:秒
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 60)
複製程式碼

5.token 的重新整理問題?

a) token 為什麼要重新整理嗎?

首先 Basic Auth 是一種最簡單的認證方法,但是由於每次請求都帶使用者名稱和密碼,頻繁的傳輸肯定不安全,所以才有 cookiessession 的運用。如果 token 不重新整理,那麼 token 就相當於上面的使用者名稱+密碼,只要獲取到了,就可以一直盜用,因此 token 設定有效期並能夠進行重新整理是必要的。

b) token 有效期多久合適,重新整理頻率多久合適?

有效期越長,風險性越高,有效性越短,重新整理頻率越高,重新整理就會存在重新整理開銷,所以這需要綜合考慮。我個人一般考慮的範圍是:15min ~ 120min。

c) 有沒有必要每次都重新整理 token ?

上面考慮的 15min ~ 120min,會存在一個問題,就是重放攻擊風險,防禦這個風險,在 JWT 可用的方案是每次請求後都重新整理一次 token ,但這樣又會存在一個新的問題:併發請求。一次併發請求是用的一個 token ,第一個完成的請求會導致後面的請求全部失敗。可用的解決方案是設定寬限時間,即一個 token 重新整理後,舊 token 仍然短暫的可用。可惜這樣並不能完美的解決重放攻擊,只是增大了不法者攻擊的成本。這個問題在 JWT 中並沒有很好的解決。

下面是可用的中介軟體,第一二個功能一樣,但是第二個不會丟擲錯誤,第三四個功能一樣,沒什麼區別。

tymon\jwt-auth\src\Providers\AbstractServiceProvider.php

protected $middlewareAliases = [
    'jwt.auth' => Authenticate::class,
    'jwt.check' => Check::class,
    'jwt.refresh' => RefreshToken::class,
    'jwt.renew' => AuthenticateAndRenew::class,
];
複製程式碼

5.1 token 的重新整理總結

因為無法完全解決重放攻擊,所以在因重放攻擊會導致巨大安全問題和損失的地方,建議使用其他安全認證措施。而日常 Api 使用建議如下設定:

有效時間:15min ~ 120min
重新整理時間:7天 ~ 30天
寬限時間:60s
複製程式碼


其他常用方法附錄

1. JWT 的 兩個 Facade

1.1 JWTAuth

JWTAuth::parseToken()->方法() 一般都可以換成 auth()->方法()

token 生成

attempt

根據 user 賬密新建一個 token。

$credentials = $request->only('email', 'password');
$token = JWTAuth::attempt($credentials);
複製程式碼

fromUser or fromSubject

根據 user 物件生成一個 token。後者是前者別名

$user = User::find(1);
$token = JWTAuth::fromUser($user);
複製程式碼

token 控制

refresh

更新 token。

$newToken = JWTAuth::parseToken()->refresh();
複製程式碼

invalidate

讓一個 token 無效。

JWTAuth::parseToken()->invalidate();
複製程式碼

check

檢驗 token 的有效性。

if(JWTAuth::parseToken()->check()) {
    dd("token是有效的");
}
複製程式碼

token 解析

authenticate or toUser or user

這三個效果是一樣的,toUserauthenticate 的別名,而 user 比前兩者少一個 user id 的校驗,但並沒有什麼影響。

$user = JWTAuth::parseToken()->toUser();
複製程式碼

parseToken

從 request 中解析 token 到物件中,以便進行下一步操作。

JWTAuth::parseToken();
複製程式碼

getToken

從 request 中獲取token。

$token = JWTAuth::getToken();  // 這個不用 parseToken ,因為方法內部會自動執行一次
複製程式碼

載荷控制

customClaims or claims

設定載荷的 customClaims 部分。後者是前者的別名。

$customClaims = ['sid' => $sid, 'code' => $code];
$credentials = $request->only('email', 'password');
$token = JWTAuth::customClaims($customClaims)->attempt($credentials);
複製程式碼

getCustomClaims

獲取載荷的 customClaims 部分,返回一個陣列。

$customClaims = JWTAuth::parseToken()->getCustomClaims()
複製程式碼

getPayload or payload

獲取所有載荷,三個都是一樣的,最後一個一般用來檢驗 token 的有效性

$payload = JWTAuth::parseToken()->payload();

// then you can access the claims directly e.g.
$payload->get('sub'); // = 123
$payload['jti']; // = 'asfe4fq434asdf'
$payload('exp') // = 123456
$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc
複製程式碼

getClaim

獲取載荷中指定的一個元素

$sub = JWTAuth::parseToken()->getClaim('sub');
複製程式碼

1.2 JWTGuard

這個 Facade 主要進行載荷的管理,返回一個載荷物件,然後可以通過 JWTAuth 來對其生成一個 token

// 載荷的高度自定義
$payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();
$token = JWTAuth::encode($payload);
複製程式碼

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
複製程式碼

1.3 其他一些用法

這裡用 auth 的寫法,因為 Laravel 有多個 guard,預設 guard 也不是 api ,所以需要寫成 auth('api') 否則,auth() 即可。

設定載荷

$token = auth('api')->claims(['foo' => 'bar'])->attempt($credentials);
複製程式碼

顯示設定 token

$user = auth('api')->setToken('eyJhb...')->user();
複製程式碼

顯示設定請求

$user = auth('api')->setRequest($request)->user();
複製程式碼

重寫有效時間

$token = auth('api')->setTTL(7200)->attempt($credentials);
複製程式碼

驗證賬密是否正確

$boolean = auth('api')->validate($credentials);
複製程式碼


最後如果你想統一設定一下規則建議你還是使用中介軟體吧,這樣你就可以通過你的中介軟體去保護你需要驗證的路由與方法。文章貼出的是其他大神寫的中介軟體,怎麼說好呢?各位道友自己好好去摸索摸索吧!

中介軟體程式碼如下:

<?php

namespace App\Http\Middleware;

use Auth;
use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

// 注意,我們要繼承的是 jwt 的 BaseMiddleware
class RefreshToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     *
     * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 檢查此次請求中是否帶有 token,如果沒有則丟擲異常。 
        $this->checkForToken($request);

       // 使用 try 包裹,以捕捉 token 過期所丟擲的 TokenExpiredException  異常
        try {
            // 檢測使用者的登入狀態,如果正常則通過
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            throw new UnauthorizedHttpException('jwt-auth', '未登入');
        } catch (TokenExpiredException $exception) {
          // 此處捕獲到了 token 過期所丟擲的 TokenExpiredException 異常,我們在這裡需要做的是重新整理該使用者的 token 並將它新增到響應頭中
            try {
                // 重新整理使用者的 token
                $token = $this->auth->refresh();
               // 使用一次性登入以保證此次請求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
               // 如果捕獲到此異常,即代表 refresh 也過期了,使用者無法重新整理令牌,需要重新登入。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }
        
        // 在響應頭中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
    }
}複製程式碼

更新異常處理的 Handler

由於我們構建的是 api 服務,所以我們需要更新一下 app/Exceptions/Handler.php 中的 render

方法,自定義處理一些異常。

Handler.php

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class Handler extends ExceptionHandler
{
    ...

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Exception $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        // 引數驗證錯誤的異常,我們需要返回 400 的 http code 和一句錯誤資訊
        if ($exception instanceof ValidationException) {
            return response(['error' => array_first(array_collapse($exception->errors()))], 400);
        }
        // 使用者認證的異常,我們需要返回 401 的 http code 和錯誤資訊
        if ($exception instanceof UnauthorizedHttpException) {
            return response($exception->getMessage(), 401);
        }

        return parent::render($request, $exception);
    }
}複製程式碼

至此,laravel的相關結合與學習暫告一段落!


相關文章