Try   HackMD

Laravel 與 JWT 搭配運用

Official Documentation

https://jwt-auth.readthedocs.io/en/develop/

JWT ?

在Laravel中,web端預設是利用session識別登入者身分合法與否,而API則可利用「JWT (Json Web Tokens)」進行身分驗證,它本身是個JSON格式的輕量級規範,有別於SAML (Security Assertion Markup Language) 較為複雜的XML格式。

API Access Token ?

即造訪後端的憑證,首先使用者會在前端進行登入的動作,若身分核對成功後,就會發一組 Token 給前端,此時就可以利用Bearer作為驗證方案,將這組憑證夾帶在Header,造訪需要權限的API,讓後端知道你的身分是合法的。一般而言 Token 是具有時效性的,以 JWT 為例,時效預設為60分鐘,時效一過或者自行登出,則該 Token 失效,必須重新取得 Token。

Practice

利用composer引入jwt-auth:

composer require tymon/jwt-auth:dev-develop

config\app.php 於 「providers」新增 (Laravel 5.4 or below):

'providers' => [
    ...

    /*
     * Package Service Providers...
     */

    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],

...

依據底層套件,發行 jwt.php 設定檔 (位於config資料夾):

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

在「.env」產生JWT憑證,若憑證為空值,則被視為不合法身分

php artisan jwt:secret

User model 引入 JWTSubject,宣告User model為JWTSubject介面,並加入下列2個方法:

<?php

namespace App\Entities;

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

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;
    
    ... (略)
        
        
    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

修改 config\auth.php :

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

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

    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

routes\api.php創建路由:

Route::group(['prefix' => 'auth'], function () {
    Route::get('/', 'AuthController@me');
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
});

創建AuthController:

php artisan make:controller AuthController

app\Http\Controllers\AuthController.php

<?php

namespace App\Http\Controllers;

class AuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api')->except('login');
    }

    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['status' => 1, 'message' => 'invalid credentials'], 401);
        }

        return response()->json(['status' => 0, 'token' => $token]);
    }

    public function me()
    {
        return response()->json(auth()->user());
    }

    public function logout()
    {
        auth()->logout();
        return response()->json(['status' => 0]);
    }
}

Running with postman

API參數對照表

URI description HTTP Method Header
api/auth 取得user資訊 GET Authorization: bearer Token
Accept: application/json
api/auth/login 登入 POST
api/auth/logout 登出 POST Authorization: bearer Token
Accept: application/json

打開postman後,使用「取得user資訊」的API,若不夾帶token、token失效或者使用非法token,就會收到如下圖的回應,且HTTP Status Code 為401

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

為何我們會收到401的狀態碼呢?因為這支 API 被宣告使用「auth:api」的中介層,倘若你身分是不合法的,就會被中介層拒於門外。這時我們就透過「登入」,取得合法的Token,並再次取得user資訊,結果就會如下

就這樣,利用 JWT 進行 API 身分驗證的機制就完成了