ajax 跨域请求api_浏览器同源策略--跨域解决方案

6443d11edfddd075c5eacd47ea5ff10d.png

开发中一定会碰到的问题,前后端数据交互,请求跨域,然后请求被拦截。那么为什么会被拦截呢?下面就让我们一起来揭开浏览器同源策略的这层面纱。

一、跨域

跨域形成的原因是:违背了浏览器的同源策略,所以浏览器出于保护,会拦截请求。

二、同源策略

同源策略是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS,CSRF等攻击。所谓源(origin)其实就是URL,指得就是:协议+域名+端口,所以即便两个不同的域名指向同一个ip地址,也非同源。

URL解析:

5dca082188d9270bc7b3401f50bab3c4.png

因此同源策略通俗的将就是:浏览器出于对网站安全性的考虑,限制了不同源之间资源相互访问的一种政策。

同源策略限制的内容:

  • Cookie、LocalStorage、IndexedDB(是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs)) 等存储性内容
  • 无法获取DOM元素并进行操作。
  • AJAX 请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

三、常见跨域场景

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示:

6c717a4aa2b6c43176b2cfaa891044da.png

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”

那么这里我们是否有一个疑惑,请求跨域了,那么请求到底发送出去了吗?

首先我们要确定的是:跨域并不是请求发不出去,请求能发出去,服务端也能接收到并且正常返回数据,其次就是返回的数据是被浏览器拦截了。

四、跨域的解决方案

1、jsonp

jsonp是跨域领域中历史非常传统的一种方法。上面 我们有提到一些标签是不受跨域限制的,这里使用的就是script标签,

实现流程:

  1. 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  2. 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  3. 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')
  4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。

// index.html
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'Iloveyou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})

// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // Iloveyou
  console.log(callback) // show
  res.end(`${callback}('我不爱你')`)
})
app.listen(3000)

当然,如果你是用类似jquery这样的库,其中的$.ajax本身是封装了JSONP方式的:

$.ajax({
    url: 'http://127.0.0.1:3000/info/jsonp?cb=myCallback',
    dataType: 'jsonp', // 注意,此处dataType的值表示请求使用JSONP
    jsonp: 'cb', // 请求query中callback函数的键名
}).done(function (res) {
    alert(JSON.stringify(res, null , 2));
});

JSONP作为一个久远的方法,其最大的优点就是兼容性非常好。

缺点:由于是通过script标签发送的请求,所以只支持get请求,并且前后端的开发改造要多一点,如果跨域服务端不支持改造,那么也无法使用该方法,限制因素很多。

2、CORS

CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求复杂请求

简单请求:

只要同时满足下面的两大条件,就属于简单请求:

条件一:

  • GET
  • HEAD
  • POST

条件二:Content-Type 的值仅限于下列三者之一

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

复杂请求:

不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

下面的详细介绍就不在一一赘述了,想要了解子段信息或者查看代码块可以参考下面的链接:

九种跨域方式实现原理(完整版)​juejin.im

3、proxy代理

这种方式仍然遵守同源策略,只是转换了一种思路,将请求移至到了服务端。
我们知道,同源政策是浏览器层面的限制。那么,如果我们不在前端跨域,而将“跨域”的任务交给后端服务,是否就规避了同源政策呢?是的。
这就是“代理”。这个代理可以将我们的请求转发,而后端并不会有所谓的同源政策限制。这个“代理”也可以理解为一个同域的后端服务。
优点:不需要第三方服务做任何的修改,只需要在前端的项目中配置proxy代理即可。
缺点:需要你有一个自己的后端服务能够接收并转发请求。如果你进行本地的纯静态页面开发,则需要一些浏览器插件或自动化工具中集成的本地服务器来实现。此外,如果请求包含一些特殊的请求头(例如cookie等等),需要在转发时特殊处理。

到这里就讲述完毕了,总结一下:

  • 讲述了跨域的原因==>同源策略==>浏览器出于对网站的保护机制==>同源指的是:协议+域名+端口号 都必须相同
  • 跨域的解决方案:
    • jsonp最久远的方法
      • 优点:兼容性好,
      • 缺点:因为利用的是script标签,所以只支持get,并且前后端开发改造很多,限制性太强
    • cors:
      • 优点:简单,只需要简单配置相应的请求与响应头信息即可,服务端需要配置Access-Control-Allow-Origin这个字段表明接收那些请求,支持各种类型的请求(get,post,put等等)
      • 缺点:需要对跨域的服务接口进行一定的改造。如果该服务因为某些原因无法改造,则无法实现。不兼容一些“古董”浏览器。
    • proxy代理
      • 优点:简单,服务端不需要修改配置,前端只需配置proxy代理即可,
      • 缺点:需要有一个自己的后端服务接收并转发请求,对于一些特殊的请求头,需要做特殊处理。

参考链接:

浏览器同源策略与ajax跨域方法汇总​www.jianshu.com
4a7531bcec128e3fd76182b1acb5d692.png
九种跨域方式实现原理(完整版)​juejin.im
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值