这几天做需求确实被跨域问题折磨的不轻,所以整理了一下过程中所遇到的问题以及解决方法,避免重复踩坑。
前言: 跨域问题是每个前端开发者都避不开的问题,在之前只从书上看过这一部分的知识,但是在平时本地开发中却很少碰到跨域的情况,所以对这一部分知识也是一知半解,这不,最近在实习,最开始搬砖搬的确实很开心,直到遇到了万恶的口语问题,被折磨的确实不轻,来来回回折腾了一个周,为了以后不再在跨域问题上踩坑,所以这次必须弄明白!
1、什么是跨域? 说到跨域就不得不提同源策略,同源策略产生原因:协议,域名,端口号,当涉及到这三者的时候就产生了跨域,简单的说就是a.com 域名下的js无法操作b.com或是c.a.com域名下的对象,更详细的说明从网上找了一张图(侵删):

同源策略的目的: 为了保证用户信息的安全,防止恶意的网站窃取数据。 同源策略限制了哪些行为: ① :Cookie、LocalStorage、和IndexDb无法读取。 ② :Dom无法获得。 ③ : AJAX请求不能发送。
Cookie: 服务器写入浏览器的信息,常用于处理登录,验证身份等场景。如果你请求了接口进行登录,服务器验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将Cookie添加在请求头中。cooki在跨域访问的时候是默认不携带的,当然也有一些方法能够将我们的cookie在发送请求的时候带上,后续会讲到。
Dom:
1.有一天你刚睡醒,收到一封邮件,说是你的银行账号有风险,赶紧点进www.yinghang.com改密码。你吓尿了,赶紧点进去,还是熟悉的银行登录界面,你果断输入你的账号密码,登录进去看看钱有没有少了。
2.睡眼朦胧的你没看清楚,平时访问的银行网站是www.yinhang.com,而现在访问的是www.yinghang.com,这个钓鱼网站做了什么呢?
// HTML
<iframe name="yinhang" src="www.yinhang.com"></iframe>
// JS
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你输入账号密码的Input')
console.log(`拿到了这个${node},我还拿不到你刚刚输入的账号密码吗`)
由此我们知道,同源策略确实能规避一些危险,不是说有了同源策略就安全,只是说同源策略是一种浏览器最基本的安全机制,毕竟能提高一点攻击的成本。其实没有刺不穿的盾,只是攻击的成本和攻击成功后获得的利益成不成正比。
跨域的解决办法: 跨域的方法有很多,比如jsonp,iframe+form等,因为现在的主流跨域解决方案是——跨域资源共CORS,所以在这就主要介绍这一种情况,对其他跨域方法有兴趣的同学可以自行查找。
介绍: CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出请求,从而克服了AJAX只能同源使用的限制。 CORS的使用需要浏览器和服务器同时支持。目前,市面上的浏览器都已支持该功能,但是,IE不能低于IE10。IE8+:IE8/9需要使用XDomainRequest对象来支持CORS。
CORS的整个实现过程都是由浏览器自动完成的,对于开发者来说,CORS通信和AJAX通信区别不大。所以实现CORS的关键在于服务器。
CORS分为两种请求方式:简单请求和非简单请求
简单请求:
· 请求方式: HEAD、POST、HEAD
· http头信息不超出一下字段:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
流程:当浏览器发现这次是简单请求时,会在请求头中增加一个Origin字段,它的作用是表明本次请求来自哪个源(协议、域名、端口号),服务器根据这个值,决定是否同意这次请求(同意的Orgigin值在服务器设置)。如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现在这次响应头中没有Access-Control-Allow-Origin字段,就会抛出一个错误,被回调函数捕获。如果Origin指定的源在许可范围内,服务器返回的响应会多出几个信息字段。
Access-Control-Allow-Origin:
必须字段,它的值要么是请求时Origin的值,要么可以搭配*来使用,但是如果要允许跨域访问携带Cookie,则该字段的值必须为明确的地址,不能含有通配符
Access-Control-Allow-Credentials(这是跨域携带Cookie的关键):若允许则设置为true,不允许则不加该字段即可。
Access-Control-Expose-Headers:
Content-Type
如何携带Cookie 我们应该如何在跨域请求中携带Cookie,除了需要服务器做相应设置,前端发送请求时也需要进行一步操作 fetch:
fetch(url, {
method: 'GET',
headers: myHeaders,
credentials: "include"
})
XMLHttpRequest:
xhr.open('GET', url);
xhr.withCredentials = true;
xhr.send();
这个地方需要注意的是,withCredentials的设置必须在open之后,send之前
同时只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
非简单请求:
非简单请求: 非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。