浏览器的同源安全策略
是由 NetScape 提出的著名的安全策略,所有支持 javaScript 的浏览器都使用这个策略。同源策略限制了一个源中加载文本或脚本与来自其它源中资源的交互方式,同源策略是浏览器最核心也最基本的安全功能。
怎样算跨域呢?
- 请求协议 http , https 的不同
- 域 domain 的不同
- 端口 port 的不同(IE 未将端口号加入到同源策略的组成部分之中)
浏览器只允许请求当前域的资源,而对其他域的资源表示不信任。但是现代浏览器在安全性和实用性上做出了让步, img/script/style/iframe 等有 src 属性的都允许跨域引用资源。
CORS
这是一个 W3C 标准,全称是“跨域资源共享”(Cross-Origin Resource Sharing),它允许浏览器向跨域服务器发出 XMLHttpRequest 请求,从而解决了 Ajax 只能同源使用的限制。 CORS 需要浏览器和服务器同时支持,但是整个通信过程,都是浏览器自动完成(IE浏览器不能低于IE10)。
简单请求
- 请求方式只能是: head , post , get
- 请求头允许的字段: Accept , Accept-Language , Content-Language , Last-Event-ID
- Content-Type: application/x-www-form-urlencoded 、 multipart/form-data 、 text/plain 三选一
对于简单请求,浏览器直接发出 CORS 请求。浏览器会自动在头信息(Request Headers)中,添加一个 Origin 字段,来表明本次请求来自哪个域(Chrome 在非跨域的情况下,也会发送 Origin 字段)。
如果这个源不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,被 XMLHttpRequest 的 onerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。
PHP 服务端配置示例:
header('Access-Control-Allow-Origin:http://127.0.0.1:80');
设置为 * 表示该数据对任何人可见(但是浏览器将不会发送 Cookie ,即使 xhr 设置了 withCredentials ),如果只希望特定的地址访问,可以设置为对应的地址。
附带身份凭证的跨域请求
一般基于 HTTP Cookie 的验证身份对于跨域 XMLHttpRequest 请求来说,浏览器并不会发送对应的身份凭证信息,如果需要带上身份凭证的 XMLHttpRequest 请求,需要做额外的设置。
PHP 服务端示例:
header("Access-Control-Allow-Credentials: true"); header("Access-Control-Allow-Origin:{$_SERVER['HTTP_ORIGIN']}"); // 动态获取请求域。可以先设置一个白名单列表,判断Orign是否在白名单里
前端 Ajax 也要打开 withCredentials 属性,从而向服务器发送 Cookie ,服务端设置 Access-Control-Allow-Credentials:true 来把响应内容返回请求者:
// Jq xhrFields: { withCredentials: true } // 原生 var xhr=new XMLHttpRequest(); xhr.withCredentials=true;
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为 * 。这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为 * ,请求将会失败。
也就是说 Access-Control-Allow-Credentials 设置为 true 的情况下, Access-Control-Allow-Origin 不能设置为 * 。
Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的 Cookie 。
非简单请求
除了上面说的简单请求外都是非简单请求。如:请求方法是 PUT 或 DELETE ;或者 Content-Type 字段的类型是 application/json ;又或者有自定义请求头。
浏览器会先发送 option (预检)请求,要求服务器确认可以这样请求。服务端需要加两个参数设置:
//指定允许其他域名访问 header('Access-Control-Allow-Origin:$_SERVER['HTTP_ORIGIN']') //是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回 header('Access-Control-Allow-Credentials:true') //预检结果缓存时间 header('Access-Control-Max-Age: 1800') //允许的请求类型 header('Access-Control-Allow-Methods:GET,POST,PUT,POST') //允许的请求头字段 header('Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization')
预检请求成功之后,浏览器就会进行正常 CORS 请求。
参考:
http://www.ruanyifeng.com/blog/2016/04/cors.html