Ajax跨域问题解析

感谢慕课网上的晓风轻老师的详细讲解。写这篇文章的目的是为了自己能做一个学习笔记,加深自己的印象以及方便以后能随时翻阅,也希望能给大家的ajax跨域学习做一个参考和浏览。本人也只是一个IT萌新,会有很多知识不足的地方,如果文中有什么不足或者不对的地方,欢迎留言指出,十分感谢。嘻嘻(#.#)

文章会从问题出现、原因、分析以及Demo搭建实战解决入手,可以根据自己的需要浏览对应的模块。

一、原因

为什么会发生Ajax跨域问题?

1 浏览器限制

浏览器对ajax的限制。 浏览器处于安全的考虑,当浏览器发现你的请求时跨域请求时,会做一些校验,如果校验不通过,则发生跨域调用失败的问题。
服务器中不会有限制,现实处理中,服务器中可能已经将请求正确处理了,前台请求也报200,但页面中会报跨域错误问题。

2 跨域

发出的请求不是本域的就会报这个错误。只要协议 / 域名 / 端口中任意一个不同,浏览器便认为是跨域。

3 XHR(XMLHttpRequest)请求

浏览器会对XHR请求进行校验,对非XHR请求并不会进行校验,所以就算非XHR请求跨域了,也并不会引发错误。

非XHR的请求有很多种。其中,最简单的非XHR请求可使用标签调用请求:
在这里插入图片描述
在这里插入图片描述

以上任何一条都有可能引起跨域问题。


二、环境搭建

搭建两个简易的项目模拟实现调用方与被调用方,实现现实业务场景中的跨域问题。
前端(调用方)与后台(被调用方)分别为两个项目,端口分别为8092(调用方)和8091(被调用方)。

1 前端

前端我使用了jasmine测试框架,简单使用可按照如下编辑
1.官网下载源码:https://github.com/jasmine/jasmine/releases
2.将下载好的源码的lib目录解压到项目中并进行引入:
在这里插入图片描述
3.编写测试页面JS代码

<script>
//统一设置超时时间
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
var baseUrl = "http://localhost:8091/cross";
//测试模块
describe("跨域demo", function() {
		//测试方法
		it("get请求", function(done) {
			var result;
			$.getJSON(baseUrl + "/get1").then(function(jsonObj) {
					result = jsonObj;
				}
			);
			//因为是异步请求,所以需要设置超时时间
			setTimeout(function() {
				expect(result).toEqual({
					"data": "get1 ok"
				});
				//校验完成,通知jasmine框架
				done();
			}, 100);

		});

		it("jsonp请求", function(done) {
			var result;
			$.ajax({
				url: baseUrl + "/get1",
				dataType: "jsonp",
				success:function(json) {
					result = json;
					console.log(json);
				}
			});
			setTimeout(function() {
				expect(result).toEqual({
					"data": "get1 ok"
				});
				done();
			}, 100);
		});
	});
</script>

2 后台

后台使用了Springboot,实际搭建和配置不再细说,以下贴上后台controller代码:
在这里插入图片描述


三、解决思路

1 浏览器

1.1 让浏览器不去做这个限制

浏览器使用命令行参数启动。
找到浏览器执行目录的路径,并在启动时加入如下参数,在该模式下访问即可不进行跨域检查。
在这里插入图片描述
但这个改动需要每一个客户端都需要改动,所以并不推荐。


2 发送非XHR类型的请求 -> JSONP

JSONP(JSON For Pending)JSON的补充使用方式,本质上是利用动态生成Script标签请求资源可以跨域解决。ajax请求类型会因此改变成script类型,所以需要后台也要将返回类型改为script类型,可使用@ControllerAdvise进行统一处理。

实战方案

2.1 编写前端代码

在这里插入图片描述

2.2 增加后台代码处理结果

在这里插入图片描述
该代码会进行处理(下方原理会详解)。

2.3 运行结果

在这里插入图片描述

原理

  1. JSONP发送的请求时script类型而非XHR请求,故浏览器不会对其进行跨域检查
  2. 普通返回类型为JSON,而JSONP返回类型则为Javascript在这里插入图片描述
  3. URL请求中的callback参数,在后台Advise切面中约定(JSONP规范的约定)了接收到该参数时会判定该请求为JSONP请求,便会对其进行相应的处理,将结果返回到以该参数值作为函数名,结果为参数的JS函数返回到前端。在这里插入图片描述
  4. 动态生成script标签,使用完成后会自动销毁(可通过对JQuery打断点查看具体实现)在这里插入图片描述在这里插入图片描述
  5. callback后的参数【_=***】,是为了防止浏览器缓存,可在ajax请求中加入cache:true允许缓存。

缺点

  1. 只支持get方法
  2. 后台也需要进行相应的改动,如果被调用方是第三方代码不可修改,此方法就不可行
  3. 缺少XHR新特性如异步、事件等
  4. JSONP方式在spingboot2.0上不推荐使用,在springboot2.0以上该方法是不可使用的

3 跨域问题的解决

​​最常见的J2EE架构

​​常见J2EE架构

3.1 修改被调用方代码,让其支持跨域

原理

被调用方解决跨域:在浏览器中直接请求,需要对被调用方进行修改,基于HTTP协议关于跨域请求的规定,在被调用方返回头中加入几个字段,告诉浏览器允许被跨域调用。

①服务器实现。需要了解HTTP协议关于跨域方面的所有请求,针对不同的场景,返回不同的头。只有知道了所有的响应头,才可进行往下的服务器配置
②Ngnix配置
③Apache配置

实战方案

Spring有非常简洁方便的方法,想快速寻求解决方法的朋友可直接看3.1.3的解决方法!

3.1.1 Filter解决方案

在这里插入图片描述
修改服务调用端,通过添加返回头完成跨域响应。

简单请求(后有详细介绍)浏览器对请求是先执行,后判断。如果是跨域请求,浏览器会在请求头中新增一个origin的字段,用于保存当前域名信息,当请求返回时,会查看返回头中有没有匹配的跨域信息,如果没有,则报错。通过Filter在【服务器端】返回头中增加参数允许跨域。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

请求头说明:

请求头说明
Access-Control-Allow-Origin允许这个域跨域,允许*通配符,但带cookie时必须全匹配,并加上Access-Control-Allow-Credentials=true的请求头
Access-Control-Allow-Methods制定允许的方法(HEAD / POST / GET / PUT / DELETE / * )
Access-Control-Allow-Header预检命令中出现这个头,假如值为content-type,就会检查服务器返回头中是否存在相关信息,如果不通过则跨域失败
Access-Control-Max-Age在指定时间内缓存预检命令结果
Access-Control-Allow-Credentials默认为空,当代cookie进行跨域时需设置为true

简单请求和非简单请求
不是所有的请求都是先执行后判断是否跨域。浏览器进行跨域请求的时候,会判断请求是否是简单请求。

简单请求: 先执行后判断。
常见的简单请求:

方法请求Header中
GET / HEAD / POST无自定义头(自定义头在下方有详解) 或 Content-Type = text-plain 、multipart/form-data、application/x-www-from-urlencoded

非简单请求: 先发送预检命令,检查通过后,才会真正发送请求。
常见的非简单请求:

  1. PUT / DELETE 方法的AJAX请求
  2. 发送JSON格式的AJAX请求
  3. 带自定义头的AJAX请求

以下ajax为非简单请求(发送JSON格式的数据)
在这里插入图片描述
以上实例需要加Access-Control-Allow-Header:content-type 才可通过跨域请求。

带cookie跨域访问
在这里插入图片描述
xhrFields: {withCreentials:true}—表示发送ajax请求时会带上cookie

Access-Control-Allow-Origin:带cookie时,该请求头需要设置调用方的域
Access-Control-Allow-Credentials:带cookie跨域时必须为true

cookie加在被调用方(服务器端)。即从本域cookie请求到被调用方。(只能调用到本域的cookie)
为了让代码能更加灵活,在filter中获取requestHeader中的origin字段(跨域时新增字段,为当前请求域地址),并设置到Access-Control-Allow-Origin中,即可实现动态设置跨域地址。
在这里插入图片描述

带自定义头的跨域
自定义头的两种方式:
在这里插入图片描述
在这里插入图片描述
后台接收方式:
在这里插入图片描述
过滤器处理方式:
通过动态获取request中的header加入response的头中,即可通过浏览器预检。
在这里插入图片描述

3.1.2 Nginx解决方案

原理:
在这里插入图片描述
服务器端使用Nginx代理,统一处理域名请求,nginx接收到请求时进行转发,即可完成跨域。

Nginx主要功能:
	①处理静态文件
	②负载均衡
虚拟主机的概念:
	多个域名指向同一个服务器,服务器根据不同的域名指向不同的服务器,看似有多台主机,实际上只有一台主机在做这个负载均衡。

实战步骤:
①配置hosts文件,实现本地域名映射
在这里插入图片描述

②下载Nginx,并在config文件夹中新建vhost文件夹。并新建一个b.com.conf(hosts配置的映射域名)。
在这里插入图片描述

③在b.com.conf中按照nginx语法(详细请看nginx语法介绍)加入如下代码:

server{
	listen 80;	#监听端口
	server_name b.com;	#监听地址
	
	location /{
		proxy_pass http://localhost:8091/;	#请求转向的服务器列表
		
		add_header Access-Control-Allow-Methods *;	#添加请求头语法
		add_header Access-Control-Max-Age 3600;
		add_header Access-Control-Allow-Credentials true;
		
		add_header Access-Control-Allow-Origin $http_origin;
		add_header Access-Control-Allow-Headers 
		$http_access_control_request_headers;
		
		if ($request_method = OPTIONS){
			return 200;
		}
	}
}

④在nginx/config目录下找到nginx.conf文件,并在最后加上该代码:
在这里插入图片描述

⑤在nginx目录下的cmd命令输入nginx.exe -t可测试配置是否正确
在这里插入图片描述

⑥在nginx目录下的cmd命令输入start nginx.exe即可启动nginx服务
在这里插入图片描述
⑦在被调用方加入cookie,修改前端代码中的调用地址为b.com。测试用例即可全部成功(测试之前记得先把被调用方的过滤器Bean给注释掉后重启)。
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

3.1.3 Spring框架解决方案

在类或方法中加入 @CrossOrigin注解即可。
在这里插入图片描述


3.2 调用方修改代码

在这里插入图片描述
隐藏跨域的使用:请求从HTTP服务器中转出,通过服务器进行转发后,浏览器会发现所有的请求都是同一个域,就不会进行跨域检查了。

3.2.1 Nginx解决方案

通过nginx的反向代理机制进行配置,使接口访问的时候让浏览器看起来只是访问本域的接口,从而实现跨域问题。
反向代理:
①配置hosts文件(a.com为请求方域名)
在这里插入图片描述

②nginx/vhost配置中添加c.com.conf配置文件,并加上以下配置。
在这里插入图片描述

server{
	listen 80;
	server_name a.com; # 调用方配置域名
	
	location / {
		proxy_pass http://localhost:8092/; # 调用方地址
	}
	
	location /ajaxserver{ # 代理,把要调用的服务器地址代理为/ajaxserver
		proxy_pass http://localhost:8091/cross/; # 被调用方地址
	}
}

③请求地址修改为/ajaxserver。
在这里插入图片描述

④重新加载nginx配置文件,并启动nginx。
在这里插入图片描述

⑤重启服务后在浏览器中进行页面访问http://a.com/page/cross.html,会发现请求除了cookie请求外都能成功。cookie在a.com域名下手动添加数据即可通过。
在这里插入图片描述
在network中查看请求可发现,请求地址均为a.com发出,通过相对地址进行请求,nginx通过相对地址进行反向代理,即可解决跨域问题。

END

后记:一看就会,一学就废。不动手之前什么事情都是容易的啊。。。以后还是要多动动手才行,否则实战上遇到的问题只会更多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值