cors深度解析

3 篇文章 0 订阅
2 篇文章 0 订阅

什么是cors?

cors,中文是跨域资源共享,是http头的协议机制,用来解决浏览器跨域问题。

什么是跨域?

老生常谈的问题,不过多赘述了,需要注意的一个点是在跨域的情况下,请求是可以被发送到服务端的,并不是没有发出去,而且服务端也是可以接受到请求头或者请求体,正常处理这个请求,只是前端拿不到response,不要误以为是前端请求没有发出去,这点也经常被用来做csrf攻击。

cors涉及相关的请求响应头都有哪些?分别是什么?

请求头描述
Origin网站请求的URL,无论跨域总默认被发送
Access-Control-Request-Method网站发送请求的http方法
Access-Control-Request-Headers主要用于预检请求,告诉服务器下次真正的请求要带哪些头
响应头描述
Access-Control-Allow-Origin跨域情况下允许响应的请求源origin,如果为*默认所有源
Access-Control-Expose-Headers指定 XMLHttpRequest的getResponseHeader 可以访问的响应头,默认情况下getResponseHeader只能访问到一些基本的请求头,该响应头可以让前端获取到额外的相应头
access-control-max-age指定预检请求被缓存的时长,默认情况下每次都发送预检请求,如果在被缓存的时间内,那么就不额外发预检请求
Access-Control-Allow-Credentials表示是否允许客户端发送cookie等凭证信息,默认情况下cors不带cookie,这个字段只能为true,不要删除即可
Access-Control-Allow-Methods指明客户端请求允许使用的http方法
Access-Control-Allow-Headers指明实际请求中客户端需要带的头部字段

如何使用cors,服务端应该怎么做,前端应该怎么做?(Access-Control-Allow-Origin)
前端请求

fetch('http://127.0.0.1:8081/cors') 
  .then(response => response.json())
  .then(data => console.log(data));

服务端响应

var http = require('http');
http.createServer(function (request, response) {
  const reqUrl = request.url
  if(reqUrl==='/cors'){
    console.log('我收到cors的路径请求了',`请求域名:${request.headers.origin}`)
    response.writeHead(200, {
      'Content-Type': "application/json",
      'Access-Control-Allow-Origin': request.headers.origin || '*'
    });
    response.end(JSON.stringify({ status:200,message:'请求成功' }))
  }
  response.writeHead(200, {
    'Content-Type': 'text/plain',
  });
  response.end('Hello World');
}).listen(8081);

console.log('Server running at http://127.0.0.1:8081/');

如果不添加相应头’Access-Control-Allow-Origin’那么请求被发送到服务端,客户端拿不到response,表现是这样
在这里插入图片描述
加上请求头就是正常请求的样子了,当然这只是简单请求。cors又分为简单请求和非简单请求。

简单请求与非简单请求满足条件?
简单请求的条件如下反之则是非简单请求

1、使用下列 HTTP 方法之一

  • GET
  • HEAD
  • POST,并且Content-Type的值在下列之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

2、请求头中只有下面这些

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (需要注意额外的限制)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

预检请求?

预检请求(preflight request),是一个跨域请求,用来校验当前跨域请求能否被理解。
不满足以上条件的需要额外的发送一次预检请求,请求方式是以options的方式被发出去,而浏览器则以请求方式和请求头来判断是否发送预检请求。前端来发送一个预检请求不符合以上条件即可,设置请求头’Content-Type’: ‘application/json’,浏览器会额外的发送一次options的请求,该请求的头默认包含以下内容
在这里插入图片描述Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST

而这里预检请求的作用就是告诉服务器我下次真实的请求会以post请求,request header中有content-type的请求,咨询服务端是否允许,在服务端没有做任何处理的情况下,请求是失败的。

在这里插入图片描述
并且控制台也告诉了我们原因:服务端在预检请求中没有告诉客户端的请求协议头允许带content-type,服务端需要设置Access-Control-Allow-Headers为content-type来告诉浏览器允许。

response.writeHead(200, {
      'Content-Type': "application/json",
      'Access-Control-Allow-Origin': request.headers.origin || '*',
      'Access-Control-Allow-Headers':'Content-Type'
});

也就说浏览器一共会发送两次请求,一次预检一次真正的请求。

两次请求,性能怎么被保证?

这样就会存在性能问题,每次都会发送两次请求不管是对网络通信还是服务端逻辑处理都会有开销的,两种解决方式:
1.全部使用简单请求,这样显然是不合理的定制性太低,很多场景不符合。
2.Access-Control-Max-Age,使用该响应头来缓存预检请求,在规定时间内不发送额外的请求,合理!

携带鉴权信息?
大部分场景都需要向服务端发送cookie或者token等鉴权信息,来告诉服务端做一些身份信息验证,然后跨域是不携带cookie信息的,
如果设置只是前端设置发送跨域请求fetch credentials:'include’的话是不会被允许的,报错信息如下

Access to fetch at 'http://127.0.0.1:8081/cors' from origin 'http://127.0.0.1:8082' 
has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Credentials' header in the response is '' which must 
be 'true' when the request's credentials mode is 'include'.

报错信息很明白: 当请求中包含凭证信息时,需要设置响应头 Access-Control-Allow-Credentials,是否带凭证信息是由 XMLHttpRequest的withCredentials 属性控制的,或者fetch。
前端

function send(){
  fetch('http://127.0.0.1:8081/cors',{
    method:'POST',
    body:JSON.stringify({ message:'预检请求测试' }),
    headers:{
      'Content-Type': 'application/json'
    },
    credentials:'include'
  })  
  .then(response => response.json())
  .then(data => console.log(data));
}

服务端

var http = require('http');
http.createServer(function (request, response) {
  const reqUrl = request.url
  if(reqUrl==='/cors'){
    console.log('我收到cors的路径请求了',`请求域名:${request.headers.origin}`)
    response.writeHead(200, {
      'Content-Type': "application/json",
      'Access-Control-Allow-Origin': request.headers.origin || '*',
      'Access-Control-Allow-Headers':'Content-Type',
      'Access-Control-Max-Age':600,
      'Set-Cookie':'name=yangbo',
      "Access-Control-Allow-Credentials":"true"
    });
    response.end(JSON.stringify({ status:200,message:'请求成功' }))
  }
  response.writeHead(200, {
    'Content-Type': 'text/plain',
  });
  response.end('Hello World');
}).listen(8081);

console.log('Server running at http://127.0.0.1:8081/');
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值