跨域及其解决方案

为什么会有跨域?

出于安全性考虑,浏览器限制脚本内发起的跨源HTTP请求。例如,XMLHttpRequest和Fetch API遵循同源策略。这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

同源策略:如果两个URL的protocol(协议)、port(端口,如果有指定的话)和host(域名)都相同的话,则这两个URL是同源。

跨域解决方案

1. JSONP

在CORS之前,开发人员也有跨域请求资源的需求,他们提出了多种方案,其中JSONP为常见的一种。JSONP作为一种古老的方案已经不被推荐在项目中使用,但其优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据,我们也有必要了解下其实现原理,开阔开发思路。

原理: 所有具有src属性的HTML标签都是可以跨域的。在浏览器中,<script>、<img>、<iframe>和<link>这几个标签是可以加载跨域(非同源)的资源的,并且加载的方式其实相当于一次普通的GET请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。

JSONP的缺点:

  • 只能实现get一种请求方式。
  • JSONP在调用失败的时候前端不能获取各种HTTP状态码。
  • 安全性问题。万一假如提供JSONP的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个JSONP的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用JSONP的时候必须要保证使用的JSONP服务必须是安全可信的。

利用script标签天生具有跨域能力和脚本执行的能力的原理,前后端约定好一个函数名callbackName,使用script标签发送请求,服务器端获取数据res后响应请求返回callback(res)格式的脚本,则会在客户端将res作为参数执行函数callback,至此,客户端获取到了接口的返回值res。

先实现一个简单的JSONP。

jsonp_v1版本

服务器示例代码(node) http://xxx.xxx.com

router.get('/test', async ctx => {
    const { keyword, callback } = ctx.query;
    let res = await testController.getData({ keyword });
    ctx.body = `${callback}({
      code: 1,
      data: ${JSON.stringify(res)},
      msg: '请求成功'
    })`;
});

前端示例代码

// 先处理下参数
const getEncodeParams = (data = {}) => {
  let res = []
  for (let key in data) {
    res.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
  }
  return res.join('&');
}
function jsonp_v1 (url, data, callbackFunc) {
  let elem = document.createElement('script');
  elem.type = 'text/javascript';
  elem.src = `${url}?${getEncodeParams(data)}&callback=callbackFunc`;
  document.body.appendChild(elem);
  window.callbackFunc = callbackFunc;
}
// 发送请求
jsonp_v1('http://localhost:3000/tool/test', { keyword: 'hello-world' }, function (res) { // 服务器处理请求后会返回"调用这个函数的脚本"
  alert(res)
});

缺点:发送多个请求时,window.callbackFunc会被重写,例如运行以下代码,我们期望alert一次,console一次,结果却是console两次。

jsonp_v1('http://localhost:3000/tool/test', { keyword: 'hello-world' }, function (res) { // 服务器处理请求后会返回"调用这个函数的脚本"
  alert(res)
});
jsonp_v1('http://localhost:3000/tool/test', { keyword: 'hello' }, function (res) { // 服务器处理请求后会返回"调用这个函数的脚本"
  console.log(res)
});
jso
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值