Skip to content

跨域问题

浏览器的同源策略

首先了解下浏览器的同源策略 同源策略SOP(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSSCSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源

存在跨域问题的场景:

  1. 资源跳转: A链接、重定向、表单提交
  2. 资源嵌入: <link>、<script>、<img>、<iframe>等dom标签,还有样式中background:url()、@font-face()等文件外链
  3. 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

同源策略限制以下几种行为: 1.) Cookie、LocalStorage 和 IndexDB 无法读取 2.) DOM 和 Js对象无法获得 3.) AJAX 请求不能发送

请求跨域

  1. jsonp跨域

原理:利用script标签加载完成后会自动执行的特性,后端返回携带数据的代码,触发执行前端已有的函数

缺点:只能实现get请求

js
// 动态创建一个script
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
document.head.appendChild(script);

// 回调执行函数
function handleCallback(res) {
    alert(JSON.stringify(res));
}
js
// 服务端返回
handleCallback({"status": true, "user": "admin"})
  1. 跨域资源共享(CORS)

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

cors规定了三种不同的交互模式,三种模式的影响越来越大,要求也越来越严格

  • 简单请求
    • 条件
      • get post head
      • 没有额外的自定义字段
      • content-type: text/plain或multipart/form-data/application/x-www-form-urlencoded
    • 规范
      • 请求头中自动添加Origin
      • 服务器响应头包含Access-Control-Allow-Origin
  • 需要预检的请求
    • 浏览器发送预检请求(不会实际显示出来),询问服务器是否允许
      • OPTIONS
    • 如果允许,再发送实际请求
  • 附带身份凭证的请求: 默认情况下跨域不会附带cookie
    • 如果要携带
      • xhr: withCredentials: true
      • fetch: credentials: 'include'
    • 会发送预检请求
      • 允许服务器设置Access-Control-Allow-Credentials: true
      • 这种模式下,服务器不得设置Access-Control-Allow-Origin*

补充 在跨域时js只能拿到一些基础的响应头,Access-Control-Expose-Headers设置允许浏览器访问的头的白名单`

  1. 代理跨域
  • 通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
  • 开发时采用的proxy也是代理跨域
  1. WebSocket协议跨域

WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

跨标签页通信

多个标签页之间的通信https://kaifeiji.cc/post/interactions-between-windows-postmessage-and-broadcastchannel/

  1. Broadcast Channel API: 广播
  2. postMessage: 一般用于定向传递数据 用法:postMessage(data,origin)方法接受两个参数
  • data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
  • origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
  1. service worker
  2. sharedWorker
  3. localStorage: window.onstorage事件监听
  4. cookie\indexdb轮询
  5. websocket: 需要服务器中转

其它(了解)

  1. document.domain + iframe

此方案仅限主域相同,子域不同的跨域应用场景

原理:页面和页面内iframe都通过js强制设置document.domain为基础主域,就实现了同域

更多

  • 前端

    • 原生 ajax : axios.defaults.withCredentials = true
    • axios:axios.defaults.withCredentials = true
  • 服务端

    • javascript
      'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
      'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)

nginx 代理跨域

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

苏ICP备2025160170号-1 | 前端进化之路 | Released under the MIT License.