全面分析ajax跨域访问原因及解决方案

引言

若想自己实践ajax跨域访问的例子,要编写后台服务代码及前台页面代码。网上很多文章都有贴代码都说过怎么解决这个问题,这篇文章主要是分析产生的原因和解决的思路。

什么是ajax跨域问题

前台调用后台服务接口的时候,如果这个接口不是同一个域的就会产生跨域问题。现在的开发模式是前后台分离发展,前后台开发是独立,所以就会出现前台大量调用后台接口的场景,只要不是同一个域就产生跨域问题。
比如我开发的是http://localhost:8080
ajax调用公司的接口http://xxx.com/xxx/xx/x
这就会报跨域错

为什么会发生ajax跨域访问

三个原因,三个同时满足才产品跨域问题
1.浏览器限制
2.跨域
3.是XHR请求

  1. 浏览器限制
    很多人认为发生跨域问题是服务器后台不允许前台调用,其实这个观点是错误的,真正原因是浏览器出于安全考虑,当发现是跨域时会进行校验,校验不通过就会报跨域安全问题。说白了就是浏览器多管闲事,不是服务器后台不允许调用。
    【验证】
    发出请求,报错:跨域安全问题
    在这里插入图片描述
    在network里查看
    在这里插入图片描述
    在这里插入图片描述
    可以看出返回值是200,Respon也是正确的,从这里看出服务器后台没有任何限制并且处理了,是浏览器报错。
  2. 跨域
    协议/域名/端口不一致都是跨域。在本地实践时,前台地址是8081端口,请求的是8080端口,所以说跨域了。虽然域名都是localhost
  3. XHR请求(XMLHttpRequest请求)
    这点很重要。如果不是XHR请求,就算是跨域浏览器也不会报错。
    浏览器只对XHR(XMLHttpRequest)请求有同源请求限制,而对script标签src属性、link标签ref属性和img标签src属性没有这这种限制,利用这个“漏洞”就可以很好的解决跨域请求。JSONP就是利用了script标签无同源限制的特点来实现的,当向第三方站点请求时,我们可以将此请求放在< script >标签的src属性里,这就如同我们请求一个普通的JS脚本,可以自由的向不同的站点请求:
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>

在network里查看
在这里插入图片描述
发现跨域也没关系。
在这里插入图片描述
JQuery的ajax方法对JSONP方法进行了封装,使用起来很方便。
这三个同时满足才有可能发生跨域安全问题

解决思路

针对三个原因解决
在这里插入图片描述

  1. 浏览器限制
    只要有办法让浏览器不去限制,指定参数让浏览器不去做校验,但价值不大,因为需要每个人都做改动,而且这个改动是客户端的改动。客户是不会愿意的- -
  2. XHR请求
    不发这个类型的。解决方案是jsonp。动态创建一个script在script里面发出跨域请求.后面会讲解
  3. 跨域
    jsonp的解决方案有很多弊端,无法满足现在的开发要求。所以jsonp用得越来越少了,那么重点就要放在解决跨域上。
    有两种解决思路,一个是用被调用方修改代码,让它支持跨域,支持基于http协议关于跨域方面的要求而做的修改。也就是说,从A域名调用B域名的时候,在B域名返回的信息里面加入一些字段,告诉浏览器我允许A域名调用,那么浏览器只要通过检验它就不会报跨域安全问题。这叫支持跨域
    如果被调用方不是我们自己公司的,没法修改。这就是第二种思路,从调用方修改,这种思路是隐藏跨域,意思是我们通过一个代理,从浏览器发出去的都是A域名的请求,在代理里面把指定的URL转到B域名里面,在浏览器看来就是同一域名,不会报跨域错误。

方法一:浏览器禁止检查

命令行参数启动:
DOS打开浏览器并加一些参数,参数作用就是禁止做校验。这个新开的浏览器不会报错,所以跨域问题是前台的校验。
用户是不会愿意这样去打开浏览器的,多麻烦
在这里插入图片描述

方法二:jsonp解决

【jsonp是什么】

json的一种补充使用方式,不是官方协议,但也是个协议约定,利用‘script标签请求资源可以跨域’来解决跨域的。

【用jsonp后台需要改动】

先对前端代码改动,主要是dataType那里。
前台代码修改
发现报语法错误,jsonp是动态创建一个script标签,script标签返回的应该是js代码(比如引jquery库返回js代码),但现在服务器后台没有做任何改动,返回的还是一个json对象,所以浏览器把json对象和字符串当作js代码来解析,所报错。所以使用jsonp后台是需要改动的

【jsonp原理】

查看network
在这里插入图片描述
可以看出普通的ajax返回的是json,jsonp返回的是js
在这里插入图片描述
再看jsonp请求后面自动加了callback=一长串,双击点开如下
在这里插入图片描述
发现刚才callback=的一长串就是返回的js代码的函数名。而数据,这个json对象是作为参数返回的。

所以可以看出jsonp的实现原理是:
jsonp是个协议约定。jsonp请求发出时自动加一个callback参数(jq库自己实现),后台发现有callback就知道这是jsonp请求,它就会把返回的数据由json变为js(后台处理),而js的内容就是一个函数调用。函数名为callback值,返回的数据作为返回的参数。

【加深印象】
为了更好理解我们来验证一下
我打开慕课网,再打开network,发现里面只有一个xhr请求
在这里插入图片描述
双击点开
在这里插入图片描述
发现里面是个json对象,在地址后面手动加一个callback参数,如下
在这里插入图片描述
发现此时返回的是一个以wangyue为函数名,json为返回参数的函数
说明其后台有对jsonp的支持。
在淘宝上找到了jsonp的应用
在这里插入图片描述

jsonp有什么弊端

  1. 服务器需要改动代码支持
    那万一接口不是自己的代码,就无能为力了
  2. 只支持get方法
    原理是动态创建script来发出请求,只能用get方法
  3. 发出的不是xhr请求
    有很多新的特性,比如异步事件等。而jsonp没有

方法三:解决跨域

3.1 被调用解决(后台处理)

【思路】:后台在响应头Response Headers增加字段,告诉浏览器我允许跨域调用。

浏览器是先执行后判断,那么浏览器如何判断是跨域请求?
在这里插入图片描述
跨域请求的请求头里多了一个origin字段。这个字段的值是当前的域名信息。
就是说,浏览器发现这个请求是跨域的时候就会加在请求头Request Headers里加一个origin字段,这个字段里的值是当前域名。等请求返回来,它就会检查响应头Response Headers里面有没有允许跨域的信息,如果没有就会报错。
所以在后台进行修改
使其返回一个带有Access-Control-Allow-Methods(允许的方法)和Access-Control-Allow-Origin(允许的域)字段的Response Headers. *是代表所有
在这里插入图片描述

简单请求和非简单请求

之前说浏览器是先执行后判断。
浏览器在发送跨域请求的时候会判断一下是简单请求还是非简单请求,简单请求:先执行后判断。如果是非简单请求,会先发一个预检命令,检查通过之后才会真正把跨域请求发出去。
详见链接:举例说明简单请求与非简单请求

带cookie的跨域

Access-Allow-Control-Origin:*是否满足所有场景
错。不能满足带cookie的跨域请求。
带cookie时候Access-Allow-Control-Origin只能全匹配,不能使用星号,还要加一个access-control-allow-credentials: true这个字段。
比如csdn中的一个请求如下:
在这里插入图片描述
那如果想支持别的域呢?
(后台处理)可以在请求头里得到orgin字段,把它给响应头的Access-Allow-Control-Origin,那这样就可以支持所有域的跨域调用了。

带自定义头的跨域

(后台处理)在请求头里得到access-control-request-headers字段信息,把它给响应头的access-control-allow-headers,那这样就可以支持所有自定义头

nginx和apache

在实际应用里跨域请求都是从调用方的浏览器发送到被调用方的http服务器,http服务器再把请求转到应用服务器,应用服务器处理之后返回到http服务器,http服务器再把响应转回给调用方浏览器。如下图所示
在这里插入图片描述
那就在两个地方可以增加响应头字段,一个时应用服务器用filter增加,另一个是被调用方HTTP服务器增加。前端:需要把调用的域名改一改,需要把请求发送到被调用方的http服务器而不是应用服务器。(之前在应用服务器用filter处理)

虚拟主机:多个域名指向一个服务器,服务器根据不同的域名将请求转到不同的应用服务器。看上去好像有一个主机,其实是多个主机。

以nginx为例,apache思路也是如此只是写法不同

在这里插入图片描述

SPRING框架解决跨域

之前说的解决方案都和具体的框架没有关系。好比filter是每个web应用都有的。如果使用的是spring框架那么解决跨域非常简单。直接加个注解就可以了。
@CrossOrigin
这个注解可以加在类上也可以加在方法上,加在类上就是该类内的所有方法都支持跨域

总结

为支持跨域服务端增加的五个响应头:
access-control-allow-methods:*//支持所有方法
access-allow-control-origin:从origin获取//支持带cookie的所有域
access-control-allow-credentials: true//支持cookie
access-control-allow-headers:从access-control-request-headers获取//支持自定义头
access-control-max-age:3600//一个小时之内的预检命令缓存

可以直接在应用服务器用filter处理增加字段/但大多数实际应用里是在http服务器增加

3.2 调用方解决(前台)

当无法修改被调用方的时候,就可以采取隐藏跨域的解决思路。
在这里插入图片描述
跨域请求经过调用方的http服务器的反向代理转发到被调用方的服务器,在浏览器上面看不到任何的跨域请求。
【反向代理】:你访问同一个域名的两个不同url,它最后会去到两个不同服务器

nginx

要对http服务器nginx/apache进行配置
注意:此处8080是后台地址,8081是前台地址
在这里插入图片描述
再改下前台代码
在这里插入图片描述
把这个改为代理之后的地址
启动nginx,这时客户端不能访问8081端口,让所有的请求经过http服务器,所以要访问a.com
隐藏跨域和支持跨域最大的不同是我们调用的url,隐藏跨域的解决方案上我们调用的url都是本域的所以都是相对地址,而之前支持跨域的解决思路里,这里必须是绝对地址。浏览器看到都是相对地址,都是同一个域的地址,所以不会有任何跨域问题。

apache

给它增加一个虚拟主机,再虚拟主机里把跨域请求做一个代理。打开apache的配置文件,找到虚拟主机的配置文件,增加一个节点
在这里插入图片描述
在这里插入图片描述
再改下前端代码里的url
在这里插入图片描述
打开a.com,没有报错,查看network里的请求地址
在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值