跨域问题

为什么会有跨域

由于浏览器的同源策略,限制了非同源资源的交互.这是个安全机制.

同源:
协议: http,https
端口: port
主机: host


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

浏览器是从两个方面去做这个同源策略的,一是针对接口的请求,二是针对Dom的查询.【前端安全】

  • 针对接口的请求
    • csrf跨站请求伪造攻击;
  • 针对Dom的查询
    • iframe操作dom;

跨域的解决方式

  1. JSONP
    在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,可以利用这一点实现get请求跨域,因为标签加载资源就是GET

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    </head>
    <body>
    <script type='text/javascript'>
    // 后端返回直接执行的方法,相当于执行这个方法,由于后端把返回的数据放在方法的参数里,所以这里能拿到res。
    window.jsonpCb = function (res) {
    console.log(res)
    }
    </script>
    <script src='http://localhost:9871/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>
    <!-- cb是前后端约定的方法名字 -->
    </body>
    </html>

    封装一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    /**
    * JSONP请求工具
    * @param url 请求的地址
    * @param data 请求的参数
    * @returns {Promise<any>}
    */
    const request = ({url, data}) => {
    return new Promise((resolve, reject) => {
    // 处理传参成xx=yy&aa=bb的形式
    const handleData = (data) => {
    const keys = Object.keys(data)
    const keysLen = keys.length
    return keys.reduce((pre, cur, index) => {
    const value = data[cur]
    const flag = index !== keysLen - 1 ? '&' : ''
    return `${pre}${cur}=${value}${flag}`
    }, '')
    }
    // 动态创建script标签
    const script = document.createElement('script')
    // 接口返回的数据获取
    window.jsonpCb = (res) => {
    document.body.removeChild(script)
    delete window.jsonpCb
    resolve(res)
    }
    script.src = `${url}?${handleData(data)}&cb=jsonpCb`
    document.body.appendChild(script)
    })
    }
    // 使用方式
    request({
    url: 'http://localhost:9871/api/jsonp',
    data: {
    // 传参
    msg: 'helloJsonp'
    }
    }).then(res => {
    console.log(res)
    })
  2. CORS
    CORS是一个W3C标准,全称是”跨域资源共享”.实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
    主要实现是在头信息之中,添加一个Origin字段

    1
    2
    Origin: http://api.bob.com  指定源
    Origin: * 所有源

    详见跨域资源共享 CORS 详解

  3. 代理
    同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
    具体实现可以用nginx反向代理接口跨域
    在VUE中可以直接配置webpack.config.js(vue.config.js)文件实现代理,其原理同上,利用node + webpack + webpack-dev-server代理接口跨域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
    historyApiFallback: true,
    proxy: [{
    context: '/login',
    target: 'http://www.domain2.com:8080', // 代理跨域目标接口
    changeOrigin: true,
    secure: false, // 当代理某些https服务报错时用
    cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
    }],
    noInfo: true
    },
    // 新
    devServer: {
    proxy: {
    '/tkp-agent/o2o/srv': {
    target: 'http://xxx.com',
    changeOrigin: true
    },
    '/tkp-agent-web': {
    target: 'http://xxx.com',
    changeOrigin: true
    }
    }
    },
    }

    代理: 也称正向代理,是指一个位于客户端和目标服务器(target server)之间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标(目标服务器),然后代理向目标服务器转交请求并将获得的内容返回给客户端。(例: 科学上网,通过香港代理服务器访问外网)
    反向代理: 是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。(例: 外网访问内网,反向代理获取数据)
    反向代理和正向代理区别


关于跨域的方式还有很多(没啥用):
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域


参考:

不要再问我跨域的问题了
跨域资源共享 CORS 详解
前端常见跨域解决方案(全)