1. 程式人生 > >laravel使用jwt和refreshToken中介軟體

laravel使用jwt和refreshToken中介軟體

 

專案根目錄命令列執行

1. 使用 composer 安裝

 composer require tymon/jwt-auth 1.*@rc

 

2 釋出配置檔案

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

3 生成加密金鑰

# 這條命令會在 .env 檔案下生成一個加密金鑰,如:JWT_SECRET=foobar

php artisan jwt:secret

如果你使用預設的 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.
     *
     * @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 [];
    }
}

 

2.4 註冊兩個 Facade

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

config/app.php

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

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', '[email protected]');
    Route::post('logout', '[email protected]');
    Route::post('refresh', '[email protected]');
    Route::post('me', '[email protected]');

});

 

2.7 建立 token 控制器

php artisan make:controller AuthController

AuthController

 

值得注意的是 Laravel 這要用 auth('api') ,至於為什麼,我另一篇關於 JWT 擴充套件詳解的文章裡有講。

<?php

namespace App\Http\Controllers;

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

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     * 要求附帶email和password(資料來源users表)
     * 
     * @return void
     */
    public function __construct()
    {
        // 這裡額外注意了:官方文件樣例中只除外了『login』
        // 這樣的結果是,token 只能在有效期以內進行重新整理,過期無法重新整理
        // 如果把 refresh 也放進去,token 即使過期但仍在重新整理期以內也可重新整理
        // 不過重新整理一次作廢
        $this->middleware('auth:api', ['except' => ['login']]);
        // 另外關於上面的中介軟體,官方文件寫的是『auth:api』
        // 但是我推薦用 『jwt.auth』,效果是一樣的,但是有更加豐富的報錯資訊返回
    }

    /**
     * Get a JWT via given credentials.
     *
     * @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.
     *
     * @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
        ]);
    }
}

 

 生成refreshToken中介軟體

php artisan meke:middleware RefreshToken

中介軟體內容

<?php

namespace App\Http\Middleware;

use Closure;
use Auth;
use JWTAuth;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;

class RefreshToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 使用 try 包裹,以捕捉 token 過期所丟擲的 TokenExpiredException  異常
        try {
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            return response()->json(['code'=>402,'data'=>[],'msg'=>'未登入']);
        } catch (TokenExpiredException $exception) {
            // 此處捕獲到了 token 過期所丟擲的 TokenExpiredException 異常,我們在這裡需要做的是重新整理該使用者的 token 並將它新增到響應頭中
            try {
                // 重新整理使用者的 token
                $token = $this->auth->refresh();
                // 使用一次性登入以保證此次請求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
                auth()->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);

            } catch (JWTException $exception) {
                // 如果捕獲到此異常,即代表 refresh 也過期了,使用者無法重新整理令牌,需要重新登入。
                //throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
                return response()->json(['code'=>402,'data'=>[],'msg'=>$exception->getMessage()]);
            }
        }

        // 在響應頭中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
    }
}
註冊路由中介軟體

 

 修改app/exceptions/handle.php中的handle方法

 轉載自https://laravel-china.org/articles/10885/full-use-of-jwt#e1eb59