背景: 中后台bff项目和前台web项目,有时候不是部署在同一个域名下的。这样就会造成跨域问题,eggjs框架本身是提供cors插件来解决的,但是在具体使用中还是碰到了很多问题。
需求: 1.实现跨域 2.跨域设置cookie
步骤:
1.eggjs官方提供了一个egg-cors内置插件,直接引入使用。
在config/plugin.ts中打开插件
cors: {
enable: true,
package: 'egg-cors',
}
2.在config/config.${ 你的环境 }.ts中添加配置信息。 注意,origin有需要可以手动改写为自定义域名,在这里只是提供一个处理思路。origin不可以为*。关于credentials的作用,参考扩展说明。
cors: {
origin: ctx => {
return ctx.request.header.origin || 'https://xxx.xxx.com'
},
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
},
3.编写一个自定义中间件,核心内容只是对返回头做一下简单处理。
新建app/middleware/crossOrigin.ts文件,书写代码:
import { Context, Application, EggAppConfig } from 'egg';
export default function crossOrigin(
options: EggAppConfig['crossOrigin'],
app: Application,
): any {
return async (ctx: Context, next: () => Promise<any>) => {
try {
ctx.response.set('Access-Control-Allow-Headers', 'content-type');
//ctx.response.set('Access-Control-Allow-Origin', 'http://xxx.xxx.com');
ctx.response.set('Access-Control-Allow-Credentials', 'true');
ctx.response.set('Access-Control-Allow-Methods', 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS');
await next();
} catch (e) {
const err = e as any;
ctx.app.emit('error', err, ctx);
}
};
}
Access-Control-Allow-Headers包含请求头中的允许的信息,这里以content-type举例。
Access-Control-Allow-Origin 因为egg-cors已经处理了,就不用再处理一遍。
Access-Control-Allow-Methods 是返回允许的请求方法,一般Options请求会在请求头中带上method,需要匹配才行。这里比较奇怪的是,cors应该已经配置了,但是没有返回这个header,只好先手动加一下了。
4.最后在config打开自定义的middleware ,在eggjs的设置cookie时候,添加samesite参数,举个例子:
ctx.cookies.set("tk", token, {
sameSite: "none"
});
5.前台请求的时候别忘了携带credentials。
扩展说明
[1]cors
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。"预检"请求用的请求方法是
OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
预检请求的request header 包括origin,Access-Control-Request-Method,Access-Control-Request-Headers
预检请求的response header 必须包括: Access-Control-Allow-Methods,Access-Control-Allow-Headers ,Access-Control-Allow-Credentials
[2]credentials
前台请求需要设置credentials,以axios为例:
withCredentialsindicates whether or not cross-site Access-Control requests should be made using credentials
withCredentials: true
根据MDN文档的说明,带credentials的请求,Access-Control-Allow-Origin 必须是一个确定的origin , 不可以为 *
When responding to a credentialed requests request, the server must specify an origin in the value of the
Access-Control-Allow-Originheader, instead of specifying the "*" wildcard.
[3]samesite
谷歌Chrome新版本的Cookie默认开启SameSite=Lax 即禁止第三方 Cookie,需要设置SameSite=None
参考资料:
1.www.ruanyifeng.com/blog/2016/0…
2.developer.mozilla.org/en-US/docs/…