CORS跨域

92 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

CORS(Cross-Origin Resource Sharing,跨域资源共享)主要是定义了当我们必须要访问跨域资源的时候,浏览器与服务器应该如何进行沟通。CORS背后的基本思想,主要就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是失败。下面我们来看两种请求的跨域实现:

简单请求的跨域实现:

我们所说的简单请求就是满足下面两个条件的请求:

  1. 请求方法是HEAD、GET、POST三种方法之一
  2. HTTP的头信息不超出以下几种字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:其中Content-Type只限于后面三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

符合上述两个条件的请求,均为简单请求。当发送简单请求时,服务端只需要增加一个Origin头部(Access-Control-Allow-Origin),就可以开启CORS。

Access-Control-Allow-Origin这个属性表示有哪些域名可以访问该资源,如果该属性设置成通配符则表示所有域名都可以访问该资源。如果没有这个头部,或有这个头部但来源信息不匹配,那么浏览器就会驳回请求。

let whitList = ['http://localhost:3000']
app.use(function (req,res,next) {
  let origin = req.headers.origin;
  if(whitList.includes(origin)){
    // 设置哪个源可以访问我
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  next();
});

上述例子中,我们将可以访问的域名加入一个白名单列表,然后判断如果访问源在白名单里,则将Access-Control-Allow-Origin的值设置成该访问源,这样就不会跨域了。

复杂请求的跨域实现

除了上述的简单请求以外,其余的均为复杂请求。复杂请求在CORS请求的时候,会增加一次预检请求(options请求),通过options请求就可以知道服务端是否允许我们跨域。发送复杂请求的时候,服务端一般会设置以下几种头部信息:

  • Access-Control-Allow-Origin: 与简单请求相同
  • Access-Control-Allow-Methods: 允许的方法
  • Access-Control-Allow-Headers: 允许的头部
  • Access-Control-Max-Age: 预检请求的存活时间
  • Access-Control-Allow-Credentials: true

在默认情况下,跨源请求不会提供凭据(cookie、HTTP认证等),我们通过将withCredentials属性设置为true,可以指定某个请求发送凭据,这时候服务器接收带凭据的请求,会设置Access-Control-Allow-Credentials: true这个http头部信息来进行响应。

下面我们来看一个复杂请求的设置例子:

app.use(function (req,res,next) {
  let origin = ‘http://localhost:3000;
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Access-Control-Allow-Headers','name');
  res.setHeader('Access-Control-Allow-Methods','PUT');
  res.setHeader('Access-Control-Max-Age',6);
  res.setHeader('Access-Control-Allow-Credentials', true);
  if(req.method === 'OPTIONS'){
    res.end();
  }
  next();
});

上述的例子中,服务端设置了可以访问的源localhost:3000,访问时可以携带的头name,请求方法put,预检请求的时间以及允许凭证这些选项。在我们前端(localhost:3000下)发送请求时,使用put方法进行调用,设置withCredentials为true即可携带cookie凭证,然后也可以设置请求头name,这样基本就实现了复杂请求的跨域。