解决跨域--jsonp、nginx、cors三种方式

from:http://blog.abreaking.com/c/jjkyjncszfs

一、什么是跨域

如果存在协议、域名、端口或者子域名不同服务端,都会算作跨域。一般来讲,浏览器为了安全的问题都是限制了跨域的访问,因而,有时候就需要想方法绕过浏览器的同源策略了。

二、使用jsonp解决的跨域问题

   根据jquery的文档:jsonp是json的一种扩展,它要求服务端的代码来检测并处理查询字符串参数。如果指定了script或者jsonpl类型,那么当服务器接收到数据时,实际上就是用了<script>标签而不是XHR(xmlHttpRequest)对象,在这种情况下,$.ajax()不再返回一个XHR对象,并且也不会 传递事件处理函数。。    
    可以看到,非跨域请求的url直接就是你请求的的url,请求成功返回json数据。。
    跨域请求的url格式为:url?callback=?    这里的callback为你的回掉函数名,cxf默认使用“_jsonp”,后面跟着的可以看作是一个标志串吧    ,而后服务器返回的格式:该回掉函数名(json数据)。。。拿到返回的数据后,jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。需要注意的是,jQuery中需要指定参数dataType:"jsonp", jsonp:"_jsonp"。
    值得一提是:Jsonp是一种比较古老的方式,而且有很大的局限性:只能是get请求,不能发送post请求,也就是说只能去获取数据,而不能发送数据到服务器。所以,不怎么建议使用这种方式。


三、使用Nginx反向代理实现跨域请求

借鉴:https://www.cnblogs.com/tianheila/p/6139241.html
    先补充一下我之前的那个问题,其实很简单,在http://127.0.0.1:80端,我通过ajax请求访问http://127.0.0.1:7777下的服务。即两个端口不一致,故为跨域请求。80端请求的http://127.0.0.1:7777/manager/ws/poem/get/all。。
    具体思路是:nginx监听8080端口。。80端不再直接对资源服务器发起请求了,而是改为直接访问Nginx服务器。其实在这里最初我也是挺疑惑的:难道80端对ngnix发起的访问效果不是和直接访问7777端效果一样吗? 后来我才明白使用ngnix,这时访问该页面就不在是http://127.0.0.1:80/poem了,而是直接是:http://127.0.0.1:8080/poem了。。
   在nginx里需要作一些配置:(下划线就是需要新增配置的)
 

location ^~/cross_origin/ {
  rewrite  ^/cross_origin/(.*)$ /$1 break;
  proxy_pass http://127.0.0.1:7777/;
  }

  location / {
      root   html;
     proxy_pass  http://127.0.0.1:80;
     index  index.html index.htm;
    }


(说明下:/cross_origin/是我待会要在原url中新增的(纯表示标识),上面表示将/cross_origin/ xxxx/xxx的url截取为xxx/xxx,其实这一步并非必要,只是纯个人注意所用)
    在原来的80端的ajax的url中修改为:/cross_origin/manager/ws/poem/get/all。。再到浏览器访问,就OK了。。
其实这种方式就是利用服务器之间的资源请求是不会有跨域限制了。。ngnix就是将两个,不管是consumer还是provider,一律看作provider,从而实现跨域请求吧。

四、使用cors解决跨域

         关于cors,建议参考:http://www.ruanyifeng.com/blog/2016/04/cors.html

CORS:cross origin resource sharingà跨域资源共享。它是一种W3C的标准,允许浏览器向跨院服务器发出XMLHttpRequest请求,从而能客服ajax同源的限制。此外,IE10以上的浏览器方能支持。。

         浏览器将cors请求分为两类:简单请求、非简单请求。

同时满足以下两大条件,就属于简单请求:

条件一:请求方式不超过以下三种方法之一:

l  HEAD

l  POST

l  GET

条件二:HTTP头信息不超出以下几种字段:

l  Accept

l  Accept-Language

l  Content-Language

l  Last-Event-ID

l  Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

反之就为非简单请求。(具体可参考我给出的链接) 

         对于ajax请求,因为ajax本身就是本身就是伴随着跨域的,跨服务器请求数据,那么就得需要服务器的同意。为此,在服务器端,需要设置:

response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080";);

 (注意:后面只能是你的域名,而不是url路径,域名可以是为*,代表接受所有的域请求)

此外,还有几个可选字段:

         Access-Control-Allow-Credentials:表示是否发送cookie,默认情况下,cookie是不包含在CORS中的。若要发送cookie到服务器端,一方面需要服务器端同意:

response.setHeader("Access-Control-Allow-Credentials", "true");

另一方面需要客户端同意:

(JQuery的ajax)

xhrFields:{

         withCredentials:true       //可以发送cookie

},

(纯生态ajax,以后如是)

var xhr = new XMLHttpRequest();

xhr.withCredentials = true;

         Access-Control-Expose-Headers表示允许在相应头里面暴露什么头信息。如:在服务器端设置:

response.setHeader("foo", "abcd");

response.setHeader("Access-Control-Expose-Headers", "foo ");

(表示允许暴露请求头foo)

可以在客户端通过XMLHttpRequest.getResponseHeader()方法获取:

XMLHttpRequest.getResponseHeader("foo")

         Access-Control-Allow-Headers表示请求头信息。。

请注意:之前说过简单请求头信息不超过那五种字段,是为简单请求,故一般我们的请求都是简单请求。如果在Access-Control-Allow-Headers设置自己的信息,如在ajax里:

headers:{"abc":"value"}, //这样总是不行

或者:

XMLHttpRequest.setRequestHeader(“abc”,”value”)

明显“abc”是不在之前说的那几种类型的,故是为非简单请求。

         对于非简单请求,浏览器 会进行两次请求,(你可以直接通过debug浏览器看到,这也是区分简单请求和非简单请求的一种方式)

第一次请求:

你会发现方法为options,这也叫做浏览器的预请求。。服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应,如下:

怎么作出如上的回应呢?我是用的java的servlet接收,你必须要写个filter(只能是filter、filter、filter,重要事情说三遍!!!直接在你的Servlet里是没用的),如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

                   // TODO Auto-generated method stub

                   // place your code here

                   HttpServletResponse response1 = (HttpServletResponse) response;            

                   response1.setHeader("Access-Control-Allow-Methods", "get,post,options");

                   response1.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080";);                 

                   response1.setHeader("Access-Control-Allow-Headers", "abc");

                   // pass the request along the filter chain

                   chain.doFilter(request, response);

         }

Tips:否则你会发现会报如下错误:

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'

 第二次请求:预检通过后就是正常的Post发送请求了。。

          Access-Control-Allow-Methods表示允许的方法,如果客户端不是get、post、head三种的一种,则是非简单请求了,浏览器又会预检了,解决方式与上面的如出一辙。

猜你喜欢

转载自blog.csdn.net/abreaking2012/article/details/81069251