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端请求的url: 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/ { location / { |
(说明下:/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三种的一种,则是非简单请求了,浏览器又会预检了,解决方式与上面的如出一辙。
发表评论