当前位置: 首页 > Web前端 > HTML

掌握 CORS 跨域请求,读这一篇文章就够了

时间:2023-03-28 10:45:41 HTML

看这篇文章就可以掌握CORS跨域请求了,出于安全考虑,浏览器限制JavaScript(简称JS)脚本发起跨域HTTP请求,同源则没有这样的限制。前端解决跨域的方案有很多,比如WebSocket协议跨域、JSONP请求跨域、跨域资源共享CORS等。一、CORS简介CORS的全称是Cross-OriginResourceSharing,译为跨域资源共享,简称跨域访问,是W3C制定的标准协议。它由一系列传输的HTTPheaders(headerfields)组成,浏览器会根据这些HTTPheader决定是否阻止前端JS代码获取跨域请求的资源。CORS的主要作用是消除各种API的同源限制,从而实现不同源(服务器)之间的资源共享,保证跨域数据传输的安全。CORS请求并不是一种特殊的HTTP请求,它也是基于HTTP通信协议的。CORS请求默认携带一个“origin”头,用于指示对目标网站的请求来源。origin字段由三部分组成:protocol、host和port,下面三种语法都是正确的。origin:nullorigin:://origin:://:2.查询浏览器兼容性。建议查询浏览器功能、兼容性以及哪个版本兼容本站。比如查询各个浏览器对CORS的支持情况,访问URL地址https://caniuse.com/?search=CORS。如下图所示:3.同源和异源的定义和示例同源策略是Netscape提出的一个著名的安全策略,是一种安全约定。目前,所有支持JS的浏览器都采用这种策略。Ajax是当代Web应用程序获取服务器数据的核心技术。可以实现网页内容的异步更新。Ajax底层的XMLHttpRequest对象和FetchAPI遵循同源策略。同源策略也是浏览器的基本安全特性之一。同源定义:当两个URL使用的协议、域名(主机)和端口相同时,称这两个URL同源,否则称这两个URL异源。下表总结了同源和异源的URL。示例说明:URLAURLB结果分析原因https://www.example.com/a/https://www.example.com/b/同源域名相同,只是路径不同https://www.example.com/a/https://www.example.com/a/c/同源域名相同,只是路径不同http://www.example.comhttp://万维网。example.com:80通源80为HTTP协议默认端口https://www.example.comhttps://www.example.com:443通源443为HTTPS协议默认端口https://www.example.comhttps://www.example.comhttps://www.example.comhttps://www.example.comhttps://www.example.comhttps://www.example.com//www.example.com不同源域名相同,协议不同https://www.example.comhttps://www.example.com:81不同源域名相同,端口不同https://www.example.comhttps://www.example.comhttps://Tool.example.com主域名相同,来源不同,二级域名不同https://www.example.comhttps://example.com主域名相同名称与来源不同,但子域名不同https://www.example.comhttps://39.105.183.157来源域名和IP不同https://www.example.comhttps://tool.box3.cn不同来源完全不同的域名http://www.example.comhttp://localhost不同来源完全不同的域名4.常见的CORS访问控制场景本例中Nginx服务器开启了HTTP/2协议,所以在HTTP/之前HTTP头名称必须转为小写2二进制编码。如果请求头和响应头中包含大写的字段名,则认为是格式错误。重点知识点:如果CORS跨域请求是GET、POST、HEAD三种方法之一,则无需在HTTP响应头中指定access-control-allow-methods字段的值。4.1简单请求什么是简单请求?只有满足以下所有条件,才会考虑“简单请求”。请注意,对于“简单请求”,浏览器不会启动CORS预检请求。1、HTTP请求方式为以下三种之一:GETPOSTHEAD2,浏览器自动添加的头部字段(例如:connection、user-agent、date、referer等)和定义的禁止头部字段除外获取规范,以及“proxy-”和“sec-”小写头字段。允许设置的header字段集合为:acceptaccept-languagecontent-languagecontent-type(见下3)3.content-type的值为以下三者之一:text/plainmultipart/form-dataapplication/x-www-form-urlencoded4,请求中的XMLHttpRequest对象都没有注册任何事件侦听器;可以使用XMLHttpRequest.upload属性访问XMLHttpRequest对象。5.请求中没有使用ReadableStream对象。例如,请参阅简单CORS请求的示例。用户访问站点https://tool.box3.cn,页面尝试跨域请求从https://api.box3.cn获取数据。发起跨域请求的JS代码如下所示:constxhr=newXMLHttpRequest();consturl='https://api.box3.cn/example/simple';xhr.open('GET',url);xhr.发送();以下是浏览器发送给服务器的请求报文(关键信息)::method:GET:authority:api.box3.cn:scheme:https:path:/example/simpleorigin:https://tool.box3.cnuser-agent:Mozilla/5.0......以下是服务器返回的响应信息(关键信息)::status:200OKserver:nginxdate:Thu,17Nov202202:35:49GMTcontent-类型:应用程序/json;charset=utf-8content-length:47access-control-allow-origin:*本例中服务器返回的头域access-control-allow-origin:*表示该资源可以被任何外域访问或接受所有请求源。access-control-allow-origin:*如果你只想让服务器允许从https://www.example.com访问,这个头字段的内容如下:access-control-allow-origin:https://www.example.com。com关键知识点:当响应带有身份凭证的请求时(例如:Cookie),服务器必须指定access-control-allow-origin字段的值,不能使用通配符“*”,否则浏览器的同-origin策略将阻止请求并在控制台中抛出错误。4.2PreflightRequest和ActualRequest首先,只有当请求具有跨域行为且不是简单请求时,才会产生CORS预检请求(CORS-preflightrequest)。其次,与“简单请求”不同,“预检请求”是浏览器自动发起的额外OPTIONS请求,用于了解服务器是否授权后续的实际请求(例如:XHR或FetchAPI发起的HTTP跨域请求)。其次,OPTIONS请求包含两个重要的headers(headerfields)access-control-request-method和access-control-request-headers。下面是需要发起HTTP预检请求的JS代码示例:constxhr=newXMLHttpRequest();xhr.open('GET','https://api.box3.cn/example/request');xhr.setRequestHeader('box3-token','111-222-333-444');xhr.发送();上面的代码使用GET请求从服务器获取数据,其中包含一个自定义的请求头(box3-token:111-222-333-444)。因为字段名超出了“简单请求”的定义范围,所以浏览器判断这是一个非简单请求,会在“实际请求”之前发起一次“预检请求”。以下是浏览器与服务器第一次交互的消息信息,包括preflight请求头和preflight响应头(注意:user-agent省略部分内容):/*preflightrequestheader*/:method:OPTIONS:权限:api.box3.cn:scheme:https:path:/example/requestaccess-control-request-method:GETaccess-control-request-headers:box3-tokenorigin:https://tool.box3.cnuser-agent:Mozilla/5.0....../*预检响应标头*/:状态:204无内容服务器:nginxdate:2022年11月17日星期四02:35:35GMTaccess-control-allow-headers:box3-tokenaccess-control-allow-origin:*access-control-request-headers告诉服务端实际请求携带的自定义headers,access-control-allow-headers告诉客户端所有支持的自定义headers,多个值用逗号分隔。通常,服务器会在OPTIONS请求的结果中添加一个缓存时间。目的是客户端减少了preflight请求的交互时间,也减轻了服务端的压力。例如服务器在响应头中指定了access-control-max-age:3600,则表示响应的有效时间为3600秒,即1小时。这段时间浏览器不会针对同一个请求再次发起preflight请求,而是直接发起实际情况。添加预检请求缓存后,本例中预检响应头的最新内容如下::status:204NoContentserver:nginxdate:Thu,17Nov202202:35:35GMTaccess-control-allow-headers:box3-tokenaccess-control-allow-origin:*access-control-max-age:3600关键知识点:对于OPTIONS请求,合法的HTTP状态码应该定义在2xx范围内。例如,将状态码设置为200或204是正确的。最后,在预检请求通过后,浏览器发送实际请求。以下是实际请求的请求头和响应头:/*实际请求的请求头*/:method:GET:authority:api.box3.cn:scheme:https:path:/example/requestbox3-token:111-222-333-444origin:https://tool.box3.cnuser-agent:Mozilla/5.0....../*实际请求的响应头*/:status:200OKserver:nginxdate:Thu,2022年11月17日02:35:35GMT内容类型:application/json;charset=utf-8content-length:45access-control-allow-origin:*4.3简单请求和凭据默认情况下,浏览器不发送Cookie信息。携带cookies,以XMLHttpRequest对象为例,需要将withCredentials属性的值设置为true。本例中,站点https://tool.box3.cn中的JS脚本向https://api.box3.cn发起简单的GET跨域请求,并带有身份凭证Cookie。JS示例代码如下:constxhr=newXMLHttpRequest();consturl='https://api.box3.cn/example/simple_cookie';xhr.open('GET',url);xhr.withCredentials=真;xhr.发送();下面是浏览器和服务器消息信息的关键部分(注意:user-agent省略了部分内容):/*简单请求的请求头*/:method:GET:authority:api.box3.cn:path:/example/simple_cookie:scheme:httpscookie:access-token=100;origin:https://tool.box3.cnuser-agent:Mozilla/5.0....../*简单请求的响应头*/:status:200OKserver:nginxdate:Thu,17Nov202202:52:07GMTcontent-type:application/json;charset=utf-8content-length:45access-control-allow-credentials:trueaccess-control-allow-origin:https://tool.box3.cn关键知识点:1.服务端必须指定access-control-allow-credentials:响应头中的true表示允许跨域请求携带cookie,否则仍然会被浏览器的CORSPolicyblocking拦截。2.服务器必须在响应头的access-control-allow-origin字段中指定一个特定的域。这个header的值不能设置为通配符“*”,否则还是会被浏览器的CORS策略拦截。4.4PreflightRequest和Credentials首先,一个完整的CORSpreflightrequest是由浏览器自动完成的,这个动作是用户察觉不到的。其次,与“简单请求与凭证”章节整理的CORS策略知识点一致。也就是说,access-control-allow-credentials:true和access-control-allow-origin字段特定域必须在OPTIONS请求的响应头中显式指定,否则后续的实际请求仍然会被浏览器的CORS策略拦截.最后,在实际请求的响应头中,这两个字段也需要显式指定,并保持与OPTIONS相同的值。重点知识点:如果实际请求的HTTP方法不是GET、POST或HEAD,则access-control-allow-methods字段的值不能设置为通配符“*”,应设置为具体的HTTP请求方法名称,多个值以逗号分隔。4.5PreflightRequest和Redirection回顾4.2节的关键知识点,PreflightRequest是指OPTIONS请求,HTTP状态码定义在2xx范围内。因此,如果重定向preflight请求,HTTP状态码必须大于2xx,大部分浏览器会报如下错误:AccesstoXMLHttpRequestat'https://api.box3.cn/example/request_redirect'fromorigin'https://tool.box3.cn'hasbeenblockedbyCORSpolicy:Responsetopreflightrequestdoesn'tpassaccesscontrolcheck:Redirectisnotallowedforapreflightrequest.有两种方法可以规避上述错误行为:1、去除服务器上预检请求的重定向。2.将请求优化为简单请求。五、常见的四种CORS错误常见的CORS跨域请求错误可能有以下四种情况(服务器上配置了以下头域):1、可信源access-control-allow-origin配置不正确。2.可信HTTP方法access-control-allow-methods配置不完整。3.可信头字段access-control-allow-headers配置不完整。4.access-control-allow-credentials服务器与请求者之间的凭证权限配置错误。6、使用浏览器找错导致CORS错误的原因是跨域请求失败,不是JS代码层面的逻辑BUG。如果JS发起的HTTP请求产生了CORS错误,在JS代码层面无法知道哪里出了问题,但是可以通过浏览器控制台获取错误信息。例如在Chrome浏览器中按F12键启动开发者调试工具,在Network面板中可以了解到具体的错误信息。如下图所示:7.理解这些HTTP请求头和响应头7.1HTTP请求头字段Headerdescriptionorigin表示预检请求或实际请求的来源站点。origin的值只包括协议、域名和端口,不包括路径和参数。access-control-request-method出现在preflightrequest中,其作用是告知服务器在实际请求中使用哪种HTTP方法。access-control-request-headers出现在preflightrequest中,其作用是告知服务器在实际请求中使用哪些HTTP请求头。7.2HTTP响应头字段Header表示access-control-allow-origin指定请求的资源可以与哪些域共享。只能为此字段指定一个来源。对于不需要携带身份凭证的请求,可以设置为通配符*,表示允许所有来源访问。跨源访问access-control-expose-headers时,XMLHttpRequest对象的getResponseHeader()方法只能得到一些最基本的响应头。如果需要获取其他响应头,通过该字段添加白名单。access-control-allow-methods对预检请求的响应指定实际请求允许使用哪些HTTP方法。access-control-allow-headers对预检请求的响应,表示实际请求允许携带哪些HTTP头。access-control-max-age指定预检请求的有效期,以秒为单位。目的是减少启动的预检请求的数量。access-control-allow-credentials设置为true时,告诉浏览器将响应公开给前端JavaScript代码。注意这个值是严格区分大小写的,正确的写法是全部小写。

最新推荐
猜你喜欢