因为浏览器的同源策略而产生跨域问题,跨域主要是为了用户的上网安全。下面例举了前端最常见的三个跨域,以及处理方式
iframe 跨域
iframe
跨域主要是为了防止恶意网站获取其他网站的dom
,拿去私密信息,如果没有跨域恶意网站可以嵌套其他网站,比如https://mail.qq.com/
,当用户输入用户名密码时通过dom
即可截取到。当然也有一些业务确实确实需要跟子页面通讯,下面列举了一系列iframe
跨域的处理方式
子域跨域 document.domain
document.domain
的作用就是获取/设置
当前文档的原始域名,默认值就是当前域名。可设置的值为父域名|当前域名
。比如a.demo.com
可设置的值就是a.demo.com|demo.com
。浏览器判断跨域时会使用document.domain
来判断是否域名跨域。
所以我们子域名与子域名使用iframe
跨域时,双方的document.domain
都设置为父域名。父域名与子域名使用iframe
跨域时,子域名将document.domain
设置为父级域名。
window.name
window.name
可读可写,值是跟着浏览器窗体走的,也就是说不是跟着页面走的,我们在使用iframe
加载子页面时,子页面可以通过name
来传递值给父级使用。
// 子页面
window.name = JSON.stringify({ title: "child" });
// 父页面
const $iframe = document.createElement("iframe");
$iframe.src = "";
$iframe.onload = () => {
// '{"title":"child"}'
console.log($iframe.contentWindow.name);
};
document.body.appendChild($iframe);
window.postMessage
window.postMessage
方法可以安全的实现跨通讯,使用时在发送方明确接收方的协议、地址、端口号就可以发送,接收方使用message
事件监听来自postMessage
的事件。
// 子页面 b
parent.postMessage("toggleFullScreen", "http://a.demo.com");
// 父页面 a
window.addEventListener("message", (event) => {
if (event.origin === "http://b.demo.com") {
// event.data 发送的消息
window[event.data]();
// event.source 获取发送方的window
event.source.postMessage(`${event.data}Success`, event.origin);
}
});
AJAX 跨域
AJAX
跨域主要是放置恶意网站请求其他网站,因为请求中会携带cookie
服务器会误认为是“本人”操作而引发安全问题。比如用户登录了银行网站,然后访问恶意网站,恶意网站请求转账到指定账户,此时请求会携带上用户信息到银行服务器。当然也有一些业务确实跨域请求其他服务器,下面列举了一系列AJAX
跨域的处理方式
JSONP
ajax
请求收到同源策略影响,但是script
标签的src
属性不会,可以访问跨域的js
脚本。我们可以利用这个特性,在服务端不返回JSON
格式,而是返回某个函数执行代码。
const $script = document.createElement("script");
$script.src = `http://www.xxx.com/?callback=getData`;
const getData = (data) => {
console.log(data);
};
document.body.appendChild($script);
// 服务端返回e
// getData({ ... })
JSONP
只支持get
而且需要后端配合
CORS
请求时,服务端在 HTTP 响应头设置Access-Control-Allow-Origin
允许当前域跨域,浏览器就会允许跨域行为
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origin>
携带凭证的 CORS
上方的请求是不携带凭证 (如 cookie) ,也就是说服务器无法认证你的信息,比如我们再 A 网站登录了,在 B 网站通过AJAX
请求 A 网站是无法拿到需要凭证的信息的 (如用户信息), 如果需要带上cookie
还需要做其他处理,参考我之前写过的文章
Proxy
在客户端浏览器发起一个非同源请求会产生跨域问题,但是服务端并没有同源策略,服务端向另一个服务器发起请求不会产生跨域问题。那我们可以将请求统一到同源的服务器,对于要跨域的请求加上特定标识的 url 前缀,在服务器上匹配如果是这个前缀的请求,则反向代理到跨域服务器。
我们在代理中还可以修改Cookie
的domain
信息,方便当前域Cookie
的写入。
这是目前最常用的方式,本地开发的跨域可以在本地建立代理,如果是线上则在线上创建代理。
canvas 跨域
canvas
提供了一个通过JavaScript
和HTML
元素来绘制图形的方式,canvas
可以从其他站点加载其他站点的资源,比如图片。在跨站引入资源没有处理时,canvas
会被污染,只要被污染的画布就无法获取画布数据,如toBlob、toDataURL、getImageData
一类api
,这就产生的跨域问题。
CORS、Proxy
canvas
产生的跨域问题跟AJAX
的处理方式差不多,可以借助Proxy
,或者在 HTTP 响应头设置Access-Control-Allow-Origin
。除此之外还需要对资源通过crossorigin
属性声明不需要凭证信息。
属性值 | 描述 |
---|---|
anonymous | 对此元素的CORS 请求将不设置凭证标志 |
use-credentials | 对此元素的CORS 请求将设置凭证;这意味着请求将携带凭证 |
"" | 设置空值,与设置anonymous 效果一致 |
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = function () {
context.drawImage(this, 0, 0)
context.getImageData(0, 0, this.width, this.height);
};
img.src = 'xxx'
携带凭证的CORS
如果需要携带凭证除了声明crossOrigin
属性值为use-credentials
外还需要做其他处理,参考我之前写过的文章。
完结撒花