浏览器的跨域问题的出现以及几种常见的解决跨域问题方案

 

1.    跨域访问问题的引出和分析

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如下

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.gyd.ajaxkuayu</groupId>
  <artifactId>serverproject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>serverproject</name>
  <description>服务端web项目</description>
  <dependencies>
  	<dependency>
  		<groupId>javax.servlet</groupId>
  		<artifactId>servlet-api</artifactId>
  		<version>2.5</version>
  		<scope>provided</scope>
  	</dependency>
  </dependencies>
  <build>
  	<plugins>
  		<!-- tomcat插件 -->
  		<plugin>
  			<groupId>org.codehaus.mojo</groupId>
  			<artifactId>tomcat-maven-plugin</artifactId>
  			<version>1.1</version>
  			<configuration>
  				<!-- 端口号 -->
  				<port>8001</port>
  			</configuration>
  		</plugin>
  	</plugins>
  </build>

新建一个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,响应页面可以获取到数据:

说明服务端没问题。

在服务端添加异步代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- 引入jquery -->
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script type="text/javascript">
	//在一个域下面的异步请求
	$.get("http://localhost:8001/serverproject/serverdataservlet",function(data){
		alert(data);
		console.log(data);
	});

</script>
</head>
<body>

</body>
</html>

测试,没问题:

现在是没有问题的,都是在一个域下。

新建一个maven的客户端程序,pom文件如下

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.gyd.ajaxkuayu</groupId>
  <artifactId>clientproject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>clientproject</name>
  <description>跨域演示客户端</description>
   <build>
  	<plugins>
  		<!-- tomcat插件 -->
  		<plugin>
  			<groupId>org.codehaus.mojo</groupId>
  			<artifactId>tomcat-maven-plugin</artifactId>
  			<version>1.1</version>
  			<configuration>
  				<!-- 端口号 -->
  				<port>8002</port>
  			</configuration>
  		</plugin>
  	</plugins>
  </build>
</project>

在客户端添加异步代码,向服务端发起请求:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- 引入jquery -->
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script type="text/javascript">
	//跨域的异步请求
	$.get("http://localhost:8001/serverproject/serverdataservlet",function(data){
		alert(data);
		console.log(data);
	});

</script>
</head>
<body>

</body>
</html>

启动客户端程序:

客户端测试:浏览器访问客户端的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(默认端口可以省略),它的同源情况如下:

  • http://www.baidu.com/zhangbo/manager.html    同源
  • https://www.baidu.com/zb/index.html   不同源(协议不同)
  • http://baidu.com/zb/index.html  不同源(域名不同)
  • http://www.baidu.com:81/zb/index.html   不同源(端口号不同)

3.同源政策的目的:

同源策略的目的是为了保证用户信息的安全。防止恶意的网站盗取数据。

设想这样一个情景:A网站是一家银行,用户登录以后,又去浏览其他的网站B,如果网站B可以读取A网站的Cookie,会发生什么问题?

显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄露,更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒用户,为所欲为。因为浏览器同时还规定,提交表单不受同源策略的限制。

由此可见,“同源政策”的必要性,否则Cookie可以共享,互联网就毫无安全可言了。

4.非同源限制范围

     随着互联网的发展,“同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。

  (1)Cookie、LocalStorage和IndexDB无法获取。

  (2)DOM无法获得。

  (3)AJAX请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面将介绍如何规避上面三种限制。

2.跨域问题的解决方案

通过CORS解决AJAX跨域

  1. 概述

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。CORS允许任何类型的请求。

维基百科上的定义是:跨域资源共享(CORS)是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。而W3C的官方文档目前还是工作草案,但是正在朝着W3C推荐的方向前进。

简言之,CORS就是为了让AJAX可以实现可控的跨域访问而生的。

CORS与JSONP相比,有优点和缺点:

优点:

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起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跨域

  1. 概述

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

    2.实现代码参考

客户端代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="../js/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
	//异步请求
	/* $.get("http://localhost:9080/bos_management/servletdataservlet",function(data){
		alert(data);
		console.log(data);
	}); */
	//跨域请求jsonp
	$.getJSON("http://localhost:9080/bos_management/servletdataservlet?callback=?",function(data){
		alert(data);
		console.log(data);
	});
</script>
</head>
<body>

</body>
</html>

服务端参考代码:  

public class ServerDataServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//----------解决方案2:JSONP
		String callback = request.getParameter("callback");
		response.setContentType("application/json;charset=utf-8");
		response.getWriter().print(callback+"({\"username\":\"小红\"})");
		
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

    3. $.getJSON的原理

 

 

客户端使用jQuery的$.getJSON方法后发生了什么?

客户端请求的URL会自动在callback的值中添加一个随机生成的js函数:

http://localhost:9080/bos_management/servletdataservlet?callback=jQuery2240793577231178939_1512798393631&_=1512798393632

服务端获取客户端请求URL的参数callback的值,即函数的名字:

将其拼接到响应的结果中:

服务端响应的内容其实是一个js调用的函数:

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值