繼上一篇 使用JWT 製作多登入系統 API ,前端經過 API 登入後會得到一個 Token,但避免使用者忘記登出導致 Token 被利用,一般都會將該 Token 設定一個短時效的過期時間,可是 Token 時間設定太短,又容易造成使用者必須頻繁登入來重新取得新的 Token,所以必須要讓使用 API 的使用者能夠自動去更新至換新的 Token。
建立一個 Middleware
php artisan make:middleware JwtRefreshToken
修改 app\Http\Kernel.php
protected $routeMiddleware = [
....
//檢查 JWT Token 及 自動更新 Token
'refresh.token' => \App\Http\Middleware\JwtRefreshToken::class,
];
編輯 JwtRefreshToken
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException as TokenInvalidException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException as TokenExpiredException;
class JwtRefreshToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next){
try {
//檢查是否有使用者通過驗證
$user = JWTAuth::parseToken()->authenticate();
// //Token TTL 到期時間
// $expireTime = JWTAuth::payload()['exp'];
// // 如果 Token 到期時間小於5分鐘,重新產生一個新的 Token 並附帶在 header 送出,讓前端重新抓取.
// if (($expireTime - time()) < 5*60 && ($expireTime - time()) > 0) {
// $refreshed = JWTAuth::refresh(JWTAuth::getToken());
// $user = JWTAuth::setToken($refreshed)->toUser();
// $request->headers->set('Authorization', 'Bearer '.$refreshed);
// // 輸出到BODY測試
// return response()->json([
// 'code' => 200,
// 'user' => $user,
// 'access_token' => $refreshed,
// 'token_type' => 'bearer',
// 'expires_in' => JWTAuth::factory()->getTTL(),
// ]);
// }
} catch (Exception $e) {
//檢查 Token 是否無效
if ($e instanceof TokenInvalidException){
$status = 401;
$message = 'This token is invalid. Please Login';
return response()->json(compact('status','message'),401);
//檢查 Token 是否到期
}else if ($e instanceof TokenExpiredException){
try
{
// 如果 Token 到期,重新產生一個新的 Token 並附帶在 header 送出,讓前端重新抓取.
$refreshed = JWTAuth::refresh(JWTAuth::getToken());
$user = JWTAuth::setToken($refreshed)->toUser();
$request->headers->set('Authorization','Bearer '.$refreshed);
// 輸出到BODY測試
// return response()->json([
// 'code' => 200,
// 'user' => $user,
// 'access_token' => $refreshed,
// 'token_type' => 'bearer',
// 'expires_in' => JWTAuth::factory()->getTTL(),
// ]);
}catch (JWTException $e){
// Token 無法被 refresh
return response()->json([
'code' => 103,
'message' => 'Token cannot be refreshed, please Login again'
]);
}
}else{
//找不到 Token.
$message = 'Authorization Token not found';
return response()->json(compact('message'), 404);
}
}
return $next($request);
}
}
修改 API 的 Controller
public function __construct()
{
//除了 login 其餘 function 都要經過 api 及 refresh.token 的 middleware 檢查
$this->middleware(['admapi','refresh.token'], ['except' => ['login']]);
}
測試
未提供 Token 時讀取資料
登入得到 Token 後可以讀取資料
Token 被更新成另一份新的
Token 被取消將無法被更新
偷改造假 Token 被判定無效