浏览器跨域
# 浏览器跨域
# 同源策略
同源:协议、域名、端口号 必须完全相同
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据
违背同源策略就是跨域
# 跨域
跨域是指浏览器页面访问不同源的服务器的时候,因为同源策略限制导致的请求不成功
如果协议、域名或者端口有一个不同,都被当作是不同的域,就不能使用 Ajax 向不同源的服务器发送 HTTP 请求
请求跨域了,请求到底发出去没有?肯定发出去了,但是浏览器拦截了响应
# 跨域的解决方式
JSONP(此方法需要前后端配合完成)
原因:script标签不受同源策略的影响(src 属性没有跨域的限制)
用法:前端准备好回调函数,放入script标签中的src属性中,向后端发送请求的时候后端就能够拿到
回调函数,将数据最为参数传给回调函数,前端就可以收到数据
缺点:只支持get请求,不支持post请求,因为发送的请求携带在script的url中,不带请求体
//定义获取数据的回调方法 function getData(data) { console.log(data); } // 创建一个script标签,并且告诉后端回调函数名叫 getData var body = document.getElementsByTagName('body')[0]; var script = document.gerElement('script'); script.type = 'text/javasctipt'; script.src = 'demo.js?callback=getData'; body.appendChild(script); //script 加载完毕之后从页面中删除,否则每次点击生成许多script标签 script.onload = function () { document.body.removeChild(script); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16cors跨域请求
用法:首先浏览器会判断该请求是简单请求simple request还是复杂请求not-so-simple request
简单请求
1.使用GET、POST、HEAD
2.HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
如果是简单请求会自动在请求头中加上origin(值为浏览器url),后端收到请求后虚打开cors接口:设置access-control-allow-origin的值为前端传过来的origin
对于简单请求,浏览器直接发起 CORS 请求,具体来说就是服务器端会根据请求头信息中的 origin 字段(包括了协议 + 域名 + 端口),来决定是否同意这次请求
如果 origin 指定的源在许可范围内,服务器返回的响应,会多出几个头信息字段:
Access-Control-Allow-Origin: http://xxx.xxx.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
1
2
3
4复杂请求
如果非简单请求(put,delete,axios请求等),浏览器会先发送一条预检option请求,预检请求会询问服务器是否支持当前域名的请求,是否支持修改的头信息字段,收到肯定回复后,浏览器才发送真正的http请求,否则报错
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样了
下面是一个预检请求的头部:
OPTIONS /cors HTTP/1.1 Origin: http://api.bob.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
1
2
3
4
5
6
7
8
服务器代理
浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端
一般我们在本地环境开发时,就是使用 webpack-dev-server 在本地开启一个服务进行代理访问的
A要访问C,两者不同源,则A先去访问和A同源的代理服务器B,由于服务器之间不存在跨域,由B去获得C的API接口,返回给A
window提供的可跨域方法postMessage
window.postMessage 是一个 HTML5 的 api,允许两个窗口之间进行跨域发送消息
这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息
// 发送消息端 var receiver = document.getElementById('receiver').contentWindow; var btn = document.getElementById('send'); btn.addEventListener('click', function (e) { e.preventDefault(); var val = document.getElementById('text').value; receiver.postMessage("Hello "+val+"!", "http://res.42du.cn"); }); // 接收消息端 window.addEventListener("message", receiveMessage, false); function receiveMessage(event){ if (event.origin !== "http://www.42du.cn") return; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15document.domain(iframe跨域问题)
该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式
只需要给两个页面都添加 document.domain = 'test.com',通过在 a.test.com 创建一个 iframe,去控制 iframe 的 window,从而进行交互