昨天臨下班的時候發現一個問題,公司系統在操作過程中老退出登入。
專案前端是 angular 後端 laravel,認證用的是 JWT,登入失效問題一定出在 token 上,所以先去檢查後端驗證程式碼。
try {
// 檢查此次請求中是否帶有 token,如果沒有則丟擲異常。
$this->checkForToken($request);
// 檢測使用者的登入狀態,如果正常則通過
if ($this->auth->parseToken()->authenticate()) {
return $next($request);
}
throw new UnauthorizedHttpException('jwt-auth', '未登入');
} catch (TokenExpiredException $exception) {
try {
\Auth::guard('api')->onceUsingId(
$this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']
);
$token = $this->auth->refresh();
return $this->setAuthenticationHeader($next($request), $token);
} catch (JWTException $exception) {
/****************************************
* 如果捕獲到此異常,即代表 refresh 也過期了,
* 使用者無法重新整理令牌,需要重新登入。
****************************************/
return $this->response->withUnauthorized()->fail(1, $exception->getMessage());
}
}
看起來沒有什麼太大的問題,把 .env 中 JWT_TTL
改成 1 分鐘後除錯,發現介面能夠正常返回 token
那麼繼續去檢查前端的程式碼,前端是通過 angular 的攔截器處理的token,當發現 response header 中有帶有 token 則用新的 token 替代舊的。
// 如果返回的資訊裡帶有重新整理的token則更換現有token
if (event.headers.has('Authorization')) {
const userToken = event.headers.get('Authorization');
this._login.refreshToken(userToken);
}
/**
* 重新整理token,在當前token過期時後臺允許使用該token換取一個新的token以繼續使用,保證使用者的連續登入
*
* @param {string} newToken
* @memberof AuthService
*/
public refreshToken(newToken: string): void {
const [tokenType, token] = newToken.split(' ');
this._cookies.put('appToken', token);
this._cookies.put('tokenType', tokenType);
}
查到這裡的時候發現在瀏覽器裡明明返回的響應中有 Authorization
這個 header 但程式碼中就是讀取不到。
糾結了一個晚上,最後查 MDN 時發現跨域中需要帶上 Access-Control-Expose-Headers
, 才能讓客戶端訪問到服務端返回的 header 。
MDN 中是這麼描述的:
The Access-Control-Expose-Headers response header indicates which headers can be exposed as part of the response by listing their names.
By default, only the 6 simple response headers are exposed:
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
If you want clients to be able to access other headers, you have to list them using the Access-Control-Expose-Headers header.
這就很尷尬,最終解決是如果使用 barryvdh/laravel-cors 這個包的話,只需要在 cors.php
配置檔案中把前端需要讀取的 header 暴露出來就可以了。
return [
/*
|--------------------------------------------------------------------------
| Laravel CORS
|--------------------------------------------------------------------------
|
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
| to accept any value.
|
*/
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'],
'exposedHeaders' => ['Authorization'],
'maxAge' => 0,
];
還是書讀得太少。:kissing:
本作品採用《CC 協議》,轉載必須註明作者和本文連結