1.1. 什么是跨域访问
什么是跨域请求?
Javascript的Ajax异步请求,如果请求的url不是和发出请求的同一个域的话,就是跨域请求。
即在一个域下的项目通过ajax去访问另外一个域下的数据。
什么算一个域?
ip或域名、端口:必须一样的url,算一个域,只要有一个不一样,则不是一个域。
结合上面来说,
如果请求源地址:127.0.0.1:9001,目标地址127.0.0.1:9002或者127.0.0.2:9001,则都是跨域请求。
那么跨域访问问题就是:
Ajax编程中,浏览器对跨域数据是屏蔽的,即无法获取另外一个域名下的数据。
1.2. 跨域问题的演示
建立两个不同源的项目,分别作为服务端和客户端。
服务端代码参考:
首先创建一个maven的服务端工程,pom如下
4.0.0
cn.gyd.ajaxkuayu
serverproject
0.0.1-SNAPSHOT
war
serverproject
服务端web项目
javax.servlet
servlet-api
2.5
provided
org.codehaus.mojo
tomcat-maven-plugin
1.1
8001
新建一个servlet
public class ServerDataServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print("{\"username\":\"小红\"}");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
启动服务:
服务端测试:通过浏览器来访问servlet,响应页面可以获取到数据:
说明服务端没问题。
在服务端添加异步代码:
Insert title here
测试,没问题:
现在是没有问题的,都是在一个域下。
新建一个maven的客户端程序,pom文件如下
4.0.0
cn.gyd.ajaxkuayu
clientproject
0.0.1-SNAPSHOT
clientproject
跨域演示客户端
org.codehaus.mojo
tomcat-maven-plugin
1.1
8002
在客户端添加异步代码,向服务端发起请求:
Insert title here
启动客户端程序:
客户端测试:浏览器访问客户端的client.html时,
浏览器的控制台中出现错误警告信息:
火狐:
谷歌:
错误的意思是说异步请求时,不允许进行跨域的访问。
说明客户端无法获取数据。
1.3跨域问题出现的原因
1.为什么会有跨域问题的存在?
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,即同源政策。
2.同源策略是什么?
1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
(1)协议相同
(2)域名相同
(3)端口相同
具体实例 比如:http://www.example.com/zb/index.html这个网站,它的协议是http://,域名是www.example.com,端口号是80(默认端口可以省略),它的同源情况如下:
3.同源政策的目的:
同源策略的目的是为了保证用户信息的安全。防止恶意的网站盗取数据。
设想这样一个情景:A网站是一家银行,用户登录以后,又去浏览其他的网站B,如果网站B可以读取A网站的Cookie,会发生什么问题?
显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄露,更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒用户,为所欲为。因为浏览器同时还规定,提交表单不受同源策略的限制。
由此可见,“同源政策”的必要性,否则Cookie可以共享,互联网就毫无安全可言了。
4.非同源限制范围
随着互联网的发展,“同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。
(1)Cookie、LocalStorage和IndexDB无法获取。
(2)DOM无法获得。
(3)AJAX请求不能发送。
虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面将介绍如何规避上面三种限制。
通过CORS解决AJAX跨域
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。CORS允许任何类型的请求。
维基百科上的定义是:跨域资源共享(CORS)是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。而W3C的官方文档目前还是工作草案,但是正在朝着W3C推荐的方向前进。
简言之,CORS就是为了让AJAX可以实现可控的跨域访问而生的。
CORS与JSONP相比,有优点和缺点:
优点:
缺点:
老的浏览器往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS,对于考虑老的兼容性问题,JSONP有一些优势。
因此,要想实现CORS,客户端浏览器必须支持CORS,而服务器端也需要设置响应的头信息,即设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
2.CORS实现代码参考
public class ServerDataServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//--------解决方案1:CORS--设置服务器响应的头信息
//1)配置单个允许的请求地址
// response.setHeader("Access-Control-Allow-Origin", "http://localhost:9003");
//2)配置多个允许的请求地址
// String [] allowDomain= {"http://localhost:9003","http://www.abc.com","http://132.12.11.11:8888"};
// Set allowedOrigins= new HashSet(Arrays.asList(allowDomain));
// String originHeader= request.getHeader("Origin");
// if (allowedOrigins.contains(originHeader)){
// response.setHeader("Access-Control-Allow-Origin", originHeader);
// }
//3)配置允许所有的请求地址
response.setHeader("Access-Control-Allow-Origin", "*");
//设置允许的请求方式(可以省略)
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setContentType("application/json;charset=utf-8");
response.getWriter().print("{\"username\":\"小红\"}");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
通过JSONP解决AJAX跨域
JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个