自建营销型网站模板,外贸平台都有什么网站,域名注册网站建设网络实名,建设网站有哪些CORS意为跨源资源共享#xff08;Cross origin resource sharing#xff09;#xff0c;它是一个W3C标准#xff0c;由一系列HTTP Header组成#xff0c;这些 HTTP Header决定了浏览器是否允许JavaScript 代码成功获得跨源请求的服务器响应。
在说CORS之前#xff0c;先…CORS意为跨源资源共享Cross origin resource sharing它是一个W3C标准由一系列HTTP Header组成这些 HTTP Header决定了浏览器是否允许JavaScript 代码成功获得跨源请求的服务器响应。
在说CORS之前先说一下关于浏览器的同源政策same-origin policy。
同源政策是浏览器安全的基石由Netscape公司引入浏览器目前所有浏览器都实行这个政策。最初它的含义是指A网页设置的 CookieB网页不能打开除非这两个网页同源。所谓同源指的是三个相同协议相同域名相同端口相同。 举例来说http://www.example.com/dir/page.html这个网址协议是http://域名是www.example.com端口是80默认端口可以省略。它的同源情况如下 http://www.example.com/dir2/other.html同源 http://example.com/dir/other.html不同源域名不同 http://v2.www.example.com/dir/other.html不同源域名不同 http://www.example.com:81/dir/other.html不同源端口不同 同源政策的是为了保证用户信息的安全防止恶意的网站窃取数据。设想这样一种情况A网站是一家银行用户登录以后又去浏览其他网站。如果其他网站可以读取A网站的 Cookie会发生什么如果 Cookie 包含隐私比如存款总额这些信息就会泄漏。更可怕的是Cookie 往往用来保存用户的登录状态如果用户没有退出登录其他网站就可以冒充用户为所欲为。因为浏览器同时还规定提交表单不受同源政策的限制。
由此可见同源政策是必需的否则 Cookie 可以共享互联网就毫无安全可言了。随着互联网的发展同源政策越来越严格。目前如果非同源共有三种行为受到限制。 1 Cookie、LocalStorage 和 IndexDB 无法读取。 2 DOM 无法获得。 3 AJAX 请求不能发送。 虽然这些限制是必要的但是有时很不方便合理的用途也受到影响。所以有时需要一些额外的操作来打破同源政策的束缚。 Cookie 是服务器写入浏览器的一小段信息只有同源的网页才能共享。但是两个网页一级域名相同只是二级域名不同时浏览器允许通过设置document.domain共享 Cookie。
举例来说A网页是http://w1.example.com/a.htmlB网页是http://w2.example.com/b.html那么只要设置相同的document.domain两个网页就可以共享Cookie。 document.domain example.com; 现在A网页通过脚本设置一个 Cookie。
document.cookie test1hello;
B网页就可以读到这个 Cookie。
var allCookie document.cookie;
注意这种方法只适用于 Cookie 和 iframe 窗口LocalStorage 和 IndexDB 无法通过这种方法规避同源政策。
另外服务器也可以在设置Cookie的时候可以指定Cookie的所属域名为一级域名比如 Set-Cookie: keyvalue; domain.example.com; path/ 这样的话二级域名不用做任何设置都可以读取这个Cookie。 同源政策规定AJAX请求只能发给同源的网址否则就报错。有三种方法规避这个限制JSONP、WebSocke和CORS。 JSONP是早期服务器与客户端跨源通信的常用方法。它的基本思想是通过在网页上添加一个script元素向服务器请求JSON数据这种做法不受同源政策限制服务器收到请求后将数据放在一个指定名字的回调函数里传回来。
function addScriptTag(src) {var script document.createElement(script);script.setAttribute(type,text/javascript);script.src src;document.body.appendChild(script);
}window.onload function () {addScriptTag(http://example.com/ip?callbackfoo);
}function foo(data) {console.log(Your public IP address is: data.ip);
};
上面代码通过动态添加script元素向服务器example.com发出请求。该请求的查询字符串有一个callback参数用来指定回调函数的名字这对于JSONP是必需的。服务器收到这个请求以后会将数据放在回调函数的参数位置返回。
foo({ip: 8.8.8.8
});
由于script元素请求的脚本直接作为代码运行。这时只要浏览器定义了foo函数该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象而不是字符串因此避免了使用JSON.parse的步骤。
WebSocket是一种通信协议使用ws://非加密和wss://加密作为协议前缀。该协议不实行同源政策只要服务器支持就可以通过它进行跨源通信。
下面是浏览器发出的WebSocket请求的头信息 GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com 上面请求头中有一个字段是Origin声明了该请求的源。正是因为有了Origin这个字段所以WebSocket才没有实行同源政策。服务器可以根据这个字段判断是否许可本次通信。如果允许这个源服务器就会做出如下回应 HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk Sec-WebSocket-Protocol: chat 接下来就是CORS了。CORS是跨源资源分享Cross-Origin Resource Sharing的缩写。它是W3C标准 堪称跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求CORS允许任何类型的请求。
同源安全策略默认阻止“跨源”获取资源XMLHttpRequest 和 Fetch API 都遵循同源策略这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源。 现代浏览器支持在API容器中例如 XMLHttpRequest 或 Fetch使用 CORSCORS 机制给了WEB服务器一种权限允许服务器进行跨源访问控制。服务器可以选择在响应报文中包含正确的CORS响应头这样浏览器就能允许JavaScript跨源访问WEB服务器提供的资源。
CORS功能概述 CORS标准新增了一组 HTTP Header字段允许服务器声明哪些源Origin通过浏览器有权限访问。另外标准要求对那些可能对服务器数据产生副作用的 HTTP 请求方法特别是 GET 以外的 HTTP 请求或者搭配某些 MIME 类型的 POST 请求浏览器必须首先使用 OPTIONS 方法发起一个预请求preflight request获知服务器端是否允许该跨源请求。服务器确认允许后才发起实际的 HTTP 请求。在预检请求的返回中服务器端也可以通知客户端是否需要携带身份凭证Credential例如 Cookie 和 HTTP 认证相关数据。CORS请求失败时会产生错误但是为了安全在JavaScript代码层面无法获知到底具体是哪里出了问题。只能查看浏览器的控制台以得知具体是哪里出现了错误。
浏览器将CORS请求分成两类简单请求simple request和非简单请求not-so-simple request。
只要同时满足以下两大条件就属于简单请求。 1) 请求方法是以下三种方法之一 HEADGETPOST 2HTTP的头信息不超出以下几种字段 AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 这是为了兼容表单form因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是只要表单可以发AJAX 就可以直接发。
凡是不同时满足上面两个条件就属于非简单请求。
浏览器对这两种请求的处理是不一样的。
对于简单请求浏览器直接发出CORS请求。具体来说就是在头信息之中增加一个Origin字段。 下面是一个浏览器发送跨源AJAX请求是简单请求例子 GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: en-us,en;q0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://foo.example 上面的头信息中Origin字段用来说明本次请求来自哪个源协议 域名 端口。而服务器根据这个值决定是否同意这次请求。
如果Origin指定的源不在许可范围内服务器会返回一个正常的HTTP回应。浏览器发现这个回应的头信息没有包含Access-Control-Allow-Origin字段就知道出错了从而抛出一个错误被XMLHttpRequest的onerror回调函数捕获。注意这种错误无法通过状态码识别因为HTTP回应的状态码有可能是200。如果Origin指定的域名在许可范围内服务器返回的响应会包含Access-Control-Allow-Origin头信息字段 HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2 Access-Control-Allow-Origin: * Keep-Alive: timeout2, max100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml […XML Data…] 本例中服务端返回的标头Access-Control-Allow-Origin: * 表明该资源可以被任意外源访问。
使用 Origin 和 Access-Control-Allow-Origin 就能完成最简单的访问控制。如果 https://bar.other 的资源持有者想限制他的资源只能通过https://foo.example来访问也就是说非 https://foo.example 域无法通过跨源访问访问到该资源可以这样做 Access-Control-Allow-Origin: https://foo.example 注意 当响应的是附带身份凭证的请求时credentialed requests request服务端必须明确 Access-Control-Allow-Origin 的值而不能使用通配符“*”后面还会再说明。
上面的头信息之中除了Access-Control-Allow-Origin是必须的字段还有两个可选的字段Access-Control-Allow-Credentials和Access-Control-Expose-Headers。
Access-Control-Allow-Credentials字段的值是一个布尔值表示是否允许发送Cookie。默认情况下Cookie不包括在CORS请求之中。Access-Control-Allow-Credentials设为true表示服务器明确许可Cookie可以包含在请求中发给服务器。这个值也只能设为true如果服务器不允许浏览器发送Cookie删除该字段即可。
CORS请求时XMLHttpRequest对象的getResponseHeader()方法只能拿到响应头中的6个基本字段Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段就必须在Access-Control-Expose-Headers里面指定例如 Access-Control-Expose-Headers: FooBar 这样XMLHttpRequest对象的getResponseHeader(FooBar)可以返回FooBar的值。
withCredentials 属性 上面说到CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器一方面要服务器指定Access-Control-Allow-Credentials: true另一方面开发者必须在AJAX请求中打开withCredentials属性。
var xhr new XMLHttpRequest();
xhr.withCredentials true;
否则即使服务器同意发送Cookie浏览器也不会发送。
但是如果省略withCredentials设置有的浏览器还是会一起发送Cookie。这时可以显式关闭withCredentials。
xhr.withCredentials false;
非简单请求 非简单请求是那种对服务器有特殊要求的请求比如请求方法是PUT或DELETE或者Content-Type字段的类型是application/json。非简单请求的CORS请求会在正式通信之前增加一次使用 OPTIONS 方法发起的预检请求preflight request到服务器以获知服务器是否允许该实际请求。预检请求“的使用可以避免跨域请求对服务器的用户数据产生未预期的影响。
预检请求会询问服务器当前网页所在的域名是否在服务器的许可名单之中以及可以使用哪些HTTP方法和请求头字段。只有得到肯定答复浏览器才会发出正式的XMLHttpRequest请求否则就报错。
如下是一个需要执行预检请求的 HTTP 请求
const xhr new XMLHttpRequest();
xhr.open(POST, https://bar.other/resources/post-here/);
xhr.setRequestHeader(X-PINGOTHER, pingpong);
xhr.setRequestHeader(Content-Type, application/xml);
xhr.onreadystatechange handler;
xhr.send(personnameArun/name/person);
上面的代码使用 POST 请求发送一个 XML 请求体该请求的 Content-Type 为 application/xml且使用了一个自定义的X-PINGOTHER 请求头。浏览器发现这是一个非简单请求就自动发出一个预检请求要求服务器确认可以这样请求。 下面是这个预检请求的HTTP头信息。 OPTIONS /doc HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: en-us,en;q0.5 Accept-Encoding: gzip,deflate Connection: keep-aliveOrigin: https://foo.example Access-Control-Request-Method: POST Access-Control-Request-Headers: X-PINGOTHER, Content-Type 预检请求用的请求方法是OPTIONS表示这个请求是用来询问的。OPTIONS 是 HTTP/1.1 协议中定义的方法用于从服务器获取更多信息是安全的方法。该方法不会对服务器资源产生影响。OPTIONS预检请求中除了origin之外同时携带了下面两个标头字段 Access-Control-Request-Method: POST Access-Control-Request-Headers: X-PINGOTHER, Content-Type Access-Control-Request-Method 告知服务器实际请求将使用 POST 方法。Access-Control-Request-Headers告知服务器实际请求将携带两个自定义请求标头字段X-PINGOTHER 与 Content-Type。服务器据此决定该实际请求是否被允许。
服务器收到预检请求以后检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后若确认允许跨源请求就可以做出如下回应。 HTTP/1.1 204 No Content Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400 Vary: Accept-Encoding, Origin Keep-Alive: timeout2, max100 Connection: Keep-Alive 服务器的响应头携带了 Access-Control-Allow-Origin: https://foo.example限制请求的源域。Access-Control-Allow-Methods 表明服务器允许客户端使用 POST 和 GET 方法发起请求。Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样Access-Control-Allow-Headers 的值为逗号分割的列表。最后字段 Access-Control-Max-Age 给定了该预检请求可供缓存的时间长短单位为秒默认值是 5 秒。在有效时间内浏览器无须为同一请求再次发起预检请求。以上例子中该响应的有效时间为 86400 秒也就是 24 小时。请注意浏览器自身维护了一个最大有效时间如果该标头字段的值超过了最大有效时间将不会生效。当然此时服务器也可以有可选的Access-Control-Allow-Credentials: true响应头含义与简单请求时是一样的。
如果服务器否定了预检请求会返回一个正常的HTTP回应但是没有任何CORS相关的头信息字段。这时浏览器就会认定服务器不同意预检请求因此触发一个错误被XMLHttpRequest对象的onerror回调函数捕获。
预检请求完成之后发送实际请求就跟简单请求一样会有一个Origin头信息字段当然也会包含预检请求中已经说明过的自定义请求头等内容。服务器的回应也都会有一个Access-Control-Allow-Origin头信息字段。 POST /doc HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: en-us,en;q0.5 Accept-Encoding: gzip,deflate Connection: keep-alive X-PINGOTHER: pingpong Content-Type: text/xml; charsetUTF-8 Referer: https://foo.example/examples/preflightInvocation.html Content-Length: 55Origin: https://foo.example Pragma: no-cache Cache-Control: no-cache personnameArun/name/person 服务器的响应 HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:40 GMT Server: Apache/2Access-Control-Allow-Origin: https://foo.example Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 235 Keep-Alive: timeout2, max99 Connection: Keep-Alive Content-Type: text/plain [Some XML payload] 这里有一个Vary响应头。Vary响应头就是让同一个URL根据某个请求头的不同而使用不同的缓存。这里的设定是让浏览器在即使同一个URL的情况下也能根据不同的Accept-Encoding或Orign进行不同的缓存。为什么要有这个设定因为默认浏览器里的缓存是以URL为key一个 URL 对应一个缓存。但如果浏览器访问了两 URL相同但CORS响应头不应该相同的资源会如何呢
比如在同一个浏览器下先打开了foo.taobao.com上的一个页面访问了服务器的资源这个资源和URL被浏览器缓存了下来与资源内容一起缓存的还有Access-Control-Allow-Origin: https://foo.taobao.com响应头。这时又打开 bar.taobao.com上的一个页面这个页面也访问服务器上之前的那个资源因为URL相同这时它会读取本地缓存。但是读到的 Access-Control-Allow-Origin头信息是缓存下的 https://foo.taobao.com 而不是自己想要的 https://bar.taobao.com这时浏览器就报跨域错误了虽然它应该是能访问到这份资源的。再比如用户先访问了foo.taobao.com的一个页面 A页面 A 里用img标签加载了一张图片注意这时候这张图片已经被浏览器缓存了并且缓存里没有 Access-Control-Allow-Origin响应头因为img发起的请求不带Origin请求头然后用户又访问了foo.taobao.com的另一个页面 B页面 B 里用 XHR 请求同一张图片结果读了缓存没有发现 CORS 响应头浏览器报跨域错误。
在CORS的场景下使用Vary: Origin来保证从不同网站发起的请求可以使用各自的缓存。比如从foo.taobao.com发起的请求缓存中的响应头是 Access-Control-Allow-Origin: https://foo.taobao.com Vary: Origin 的话bar.taobao.com在发起同URL的请求就不会使用这份缓存了因为Origin请求头变了。
还有img标签发起的非 CORS 请求缓存中的响应头是 Vary: Origin 的话 在使用 XHR 发起的 CORS 请求也不会使用那份缓存因为Origin请求头从无到有也算是变了。
最佳的实践原则是 If CORS protocol requirements are more complicated than setting Access-Control-Allow-Origin to * or a static origin, Vary is to be used. 如果你的 Access-Control-Allow-Origin响应头不是简单的写死成了*或者某一个固定的源那么你就应该加上Vary: Origin响应头。
最后再重复说明一下前面已经提及过的附带身份凭证credential的请求。 对于跨源的XMLHttpRequest 或 Fetch 请求浏览器不会发送身份凭证信息。如果要发送凭证信息需要设置 XMLHttpRequest对象的 withCredentials标志为true或在构造 Request 对象时设置。
本例中https://foo.example 的某脚本向 https://bar.other 发起一个 GET 请求并设置 Cookies。在 foo.example 中可能包含这样的 JavaScript 代码
const invocation new XMLHttpRequest();
const url https://bar.other/resources/credentialed-content/;function callOtherDomain() {if (invocation) {invocation.open(GET, url, true);invocation.withCredentials true;invocation.onreadystatechange handler;invocation.send();}
}将 XMLHttpRequest 的 withCredentials 标志设置为 true才能向服务器发送 Cookies。这是一个简单 GET 请求所以浏览器不会对其发起“预检请求”。但是如果服务器端的响应中没有Access-Control-Allow-Credentials: true浏览器不会把响应内容返回给请求的发送者。 客户端发起的请求头 GET /resources/credentialed-content/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Accept-Language: en-us,en;q0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Referer: https://foo.example/examples/credential.html Origin: https://foo.example Cookie: pageAccess2 请求中指定了 Cookie 是属于 https://bar.other 的内容。如果来自服务器的响应中缺失 Access-Control-Allow-Credentials: true则服务器响应内容会被浏览器忽略不会提供给请求的发送者。
服务器的响应 HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:34:52 GMT Server: Apache/2Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Credentials: true Cache-Control: no-cache Pragma: no-cache Set-Cookie: pageAccess3; expiresWed, 31-Dec-2008 01:34:53 GMTVary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 106 Keep-Alive: timeout2, max100 Connection: Keep-Alive Content-Type: text/plain [text/plain payload] CORS 预检请求不能包含凭据但对预检请求的响应头中必须指定 Access-Control-Allow-Credentials: true 来表明可以携带凭据进行实际的请求。
在服务器响应附带身份凭证的请求时
服务器不能将 Access-Control-Allow-Origin 的值设为通配符“*”而应将其设置为特定的域如Access-Control-Allow-Origin: https://example.com。服务器不能将 Access-Control-Allow-Headers 的值设为通配符“*”而应将其设置为请求头名称的列表如Access-Control-Allow-Headers: X-PINGOTHER, Content-Type服务器不能将 Access-Control-Allow-Methods 的值设为通配符“*”而应将其设置为特定请求方法名称的列表如Access-Control-Allow-Methods: POST, GET
另外上面的响应标头中也携带了 Set-Cookie 字段尝试对 Cookie 进行修改修改pageAccess的内容。如果操作失败将会抛出异常。同样的如果用户设置其浏览器拒绝所有第三方 cookie那么将不会被保存。