tags: [组件]
categories: 业务场景解决方案
什么是跨域
由于浏览器的同源策略,导致只要协议、域名、端口有任何一个不同,都被当作是不同的域,从而导致了跨域访问的需求.
服务器端不存在跨域
不能通过ajax的方法去请求不同源中的文档
不同域的框架之间是不能进行JS交互操作的,不同的框架之间是可以获取window对象的,但是无法获取响应的属性和方法
如果是协议和端口造成的跨域问题“前台”是无能为力的
在跨域问题上,域仅仅是通过“URL首部”来识别,而不会去尝试判断相同的IP地址对应着两个域或两个域是否在同一个IP上
- URL首部:window.location.protocol + window.location.host
针对接口的请求
针对Dom的查询
跨域的方法
双向通信:即两个iframe,页面与iframe或是页面与页面之间
单向跨域:一般用来获取数据
iframe + document.domain
将两个页面的 document.domain 都设成相同的域名(只能设置成自身或更高一级的父域,且主域必须相同)
共享Cookie
服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.hello.com
Set-Cookie: key=value; domain=.hello.com; path=/
共享数据
<!--a页面-->
<iframe src="http://bbb.hello.com/b.html" onload="load()" id="frame"></iframe>
<script>
document.domain = 'hello.com';
let a = "this is a";
// 获取b页面数据
function load(){
let frame = document.getElementById("frame")
console.log(frame.contentWindow.b) // this is b
}
</script>
<!--b页面-->
<script>
document.domain = 'hello.com';
let b = "this is b"
// 获取a页面数据
console.log(window.parent.a); // this is a
</script>
ifame + location.hash
父窗口可以对iframe进行URL读写,iframe也可以读写父窗口的URL
hash一般用于浏览器锚点定位,Server端并不关心这部分,应该说HTTP请求过程中不会携带hash,所以这部分的修改不会产生HTTP请求,但是会产生浏览器历史记录。
页面a:[http://www.hello.com/a.html](http://www.hahaha0.com/a.html)
<!--a中通过iframe引入了b-->
<iframe id="frame" src="http://www.hello1.com/b.html"></iframe>
<script> let frame = document.getElementById('frame');
// 向b传hash值
frame.src = frame.src + '#a=我是a';
// 给同域c使用的回调方法
function cb(data) {
console.log(data) // 打印 我是a+b
}</script>
页面b:[http://www.hello1.com/b.html](http://www.hahaha1.com/b.html)
<!--b中通过iframe引入了中间人c-->
<iframe id="frame" src="http://www.hello0.com/c.html"></iframe>
<script> let frame = document.getElementById('frame');
// 监听a传来的hash值,传给c.html
window.onhashchange = function () {
frame.src = frame.src + location.hash + '+b';
};</script>
页面c:[http://www.hello0.com/c.html](http://www.hahaha0.com/c.html)
<script> // 监听 b 的hash值变化
window.onhashchange = function () {
// c调用父亲的父亲,来操作同域a的js回调,将结果传回
window.parent.parent.cb(location.hash.replace('#a=', ''));
};</script>
iframe + window.name
页面a:[http://www.hello1.com/abc/a.html](http://www.hahaha1.com/abc/a.html)
<iframe src="http://www.hello2.com/abc/b.html" id="frame" onload="load()"></iframe>
<script> let flag = true
// onload事件会触发2次
// 第1次onload跨域页b成功后,留下数据window.name,后切换到同域代理页面
// 第2次onload同域页c成功后,读取同域window.name中数据
function load() {
if(flag){
// 第1次
let frame = document.getElementById('frame')
frame.src = 'http://www.hello1.com/abc/c.html'
flag = false
}else{
// 第二次
console.log(frame.contentWindow.name) // 我是b
}
}</script>
页面b:[http://www.hello2.com/abc/b.html](http://www.hahaha2.com/abc/b.html)
<script> window.name = '我是b' </script>
H5 的 postMessage
IE8+
发送信息的postMessage事件: otherWindow.postMessage(message, targetOrigin)
otherWindow: 目标窗口,也就是给哪个window发消息,是window.frames属性的成员或者window.open创建的窗口
message:要发送的消息,String | Object
targetOrigin: 限定消息接收范围,不限制请使用“*”
- transfer(可选): 是一串和
message
同时传递的 「Transferable」 对象,这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权
监听接收信息的 message 事件
既可以处理 Get 请求也可以处理 POST 请求
页面a:[http://www.hello1.com/abc/a.html](http://www.hahaha1.com/abc/a.html)
,创建跨域 iframe
并发送信息
<iframe src="http://www.hello2.com/abc/b.html" id="frame" onload="load()"></iframe>
<script>
function load() {
let frame = document.getElementById('frame')
// 发送
frame.contentWindow.postMessage('哈喽,我是a', 'http://www.hahaha2.com/abc/b.html')
// 接收
window.onmessage = function(e) {
console.log(e.data) // 你好,我是b
}
}</script>
页面b:[http://www.hello2.com/abc/b.html](http://www.hahaha2.com/abc/b.html)
,接收数据并返回信息
<script> // 接收
window.onmessage = function(e) {
console.log(e.data) // 哈喽,我是a
// 返回数据
e.source.postMessage('你好,我是b', e.origin)
}</script>
JSONP
通过script标签引入的JS是不受同源策略的限制的,可以通过script标签引入一个文件,此文件必须返回一个JS函数的调用(要和后端约定好)
只能实现GET请求
jquery的getJSON()会自动判断是否跨域,不跨域就调用普通的ajax();
跨域则会以异步加载JS文件的形式来调用JSONP的回调函数
JSONP优点:
不像XHR对象实现Ajax请求那样受到同源策略的限制
兼容性更好,不需要XHR或者ActiveX的支持
在请求完毕后可以通过调用callback的方式回传结果
JSONP缺点:
只支持GET而不支持POST等其它类型的HTTP请求
只支持跨域HTTP请求的情况,不能解决不同域的两个页面之间如何进行JS调用的问题
页面a:[http://www.hello1.com/abc/a.html](http://www.hahaha1.com/abc/a.html)
<script type="text/javascript"> //回调函数
function cb(res) {
console.log(res.data.b) // 我是b
}</script>
<script type="text/javascript" src="http://www.hello2.com/abc/b.js"></script>
页面b:[http://www.hello2.com/abc/b.js](http://www.hahaha2.com/abc/b.js)
var b = "我是b"
// 调用cb函数,并以json数据形式作为参数传递
cb({
code:200,
msg:"success",
data:{
b: b
}
})
CORS
IE10+
CORS:
使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信。
服务器端对于CORS的支持,主要通过设置Access-Control-Allow-Origin来进行。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头部信息,有时还会多出一次附加请求,但用户不会有感觉。
CORS与JSONP相比,无疑更为先进、方便和可靠:
JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
使用CORS,开发者可以使用普通的XHR发起请求和获得数据,比起JSONP有更好的错误处理
JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS
// 来源
Origin: http://www.hahaha.com
// 该CORS请求的请求方法
Access-Control-Request-Method: POST
// 额外发出的头信息字段
Access-Control-Request-Headers: X-Token, X-Test
// 允许发送 Cookie 和 HTTP 认证信息, 同时客户端也需要设置‘xhr.withCredentials = true’
Access-Control-Allow-Credentials: true
:::tips
注意,如要发送 Cookie
,Access-Control-Allow-Origin
字段就不能设为星号,必须指定明确的、与请求网页一致的域名,同时,Cookie
依然遵循同源政策,只有用服务器域名设置的 Cookie
才会上传,其他域名的 Cookie
并不会上传,且(跨源)原网页代码中的 document.cookie
也无法读取服务器域名下的 Cookie
,下面还会提到
:::
预检请求报错
No 'Access-Control-Allow-Origin' header is present on the requested resource
且 The response had HTTP status code 404
告诉后端请允许下 OPTIONS
请求:服务端没有设置允许 OPTIONS
请求,那么在发起该预检请求时响应状态码会是404,因为无法找到对应接口地址.
No 'Access-Control-Allow-Origin' header is present on the requested resource
且 The response had HTTP status code 405
告诉后端请关闭对应的安全配置:服务端已经允许了 OPTIONS
请求,但是一些配置文件中(如安全配置)阻止了 OPTIONS
请求.
No 'Access-Control-Allow-Origin' header is present on the requested resource
且 OPTIONS 请求 status 为 200
告诉后端请增加对应的头部支持:服务器端允许了 OPTIONS
请求,配置文件中也没有阻止,但是头部匹配时出现不匹配现象。所谓头部匹配,就比如 Origin
头部检查不匹配,或者少了一些头部的支持(如 X-Requested-With
等),然后服务端就会将 Response
返回给前端,前端检测到这个后就触发 XHR.onerror
,从而导致报错。
OPTIONS 请求 status 为 500
告诉后端检测到预检请求时,请把它搞成200:服务端针对 OPTIONS
请求的代码出了问题,或者没有响应。
其他
中间件跨域
服务器代理跨域
Flash URLLoader跨域
动态创建 script 标签
websocket协议支持跨域: ws没有同源限制,客户端可以与任意服务器通信
遇到的跨域问题
单点登陆嵌入子系统页面
通过 iframe + document.domain 的方式解决
icon-font 放到 GitHub 上导致跨域问题
通过将icon-font 的 url 改成 base64 的形式直接嵌入到页面中
这样会产生跨域问题
@font-face {
font-family: 'idoll-icon-online';
src: url('@{icon-url}/idoll-icon-online.eot?67rfls');
src: url('@{icon-url}/idoll-icon-online.eot?67rfls#iefix') format('embedded-opentype'),
url('@{icon-url}/idoll-icon-online.ttf?67rfls') format('truetype'),
url('@{icon-url}/idoll-icon-online.woff?67rfls') format('woff'),
url('@{icon-url}/idoll-icon-online.svg?67rfls#idoll-icon-online') format('svg');
font-weight: normal;
font-style: normal;
}
添加了base64的方式便不会跨域了
@font-face {font-family: "idoll-icon-pro2";
src: url('@{icon-url}/idoll-icon-pro2.eot?t=1544695441349'); /* IE9*/
src: url('@{icon-url}/idoll-icon-pro2.eot?t=1544695441349#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),
url('@{icon-url}/idoll-icon-pro2.ttf?t=1544695441349') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('@{icon-url}/idoll-icon-pro2.svg?t=1544695441349#iconfont') format('svg'); /* iOS 4.1- */
}
资源
前端跨域知识总结
最直白的跨域访问原理
浏览器的同源策略
不要再问我跨域的问题了
前端跨域问题解决方案(基于node与nginx) - 掘金
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS