同源策略:
是由 NetScape 提出的著名的安全策略, 所有支持 JavaScript 的浏览器都使用这个策略. 同源策略限制了一个源中加载文本或脚本与来自其它源中资源的交互方式.
IE 特例:
授信范围(Trust Zones): 两个相互之间高度互信的域名, 如公司域名(corporate domains), 不遵守同源策略的限制.
端口: IE 未将端口号加入到同源策略的组成部分之中, 因此 http://company.com:81/index.html 和 http://company.com/index.html 属于同源并且不受任何限制.
以上参考自 MDN.
所谓同源是指: 域名, 协议, 端口都相同. 同源策略是浏览器最核心也最基本的安全功能
. 这是知乎上关于同源策略的一篇文章. https://www.zhihu.com/question/25427931 . 但是浏览器在安全性和实用性上做出了让步, img/script/style/iframe 等有 src 属性的都可以跨域引用资源.
方案一 -- 通过 JSONP 跨域
JSONP 是 JSON with padding 的简写, 看起来与 JSON 差不多, 但是包含在函数调用中的 JSON, 利用动态 script 元素来使用(具有 src 属性的如 img,iframe,srcipt 都不受同源策略的影响). 该协议的一个要点就是允许用户传递一个 callback 参数给服务端, 然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据, 这样客户端就可以随意定制自己的函数来自动处理返回数据了.
如果使用 jQuery, 可以在 type 为 get 的时候 dataType 设为 JSONP, 就可以了.
- $.Ajax({
- url: 'http://www.qdaily.com/get_user_and_radar.json?winWidth=1280&winHeight=800',
- type: 'get',
- dataType: 'jsonp',
- data: {},
- })
- .done(function(data) {
- console.log(data.status);
- })
然后, 很幸运的就可以看到这个:
Paste_Image.PNG
但是可以看出其实是请求到数据的, 只是没有执行我们的 callBack 函数.
Paste_Image.PNG
因为前面有提到: 服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据, 也就是服务端的返回需要拼接成这样(比如函数名叫做 haddleData):
haddleData({"status":true,subscribes:["12","23"]})
另外: jQuery 的 Ajax 方法可以传入两个参数
- JSONP : "callback"// 设置这个会替换浏览器发送请求时地址后面自动添加的? callback=xxx 中的 callback 这个字, 一般情况下不用传这个参数
- jsonpCallback: "success_jsonpCallback"// 这个值将用来取代 jQuery 自动生成的随机函数名, 也就是上句话中的'xxx'.
[这儿遗留了一个问题, 还请各位解决] 如果 jsonpCallback 不手动设置的话, jQuery 是会自动生成个随机的字符串的, 但是服务器那边的函数名要写什么, 回调函数才会正确的执行呢?
看一个运行成功的实例:
- $.Ajax({
- url: 'http://localhost:8000/remote/remote.js',
- type: 'get',
- dataType: 'jsonp',
- JSONP: 'back',
- jsonpCallback: "haddleData",
- data: {},
- })
- .done(function(data) {
- console.log(data.status);
- })
- // 这段程序运行在 8080 端口的
remote.JS 作为远端其中写了句
haddleData({"status":true,subscribes:["12","23"]})
Paste_Image.PNG
对于大部分 JS 初学者, 大多使用 jQuery, 而对原生不太熟悉. 但是须知, Ajax 和 JSONP 其实本质上是不同的东西, Ajax 的核心是通过 XmlHttpRequest 获取非本页内容, 而 JSONP 的核心则是动态添加 < script > 标签来调用服务器提供的 JS 脚本, 利用脚本下载下来后立即执行的事实实现 callback 方法的调用.
原生 JS 的 JSONP 简单实现:
- <script>
- function haddleData(data){
- console.log(data.status);
- }
- </script>
- <script type="text/javascript" src="http://localhost:8000/remote/remote.js"></script>
当然有多个本地函数需要处理的时候, 加上回调函数名才是方便的, 像这样:
<script type="text/javascript" src="http://localhost:8000/remote/remote.js?callback=haddleData"></script>
加上 callback 参数, 可以使得服务器端根据前端指定的方法名 cb 动态返回 cb(data); 而不是都写死 handleData(data);
[总结下 JSONP] :
优点:
简单, 函数回调在本地处理;
缺点:
1, 安全性(存在注入漏洞, 如 CSRF,XSS);
2, 如果出现错误, 不会像 http 请求那样有状态码;
3, 只能使用 get 请求;
方案二 --CORS(Cross-Origin Resource Sharing)
这是一个 W3C 标准(显然比 JSONP 背景深厚许多), 同样需要浏览器和服务器同时支持, 但是整个通信过程, 都是浏览器自动完成, 不需要用户参与, 就像平时写 Ajax 一样(如果使用的是 jQuery 的话).
下面是原生 JS 实现 CORS 的代码
- function createCORSRequest(method,url){
- var xhr=new XMLHttpRequest();
- if("withCredentials" in xhr){
- xhr.open(method,url,true);
- }else if(typeof XDomainRequest != "undefined"){//IE10 之前的版本使用 XDmainRequest 支持 CORS
- xhr=new XDomainRequest();
- xhr.open(method,url);
- }else{
- xhr=null;
- }
- return xhr;
- }
- var request=createCORSRequest("get","待访问的地址");
- if(request){
- request.onload=function(data){
- //do sth
- };
- request.send();
- }
适用场景:
承载的信息量大, get 形式搞不定, 需选用 post 传输. CORS 支持所有类型的传输.
兼容性:
移动端全面支持(除 opera mini),PC 上 IE8+.
CORS 思想:
使用自定义的 HTTP 头部让浏览器与服务器进行沟通, 从而决定请求或响应是应该成功还是失败.(请求和响应都不包含 cookie)
当然如果设置成下面这样, 所有的跨域都可以实现了, 但这样毕竟太不安全.
"Access-Control-Allow-Origin:*";// 允许任何域向我们的服务器发送请求
一般情况下, 浏览器发送一个额外的 Origin 头部(由浏览器自动生成发送)
Origin:http://localhost:8080// 本地网址
然后由服务器发送一个响应表头: Access-Control-Allow-Origin, 如果服务器接收该请求, 返回值 (只能是通配符或单域名.) 就和请求值一样.
Access-Control-Allow-Origin:http://localhost:8080
=>CORS 方案的重点其实就在于服务器端的配置.
简单请求
只使用 GET, HEAD 或者 POST 请求方法.
如果使用 POST 向服务器端传送数据, 则数据类型 (Content-Type) 只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain 中的一种.
不会使用自定义请求头(类似于 X-Modified 这种).
HTTP 头部信息不超出以下{Accept,Accept-Language,Content-Language,Last-Event-ID,content-type(只限于上面提到的 3 种类型)}
对于简单请求, 浏览器直接发出 CORS 请求. 浏览器会自动在头信息 (Request Headers) 中, 添加一个 Origin 字段, 来表明本次请求来自哪个域.
Paste_Image.PNG
如果这个源不在许可范围内, 会报错: No 'Access-Control-Allow-Origin' header is present on the requested resource.
如果 Origin 指定的域名在许可范围内(必须是跨域了的),Response Headers 中会多出几个头信息字段.
- Access-Control-Allow-Credentials:true// 值为 true 表示允许发送 cookie
- Access-Control-Allow-Methods:GET, POST, OPTIONS
- Access-Control-Allow-Origin:http://localhost:8080
- Access-Control-Max-Age:1728000
withCredentials 属性
因为 CORS 默认不发送 cookie 和 http 认证, 如果要把 Cookie 发到服务器, 就要指定 Access-Control-Allow-Credentials:true;
另外 Ajax 中也要打开 withCredentials 属性.
- var xhr=new XMLHttpRequest();
- xhr.withCredentials=true;
jQuery Ajax 请求参数中加入
- xhrFields: {
- withCredentials: true
- }
非简单请求
除了上面说的简单请求外都是非简单请求, 比如: 请求方法是 PUT
或 DELETE, 或者 Content-Type 字段的类型是 application/JSON, 又或者有自定义请求头 Access-Control-Request-Headers: X-Custom-Header.
比如, 我添加自定义请求头
xhr.setRequestHeader('Some-Custom-Response-Header', 'value');
就会发现连续向同一地址请求了两次, 而第一次请求什么值也没拿到
Paste_Image.PNG
第二次请求
Paste_Image.PNG
这是因为浏览器发现, 这是一个非简单请求, 就自动发出一个 "预检" 请求, 要求服务器确认可以这样请求."预检" 请求用的请求方法是 OPTIONS, 表示这个请求是用来询问的."预检" 请求之后, 浏览器球会进行正常 CORS 请求.
服务器端配置(Rails 为例)
1, 可以参考这个方法: 为 RESTful API 配置 CORS 实现跨域请求, 写的挺详细的. 然而对于 Rails 并不算熟悉的我来说, 有 Gem 包, 怎么可能放着不用呢~
2, 这是 Rack CORS 中间件的项目地址 https://github.com/cyu/rack-cors , 按照文档的说明 1 分钟就可以基本搞定.
[问题:]
当我请求方式为 put 时, 却出现了 404 错误, 是因为没有为 options 提供路由么?
Rails 中使用 CORS 中的功能和细节还有很多, 等到需要时再行挖掘吧, 欢迎使用过的各位一起交流问题.
来源: http://www.bubuko.com/infodetail-2862643.html