AJAX跨域介绍
AJAX 跨域访问是用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面
由于安全方面的原因, 客户端js使用xmlhttprequest只能用来向来源网站发送请求,比如在www.readlog.cn下去请求test.readlog.cn的数据,都是不行的。
什么是AJAX跨域问题
- 简单来说,就是前端调用后端服务接口时
- 如果服务接口不是同一个域,就会产生跨域问题
AJAX跨域场景
- 前后端分离、服务化的开发模式
- 前后端开发独立,前端需要大量调用后端接口的场景
- 只要后端接口不是同一个域,就会产生跨域问题
- 跨域问题很普遍,解决跨域问题也很重要
AJAX跨域原因
- 浏览器限制:浏览器安全校验限制
- 跨域(协议、域名、端口任何一个不一样都会认为是跨域)
- XHR(XMLHttpRequest)请求
AJAX跨域问题解决思路
- 浏览器:浏览器取下跨域校验,实际价值不大
- XHR:不使用XHR,使用JSONP,有很多弊端,无法满足现在的开发要求
- 跨域:被调用方修改支持跨域调用(指定参数);调用方修改隐藏跨域(基于代理)
如图:
解决跨域问题
实例
1、新建一个SpringMVC的Maven工程,参考:【Maven】Eclipse 使用Maven创建SpringMVC Web项目,pom.xml文件如下:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <groupId>com.test</groupId> 6 <artifactId>test-ajax-cross</artifactId> 7 <packaging>war</packaging> 8 <version>0.0.1-SNAPSHOT</version> 9 <!-- 定义maven变量 --> 10 <properties> 11 <!-- spring --> 12 <spring.version>4.2.0.RELEASE</spring.version> 13 14 <!-- log --> 15 <commons-logging.version>1.1.3</commons-logging.version> 16 17 <!-- Servlet --> 18 <servlet.version>3.0.1</servlet.version> 19 <jsp-api.version>2.2</jsp-api.version> 20 21 <!-- jstl --> 22 <jstl.version>1.2</jstl.version> 23 <standard.version>1.1.2</standard.version> 24 25 <!-- Tool --> 26 <!-- jackson json包 --> 27 <jackson-databind.version>2.9.7</jackson-databind.version> 28 <jackson-core.version>2.9.7</jackson-core.version> 29 <jackson-annotations.version>2.9.7</jackson-annotations.version> 30 31 <!-- test --> 32 <junit.version>3.8.1</junit.version> 33 34 <!-- jdk --> 35 <jdk.version>1.8</jdk.version> 36 <maven.compiler.plugin.version>2.3.2</maven.compiler.plugin.version> 37 </properties> 38 39 40 <dependencies> 41 42 <dependency> 43 <groupId>org.springframework</groupId> 44 <artifactId>spring-core</artifactId> 45 <version>${spring.version}</version> 46 </dependency> 47 48 <dependency> 49 <groupId>org.springframework</groupId> 50 <artifactId>spring-beans</artifactId> 51 <version>${spring.version}</version> 52 </dependency> 53 54 <dependency> 55 <groupId>org.springframework</groupId> 56 <artifactId>spring-context</artifactId> 57 <version>${spring.version}</version> 58 </dependency> 59 60 <dependency> 61 <groupId>org.springframework</groupId> 62 <artifactId>spring-jdbc</artifactId> 63 <version>${spring.version}</version> 64 </dependency> 65 66 67 <dependency> 68 <groupId>org.springframework</groupId> 69 <artifactId>spring-expression</artifactId> 70 <version>${spring.version}</version> 71 </dependency> 72 73 <dependency> 74 <groupId>org.springframework</groupId> 75 <artifactId>spring-web</artifactId> 76 <version>${spring.version}</version> 77 </dependency> 78 79 <dependency> 80 <groupId>org.springframework</groupId> 81 <artifactId>spring-webmvc</artifactId> 82 <version>${spring.version}</version> 83 </dependency> 84 85 <dependency> 86 <groupId>org.springframework</groupId> 87 <artifactId>spring-tx</artifactId> 88 <version>${spring.version}</version> 89 </dependency> 90 91 92 93 94 <!-- Servlet --> 95 <dependency> 96 <groupId>javax.servlet</groupId> 97 <artifactId>javax.servlet-api</artifactId> 98 <version>${servlet.version}</version> 99 <scope>provided</scope> 100 </dependency> 101 <dependency> 102 <groupId>javax.servlet.jsp</groupId> 103 <artifactId>jsp-api</artifactId> 104 <version>${jsp-api.version}</version> 105 <scope>provided</scope> 106 </dependency> 107 108 <!-- jstl --> 109 <dependency> 110 <groupId>javax.servlet</groupId> 111 <artifactId>jstl</artifactId> 112 <version>${jstl.version}</version> 113 </dependency> 114 115 <dependency> 116 <groupId>taglibs</groupId> 117 <artifactId>standard</artifactId> 118 <version>${standard.version}</version> 119 </dependency> 120 121 122 <!-- jackson json包 --> 123 <dependency> 124 <groupId>com.fasterxml.jackson.core</groupId> 125 <artifactId>jackson-databind</artifactId> 126 <version>${jackson-databind.version}</version> 127 </dependency> 128 129 <dependency> 130 <groupId>com.fasterxml.jackson.core</groupId> 131 <artifactId>jackson-core</artifactId> 132 <version>${jackson-core.version}</version> 133 </dependency> 134 135 <dependency> 136 <groupId>com.fasterxml.jackson.core</groupId> 137 <artifactId>jackson-annotations</artifactId> 138 <version>${jackson-annotations.version}</version> 139 </dependency> 140 141 <!-- test --> 142 <dependency> 143 <groupId>junit</groupId> 144 <artifactId>junit</artifactId> 145 <version>${junit.version}</version> 146 <scope>test</scope> 147 </dependency> 148 149 </dependencies> 150 151 152 <build> 153 <plugins> 154 <!-- define the project compile level --> 155 <plugin> 156 <groupId>org.apache.maven.plugins</groupId> 157 <artifactId>maven-compiler-plugin</artifactId> 158 <version>${maven.compiler.plugin.version}</version> 159 <configuration> 160 <source>${jdk.version}</source> 161 <target>${jdk.version}</target> 162 </configuration> 163 </plugin> 164 </plugins> 165 <finalName>test-ajax-cross</finalName> 166 </build> 167 168 </project>
2、新建一个测试controller,注意返回的是一个json对象,项目中需要加入json依赖
1 package com.test.ajax.cross.controller; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import javax.servlet.http.HttpServletRequest; 7 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 import org.springframework.web.servlet.ModelAndView; 12 13 @Controller 14 @RequestMapping("/test") 15 public class TestController { 16 17 @RequestMapping(value="/get") 18 @ResponseBody 19 public Map getTest(HttpServletRequest request){ 20 Map<String, Object> map = new HashMap(); 21 map.put("data", "TestController getTest()"); 22 return map; 23 } 24 }
3、新建一个测试界面webapp/static/test.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Insert title here</title> 6 <script src="jquery-1.11.3.min.js" type="text/javascript"></script> 7 </head> 8 <body> 9 <h2>测试界面</h2> 10 <a href="#" onclick="get()">发送get请求</a> 11 </body> 12 <script type="text/javascript"> 13 function get(){ 14 $.getJSON("http://localhost:8080/test-ajax-cross/test/get").then(function(result){ 15 console.log(result); 16 $("body").append("<br>" + JSON.stringify(result)); 17 }); 18 } 19 </script> 20 </html>
4、将项目发布到Tomcat中,并访问测试界面,http://localhost:8080/test-ajax-cross/static/test.html
5、修改hosts文件(用来映射域名与IP),将a.com映射到127.0.0.1,在使用地址:http://a.com:8080/test-ajax-cross/static/test.html#,访问,可以看到无法完成ajax请求了
解决方案一(禁止浏览器检查)
禁止浏览器检查,启动浏览器时,添加参数禁止浏览器对ajax做检查
windows采用以下方式打开谷歌浏览器
"C:\Users\UserName\AppData\Local\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir //不知道chrome.exe 地址的话 右键chrome图标-->属性-->如下图-->图中 目标 就是文件的位置了,直接复制出来即可
mac采用以下方式打开谷歌浏览器
//chrome 浏览器 open -a "Google Chrome" --args --disable-web-security --user-data-dir //safari 浏览器 open -a '/Applications/Safari.app' --args --disable-web-security --user-data-dir
输入地址:http://localhost:8080/test-ajax-cross/static/test.html#,进行访问,可以看到能正常完成ajax请求了
解决方案二(使用JSONP)
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
JSONP的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来
当通过<script>元素调用数据时,响应内容必须用javascript函数名和圆括号包裹起来。而不是发送这样一段JSON数据,这就是JSONP中P的意义所在
在实践中,支持JSONP的服务不会强制指定客户端必须实现的回调函数名称,比如handleResponse。相反,它们使用査询参数的值,允许客户端指定一个函数名,然后使用函数名去填充响应。许多支持JSONP的服务都能分辨出这个参数名。另一个常见的参数名称是callback,为了让使用到的服务支持类似特殊的需求,就需要在代码上做一些修改了
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据
1、修改Java后台TestController类,增加jsonp的响应返回
1 package com.test.ajax.cross.controller; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import javax.servlet.http.HttpServletRequest; 7 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 import org.springframework.web.servlet.ModelAndView; 12 13 import com.fasterxml.jackson.core.JsonProcessingException; 14 import com.fasterxml.jackson.databind.ObjectMapper; 15 16 @Controller 17 @RequestMapping("/test") 18 public class TestController { 19 20 private ObjectMapper objectMapper = new ObjectMapper(); 21 22 @RequestMapping(value="/get") 23 @ResponseBody 24 public Map getTest(HttpServletRequest request){ 25 Map<String, Object> map = new HashMap(); 26 map.put("data", "TestController getTest()"); 27 return map; 28 } 29 30 @RequestMapping(value="/getJsonp") 31 @ResponseBody 32 public String getJsonp(HttpServletRequest request) throws JsonProcessingException{ 33 // 与前端约定好回调方法名称,默认是callback 34 String callback = request.getParameter("callback"); 35 Map<String, Object> map = new HashMap(); 36 map.put("data", "TestController getTest()"); 37 String ret = callback+"("+ objectMapper.writeValueAsString(map)+")"; 38 return ret; 39 } 40 }
2、新建一个测试界面webapp/static/test2.html,用于jsonp请求,
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Insert title here</title> 6 <script src="jquery-1.11.3.min.js" type="text/javascript"></script> 7 </head> 8 <body> 9 <h2>测试jsonp界面</h2> 10 <a href="#" onclick="get()">发送get请求</a> 11 </body> 12 <script type="text/javascript"> 13 function get(){ 14 15 $.ajax({ 16 url: "http://localhost:8080/test-ajax-cross/test/getJsonp", 17 dataType: "jsonp", 18 jsonp:"callback", // 默认值是callback,可以修改 19 success: function (result) { 20 console.log(result); 21 $("body").append("<br>" + JSON.stringify(result)); 22 } 23 }); 24 } 25 </script> 26 </html>
3、浏览器中输入地址:http://a.com:8080/test-ajax-cross/static/test2.html#,进行访问
观察:使用F12查看请求
参数callback对应的值是,请求响应后的回调方法
参数_作用,值是随机数,为了是请求不使用缓存
返回内容是:jQuery111306019926935268467_1567783875359({"data":"TestController getTest()"})
注意:JSONP的弊端
-
-
- 服务器需要改动代码支持
- 只支持GET
- 发送的不是XHR请求
-