由于 Javascript 同源策略的存在使得一个源中加载来自其它源中资源的行为受到了限制。即会出现跨域请求禁止。
通俗一点说就是如果存在协议、域名、端口或者子域名不同服务端,或一者为IP地址,一者为域名地址(在跨域问题上,域仅仅是通过“ url的首部 ”来识别而不会去尝试判断相同的IP地址对应着两个域或者两个域是否同属同一个IP),之中任意服务端旗下的客户端发起请求其它服务端资源的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。
但很多时候我们却又不得不去跨域请求资源,这个时候就需要我们想方法去绕过浏览器同源策略的限制了。
常见的跨域请求解决方法:
1.Jsonp 利用script标签发起get请求不会出现跨域禁止的特点实现
2.window.name+iframe 借助中介属性window.name实现
3.html5的 postMessage 主要侧重于前端通讯,不同域下页面之间的数据传递
4.Cors需要服务器设置header:Access-Control-Allow-Origin
5.Nginx反向代理 可以不需要目标服务器配合,不过需要Nginx中转服务器,用于转发请求(服务端之间的资源请求不会有跨域限制)
下面分别说一下常用几种解决方案的具体实现:
1 Jsonp
一般情况下由于同源策略我们不能够通过XHR跨域去请求资源,但是我们的script标签却可以不受此限制成功的外链到来自于其他域下的资源。利用script标签这个特点,我们可以成功的绕过同源策略。但这种方式存在局限性,我们只能够发起get跨域请求。具体实现:
客户端
服务端
script标签跨域发起get请求,同时传递在客户端已经注册的全局函数 process_fun 作为查询参数,服务端代码提取查询参数,并传入需要返回的数据。script标签还有一个特点,就是会立即执行调用所请求到的资源,所以这个时候服务端返回的 process_fun(data) 这一句代码会被立即执行,即把数据 data 传入我们事先定义好的函数 process_fun 中执行进一步的处理。
2 Cors
即跨域资源共享。实现cors通信的关键是服务器,只要服务器实现了cors接口,就可以跨源通信。不过不同于jsonp,cors对于IE8以下的浏览器是不支持的。
客户端
服务端
跨域资源共享在我看来是最直接也最简便的解决跨域问题的方式了,唯一的缺陷也就只是不能覆盖支持所有的浏览器。客户端不需要去做另外的改变,跟一般状况下发送的异步请求一致就行,甚至客户端根本不需要知道所请求的接口是跨域的。而服务端所需要做的也只是返回响应的同时设置 Access-Control-Allow-Origin
响应头部,意为“予许指定源(‘*’为任意源)发起跨域资源请求”。
3 window.name 和 postMessage
window.name 和 postMessage 主要都侧重于纯前端页面之间的数据通讯,前者利用了 “ 在同一浏览器窗口载入的不同页面( 无论它们是否不同域 ),共享同一个window.name,并且都对 window.name 有读写的权限 ” 的这一特性来实现页面间的数据交换,后者则是HTML5的API,不同域下的页面在满足一定关系的条件下可以通过此API跨域传送数据。
4 Nginx反向代理
Nginx反向代理解决跨域问题则是利用了服务端之间的资源请求不会有跨域限制的特点实现的,具体来说就是我们前端发起的请求被Nginx拦截,再由Nginx代由转发请求到资源服务器请求资源。
比如现在我们有两个Nodejs服务,分别是http://127.0.0.1:3000 和 http://127.0.0.1:5000,5000端口对应的服务端下的页面需要发起请求3000端口所对应的服务端的资源,当然,在这种情况下如果不做任何的额外处理,请求会产生跨域。这个时候我们可以用Nginx来代理转发我们的请求,前端不去直接对资源服务器发起请求,而是改为直接访问Nginx服务器,看到这里你可能会问了,发起请求的前端页面是属于 http://127.0.0.1:5000 所在域下的,对Nginx服务发起请求难道不会和之前直接发起的请求一样出现跨域吗?所以这里需要明白的一点是,一开始我们可能是通过 http://127.0.0.1:5000/ 这样的路径访问到我们的页面的,但是如果我们使用Nginx作为反向代理,代理服务器监听8080端口,我们这时候再访问该页面就不再是访问 http://127.0.0.1:5000/ , 而是 http://127.0.0.1:8080/ 了,在Nginx中我们再做这样的配置:
1
2
3
|
location / { proxy_pass http: //127.0.0.1:5000; } |
就能成功访问到首页了,而这个时候首页是在 8080 端口所对应的域下(即Nginx服务)被渲染出来的,所以首页这个时候便不再与 http://127.0.0.1:5000 同域, 而是与 Nginx 服务同域了,也就是说前端这时候对 Nginx 服务发起请求就不会再是跨域。
访问 Nginx 我们可以实现了,接下来要做的就是对请求进行代理转发。
前端的 Ajax 请求是这样的:
Nginx 需要对该请求进行拦截,所以可以做如下配置:
location配置的意思是对包含 “cross_origin” 请求拦截,并对请求路径进行重写,一开始请求路径是 “/cross_origin/get_json?type=20170126” ,重写后便成了 “/get_json?type=20170126”,$1代表(.*)中的内容,而(.*)则代表 cross_origin 后面的全部字符,也就是我们会把 cross_origin 部分去掉,但是保留 cross_origin 之后的所有字符,当然这一步并非是绝对必要的。接下来我们再把请求代理到 3000 端口所对应的资源服务,所以请求路径从一开始的 “127.0.0.1:8080/cross_origin/get_json?type=20170126” 经过代理服务器Nginx之后变成了 “127.0.0.1:3000/get_json?type=20170126”。在 “127.0.0.1:3000” 下存在对应的接口:
现在在前端触发点击事件发起请求:
得到了响应,至此我们成功发起了跨域请求。