因为跨域问题导致的无法读取 response header
昨天临下班的时候发现一个问题,公司系统在操作过程中老退出登录。
项目前端是 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 协议》,转载必须注明作者和本文链接
推荐文章: