跨域,不可不知的基础概念

16,049 阅读7分钟

跨域这两个字在前端开发者身上都会碰到的,无论你在工作上或者面试中无可避免会遇到这个问题。尤其是最初搭建项目的时候。但单纯只是为了面试背几个方案,也无足轻重,重要是理解透彻,懂得跨域的前因后果,以及当下最为方便快捷的解决方案,才是上上策。而不是当时网上搜几篇文章看着将项目配置一下问题解决了,然后就将它抛之脑后了。日子虽然可以照常混,但是下次再遇到这个问题的时候又是一顿乱搜,效率实属不高,面试的时候也是要再次查找各种资料去了解。

为了全面的了解跨域的前世今生,彻底搞懂这个东西!于是就有了这篇文章。

为什么会存在跨域这个问题

因为存在浏览器同源策略,所以才会有跨域问题。那么浏览器是出于何种原因会有跨域的限制呢。其实不难想到,跨域限制主要的目的就是为了用户的上网安全。

没有同源策略限制的两大危险场景

如果浏览器没有同源策略,会存在什么样的安全问题呢。下面从 DOM 同源策略和 XMLHttpRequest 同源策略来举例说明:

如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

  1. 做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com
  2. 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。

如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

  1. 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com 向用户的 cookie 中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
  3. http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。
  4. 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。
  5. 而且由于 Ajax 在后台执行,用户无法感知这一过程。

因此,有了浏览器同源策略,我们才能更安全的上网。

浏览器的同源策略

同源策略(Same Origin Policy,SOP)是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 Java)对不同域的服务进行跨站调用(通常指使用请求)。它能帮助阻隔恶意文档,减少可能被攻击的媒介。这是一个用于隔离潜在恶意文件的关键的安全机制。同源策略机制是一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略。则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础上的,浏览器只是针对同源策略的一种实现。

同源策略是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,比如源a的js不能读取或设置引入的源b的元素属性。

同源的定义

所谓同源是指:两个页面具有相同的域名、协议、端口,三个要素缺一不可。

同源策略带来的问题

  1. 一级域名相同,只是二级域名不同的同一所有者的网页被限制(Cookie、LocalStorage、IndexDB的读取)
  2. 无法跨域发送 AJAX 请求
  3. 无法操作 DOM
  • Q:为什么 Form 表单可以跨域发送请求,而 AJAX 不可以。
    A:因为 Form 表单提交之后会刷新页面,所以即使跨域了也无法获取到数据,所以浏览器认为这个是安全的。而 AJAX 最大的优点就是在不重新加载整个页面的情况下,更新部分网页内容。如果让它跨域,则可以读取到目标 URL 的私密信息,这将会变得非常危险,所以浏览器是不允许 AJAX 跨域发送请求的。

跨域的原理

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的。
同源策略,是浏览器对 JavaScript 实施的安全限制,只要协议、域名、端口有任何一个不同,都被当作是不同的域。
跨域原理,即是通过各种方式,避开浏览器的安全限制

跨域解决方案

最初做项目的时候,使用的是jsonp,但存在一些问题,使用get请求不安全,携带数据较小,后来也用过iframe,但只有主域相同才行,也是存在些问题,后来通过了解和学习发现使用代理和proxy代理配合起来使用比较方便,就引导后台按这种方式做下服务器配置,在开发中使用proxy,在服务器上使用nginx代理,这样开发过程中彼此都方便,效率也高;现在h5新特性还有 windows.postMessage()

  • JSONP
    Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链 接却可以访问跨域的 js 脚本,利用这个特性,服务端不再返回 JSON 格式的数据,而是 返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。

步骤:
1. 去创建一个script标签
2. script的src属性设置接口地址
3. 接口参数,必须要带一个自定义函数名,要不然后台无法返回数据
4. 通过定义函数名去接受返回的数据
//动态创建 script
var script = document.createElement('script');

// 设置回调函数
function callbackFunction(data) {
    console.log(data);
}

//设置 script 的 src 属性,并设置请求地址
script.src = 'https://api.ycsnews.com/test?callback=callbackFunction';

// 让 script 生效
document.body.appendChild(script);

laravel php 后端代码

public function test(Request $request){
    //获取回调函数名
    $callback= $request->callback;
    //服务端返回数据
    $data = [
        [
            'code'=>0,
            'data'=>[1,2,3,4,5]]
    ];
    //动态执行回调函数
    //方法1:
     return response()->jsonp($callback, $data);//接收的方法名和传输的数据
    //方法2:
    // return response()->json($data)->setCallback($callback);
}

image.png JSONP 的缺点: JSON 只支持 get,因为 script 标签只能使用 get 请求; JSONP 需要后端配合返回指定格式的数据。

  • document.domain 基础域名相同 子域名不同

  • window.name 利用在一个浏览器窗口内,载入所有的域名都是共享一个window.name

  • CORS CORS(Cross-origin resource sharing)跨域资源共享 服务器设置对CORS的支持原理:服务器设置Access-Control-Allow-Origin HTTP响应头之后,浏览器将会允许跨域请求。实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

  • proxy代理 目前常用方式。 通俗点说就是客户端浏览器发起一个请求会存在跨域问题,但是服务端向另一个服务端发起请求并无跨域,因为跨域问题归根结底源于同源策略,而同源策略只存在于浏览器

    那么我们是不是可以通过 Nginx 配置一个代理服务器,反向代理访问跨域的接口,并且我们还可以修改 Cookiedomain 信息,方便当前域 Cookie 写入

  • window.postMessage() 利用h5新特性window.postMessage()

  • Websocket

跨域配置

Vue-CLI中代理的多种配置

Vue-CLI 是基于 webpack 的,通过 webpack-dev-server 在本地启动脚手架,也就是在本地启动了一个 Node 服务,来实时监听和打包编译静态资源,由于都是封装好的,只需要配置即可,我们在 vue.config.js 中配置代理如下,写法很多,列几个常见的自行选择

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://www.hahaha.com'
    }
  }
}
复制代码

如上所示时,当你请求 /api/abc 接口时就会被代理到 http://www.hahaha.com/api/abc