0
点赞
收藏
分享

微信扫一扫

网络-HTTP请求跨域访问控制


本文根据MDN整理

我不说什么是跨域,网上很多,但是要说一下,跨域访问控制这个概念,是浏览器的行为,不是服务器的行为(虽然不少工作都是后端的工程师去配置),产生跨域访问控制的原因有两个:
1.前端发出跨域请求,浏览器发现是跨域请求,拦截该请求
2.前端发出跨域请求,跨域请求正确发送出去,当服务器返回数据给浏览器的时候,浏览器发现是跨域请求,则将服务器返回的结果拦截,此时,前端工程师应该会发现not found response的相关错误信息
综上所述,跨域访问控制是浏览器的行为

随着越来越多的前后端分离项目兴起,所以HTTP新增了一组适用于跨域的头部字段,以及一个叫做OPTION的交互机制,规范要求,除了简单请求,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。

承接上个自然段,符合下面3个条件的HTTP请求叫做简单请求,它们不会产生跨域访问控制(CORS)
条件1. 请求方法必须是下面三个中的一个
GET,HEAD,POST
条件2. 请求头中的每一个字段必须是下面9个中的一个
Accept
Accept-Language
Content-Language
Content-Type
DPR
Downlink
Save-Data
Viewport-Width
Width
条件3. 若请求头中包含Content-Type,则Content-Type属性必须是下面3个中的一个
text/plain
multipart/form-data
application/x-www-form-urlencoded

下面是一个最简单的访问控制请求头,这个请求会出现跨域,因为Origin不满足条件2
对于request

Origin:https://shiwentian.cn

对于response

// 此处注意这个星号,对于Credentials特性,此处星号有说道,一定要注意
Access-Control-Allow-Origin:*

上面的response表示当前request请求的资源可以被任意外域访问,而不只是https://shiwentian.cn,如果response中的返回值是下面这样

Access-Control-Allow-Origin:aa.com

则表示该资源只允许域名是aa.com的请求访问

通过Origin与Access-Control-Allow-Origin,完成了最简单的访问控制

预检请求(preflight request)
我个人理解"预检"的意思就是,预先检查当前请求中,不属于上文描述的简单请求的那一部分属性,是否被服务器支持,比如下面的例子中,Abc这个属性就是不属于简单请求,并且,GLJ属性也不属于简单请求,所以预检请求会询问服务器,是否支持Abc,以及Content-Type中的GLJ属性,request如下

GET /resources/public-data/ HTTP/1.1
Abc:MYS
Content-Type:GLJ
Origin: http://shiwentian.cn

虽然该请求是GET请求,符合简单请求中的条件1,但是由于header中有一个我自定义的属性Abc,不符合简单请求中的条件2,而且Content-Type中的GLJ也不符合简单请求中的条件3,所以该请求发起之前,浏览器会先发送一个预检请求,浏览器发送的预检请求如下

GET /resources/public-data/ HTTP/1.1
Abc:MYS
Origin: http://shiwentian.cn
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Abc, Content-Type

该预检请求询问服务器:“我这个头部有Abc的属性,并且媒体类型是Content-Type似乎有些特殊,你告诉我你支持什么类型,然后我这边判断一下,看看是否支持这两个类型”
此时服务器收到浏览器的问询,则服务器返回的response格式如下

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://shiwentian.cn
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Abc, Content-Type
Access-Control-Max-Age: 86400
Content-Type: text/plain

这个返回的意思一看就明白了,表示服务器告诉浏览器"我目前支持POST, GET, OPTIONS方法,关于自定义的请求头目前支持Abc,关于自定义的媒体类型支持text/plain,我这个回答有效期是86400秒,也就是24小时,你24小时之内发送跨域请求的时候不要在问我了,好烦…"

此时,浏览器发现Content-Type:GLJ不等于Content-Type: text/plain,则会从控制台告知前端开发人员,出现跨域问题,

文章至此,大部分关于访问控制的内容已经描述完毕,下面来说访问控制的另外一个我个人认为已经过时的特性-附带身份凭证

附带身份凭证(Credentials)
之所以我说Credentials过时,是因为它基于cookie,因为cookie过时,下面是使用XMLHttpRequest对象向shiwentian.cn发送一个GET实际请求(注意不是OPTIONS预检请求

var invocation = new XMLHttpRequest();
var url = 'http://shiwentian.cn';

function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;//注意这里
invocation.onreadystatechange = handler;
invocation.send();
}
}

上述代码实际生成的request如下

GET /shiwentian.cn/ HTTP/1.1
Origin: http://shiwentian.cn
Cookie: name1=mys

注意withCredentials=true,表示发送请求的时候,附带cookie,此时,如果服务器端的响应response中未携带头部消息 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者,所以服务端的response返回格式应该如下,这样浏览器才不会拦截response

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://shiwentian.cn //注意这里不是星号
Access-Control-Allow-Credentials: true //response中必须带这个

下面注意,注意,注意
对于附带身份凭证的请求(Credentials),服务器不允许设置 Access-Control-Allow-Origin 的值为星号"*",这是因为请求的首部中携带了 Cookie 信息,如果设置成星号,请求将会失败

综上所述,我个人强烈不要使用cookie这种过时的http头部属性



举报

相关推荐

0 条评论