簡介配置 Laravel 5.6 + JWT + AngularJS 配置 Api 跨域授權訪問,以及實現 Token 重新整理。
因內容較多,涉及較廣,這裡只能概括的講一下了。
安裝配置
JWT
詳細配置過程,請檢視官方文件。
- 安裝 tymondesigns/jwt-auth
composer require tymon/jwt-auth "1.0.0-rc.2"
- 生成key
php artisan jwt:secret
- User 實現 JWTSubject 介面
- 修改 auth.php 配置
'api' => [ 'driver' => 'jwt', 'provider' => 'users', ],
-
一個【授權 + 重新整理】 的中介軟體
新增到app/Http/Kernel.php
protected $routeMiddleware = [ ... 'refresh.token' => \App\Http\Middleware\RefreshToken::class ... ];
RefreshToken 中介軟體
class RefreshToken extends BaseMiddleware { public function handle($request, Closure $next) { // 檢查此次請求中是否帶有 token,如果沒有則丟擲異常。 $this->checkForToken($request); Log::debug($request->headers->all()); // 使用 try 包裹,以捕捉 token 過期所丟擲的 TokenExpiredException 異常 try { // 檢測使用者的登入狀態,如果正常則透過 if ($this->auth->parseToken()->authenticate()) { return $next($request); } $token = $this->auth->refresh(); // 使用一次性登入以保證此次請求的成功 } 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); } }
- 幾個關鍵的配置
- JWT_TTL Token的有效時間,單位:分,建議設定 20 min ~ 1 h。
- JWT_REFRESH_TTL=1440 重新整理後的 Token 有效時間,單位:分,建議設定在 7 ~ 30 day。
- JWT_BLACKLIST_GRACE_PERIOD 防止併發請求導致某些請求失效,單位:秒,建議設定 10 ~ 60 s。
跨域
詳細配置過程,請檢視官方文件。
- 安裝 barryvdh/laravel-cors
- 配置 cors
在配置檔案 cors.php 中,設定exposedHeaders
,不然無法跨域獲取到 header 中的 Authorization 值來重新整理前端令牌。'exposedHeaders' => ['Authorization'],
後端 Laravel
登入
-
登入 Api 介面
驗證賬號密碼,返回 Token,我這裡使用的 username 欄位。
Api返回,自己找一個返回success、error、message 的 Trait 工具就可以了,沒必要用 dingo。class LoginController extends ApiController { use AuthenticatesUsers; public function login(LoginRequest $request) { $credentials = $this->credentials($request); if ($token = auth('api')->attempt($credentials)) { $user = auth('api')->user(); return $this->success( [ 'user' => $user, 'token' => $token, ]); } return $this->failed('賬號或密碼錯誤!'); } public function username() { return 'username'; } }
- 路由
Api 介面都要使用 api 中介軟體,控制器最好放在 \Api 名稱空間下。Route::middleware('api')->prefix('api/user')->namespace('Modules\Account\Http\Controllers\Api')->group(function () { Route::post('login', 'LoginController@login')->name('user.login'); });
- 給需要授權的介面新增中介軟體,比如獲取使用者列表 getList
public function __construct(UserRepository $repository) { $this->middleware('refresh.token')->only([ 'getList', 'get', 'update' ]); $this->repository = $repository; }
前端 Angular
Token
在登入授權時,將返回的 token 儲存在 TokenService 中進行管理。
this.tokenService.set({
token: res.data.token
});
Interceptor
攔截器將每一個請求的頭中放入 Authorization 資訊。
setReq(req: HttpRequest<any>, options: DelonAuthConfig): HttpRequest<any> {
return req.clone({
setHeaders: {
Authorization: `Bearer ${this.model.token}`,
},
});
}
Refresh Token
如果返回的 Response 的 Header 中有 Authorization 資訊,說明請求中帶的 Token 已經過期,伺服器將 重新整理的 Token 放在了 Response 頭中。前端獲取後更新 Token 。
let newAuth = event.headers.get('Authorization');
if (newAuth) {
let token = newAuth.slice(7);
this.tokenService.set(Object.assign(this.tokenService.get(), {
token: token,
}));
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結