关于跨域问题

245 阅读2分钟

写个小记,记录一下关于跨域这件事

跨域问题的产生

跨域的问题,离不开一个关键词:同源策略。在mdn的描述中:同源策略是一个重要的安全策略

同源指的是相同的协议,域名和端口。这三者只要有一个不同,浏览器就会认为网站和资源来源是非同源关系。根据同源策略,浏览器会限制网站访问使用该资源。(即使是请求成功并且返回数据)最常见的例子就是跨域。使用同源策略,可以保护网站资源免受大部分的恶意资源的脚本攻击和污染。

而在现在前后端分离的开发模式下,经常会因为前后端不同源而产生跨域问题。跨域问题就像是一道城墙,阻挡开发者们的信息交互,因此就有各种解决跨域的方案产生。

跨域的解决方式

关闭浏览器检测

浏览器实现了同源策略,对网站的所有请求都做了同源检测。检测不合格,即使是请求成功返回数据也是不予许访问数据。所以我们可以关闭浏览器的同源策略。具体办法就自己baidu啦。因为这种方法也就只能在自己本机开发的时候使用,这里也就不多做说明。

jsonp方式

jsonp的实现基于script的src属性不受同源策略的影响。前端动态插入script标签,并且将src属性指向实现了jsonp的接口,同时声明好前后端协调好的回调函数。回调函数会被后台直接在客户端调用,并传入数据。前端只需在函数中加入获取数据后的后续操作。 例如: 后端(node)

jsonpAction() {
    const cb = this.get('callback');
    if (think.isEmpty(cb)) {
        this.fail(100, '缺少回调函数参数');
        return;
    }
    const data = {
        msg: 'jsonp数据'
    };
    this.body = `cb(${JSON.stringify(data)})`;
}

前端

let script = document.createElement('script');
script.src = 'protocol://domain/index/jsonp?callback=cb'; 
document.body.appendChild(script);

function cb(data) {
    console.log('后台返回的数据', data);
}

jsonp的方式由于实现的过程限制,只能适用于get方式。

代理方式

代理的方式其实也很好理解。因为服务器和服务器之间的请求没有浏览器的这种同源策略,所以我们可以使用一个代理的服务器,前端请求代理的服务器,通过代理服务器去请求真正的数据源服务器。然后在获取到数据之后代理服务器在返回前端。绕过浏览器的检查。在vue脚手架搭建的开发环境中就有集成这样功能。想要了解的不妨看一下vue文档(通过webpack集成的中间件)

CORS方式

上面的jsonp和代理的方式都是通过绕过浏览器的检测来获取数据。而CORS方式则是通过告诉浏览器,允许不同源的数据被访问。其主要的就是http请求中的 Access-Control-Allow-Origin 这一个请求头。在服务器返回的http请求中设置这样的一个请求头,告诉浏览器,哪一些跨域请求可以被允许。例如:

corsAction() {
    this.header('Access-Controle-Allow-Origin', '*');
}

上面的这段代码是在告诉浏览器说无论是任何的访问源来访问cors这个接口都被允许获取到数据,直接通过了浏览器的检测,允许了访问。这种方式相较于jsonp更为全面一些,即使是post请求也是被允许的,相较于代理的方式也来的更为的直接。但是这样的方式还是有缺点:
一、 服务器这样的设置,前端的任意访问源都被允许,安全性变差
二、 这样通配的方式在请求不允许携带cookie到后端去。
为了解决上面的问题,对代码做出一下的改动

corsAction() {
    // 获取域名
    let origin = this.header("origin");
    if (think.isEmpty(origin)) {
        const protocol = this.ctx.protocol;
        const referer = this.referer(true);
        origin = `${protocol}://${referer}`;
    }
    const host = origin.split('://')[1];
    // 白名单列表
    const whiteDomain = ['bbstage.jlinz.cn'];
    let flag = false;
    whiteDomain.forEach(domain => {
        if (host === domain) flag = true;
    })
    if (flag) {
        this.header("Access-Control-Allow-Origin", origin);
        this.header("Access-Control-Allow-Headers", " Origin, X-Requested-With, Content-Type, Accept");
        this.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    }
}

在上面的代码中,我们获取到了请求源的协议和域名,并且声明了一个白名单。我们将请求源的域名和白名单中的域名一一对比,如果白名单中有,我们就设置允许访问的请求头。在cors的方式中,我们只要指定了允许访问的域名,前端的访问就可以携带cookie到后端,我们也能控制那些请求源被允许访问。

以上,就是我对开发过程中遇到跨域的记录。有什么不对的,希望大家多多指点。thanks!