【eggjs】记录跨域问题(部署/跨域/cookie/samesite/cors)

1,218 阅读2分钟

背景: 中后台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 包括originAccess-Control-Request-MethodAccess-Control-Request-Headers

预检请求的response header 必须包括: Access-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Credentials

[2]credentials

前台请求需要设置credentials,以axios为例:

withCredentials indicates 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-Origin header, 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/…

3.chromestatus.com/feature/508…

4.www.eggjs.org/zh-CN/core/…