前端常见跨域

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014168594/article/details/88802693

前端跨域

本文总结前端常用的跨域方案和例子,以及周边的应用知识

(1)跨域原理
(2)域名概念
(3)本地简单模拟跨域
(4)常见跨域方案

一、跨域原理

我们常说的跨域,是由浏览器同源政策的限制引起的。

同源政策:

同源策略/SOP(Same origin policy)是一种约定,是浏览器最核心也最基本的安全功能。它的核心就在于它认为自任何站点装载的信赖内容是不安全的。

所谓同源是指"协议+域名+端口"三者相同。

有三个方面限制:

1.DOM 同源策略:禁止对不同源页面 DOM 进行操作。主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。(xss攻击)

2.XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。(CSFR攻击)

3.cookie,localstorage和IndexDB无法读取(CSFR攻击)

二、域名概念

顶级域名

互联网DNS等级之中的最高级的域,它保存于DNS根域的名字空间中。顶级域名是域名的最后一个部分,即是域名最后一点之后的字母。如 .cn、.com、.net

主、子域名

baidu.com是主域名,www.baidu.com和wap.baidu.com以及new.baidu.com都是他的子域名

父、子域名

一个相对概念。如 a.test.com是 test.com的子域名,q.a.test.com是 a.test.com的子域名

在这里插入图片描述

window.location 属性

例子:https://www.test.com:8080/first/second/index.html?user=123&name=panda#home

protocol(协议) => ‘https:’

hostname(主机名) => ‘www.test.com

port(端口号) => ‘8080’

host(主机名) => ‘www.test.com:8080

origin(来源) => ‘https://www.test.com:8080

pathname(路径) => ‘/first/second/index.html’

search(url参数) => ‘?user=123&name=panda’

hash(hash值) => ‘#home’

href(地址) => ‘https://www.test.com:8080/first/second/index.html?user=123&name=panda#home

三、简单模拟跨域

安装 node.js

在相应文件夹下打开cmd,输入: http-server 开启本地服务

在这里插入图片描述

在不同文件下开启本地服务,通过一样的 ip 地址但是不同的端口号,可用于简单本地模拟跨域

四、常见跨域方案

(1) jsonp 跨域

(2) location.hash

(3) window.name

(4) window.domain

(5) postMessage

(6) 跨域资源共享(CORS)

(7) 代理

(1)jsonp 跨域

原理:在html页面中通过标签从不同域名下加载资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。(常用作跨域服务器的请求)

原生代码:

<script type="text/javascript">
	var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = 'http://www.test.com:8080/login?user=admin&callback=onBack';

    //document.head.appendChild(script);
    document.getElementsByTagName('head')[0].appendChild(script);

	// 回调执行函数
    function onBack(res) {
        alert(JSON.stringify(res));
    }
</script>

一般请求带到了服务器,解析参数然后拼成字符串或者 Json 当成返回数据

如返回: onBack({“status”: 200, “user”: “admin”}),就是执行上面的 onBack 函数

可以通过之前的模拟跨域,通过一样的 IP 地址但是请求不同端口的 js 文件在本地模拟服务器跨域的返回。(这里www.test.com就是我们的本地模拟的ip地址)

jQ 代码:

$.ajax({
    url: 'http://www.test.com:8080/login',
    type: 'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: "onBack",    // 自定义回调函数名
    data: {}
});

jsonp 跨域缺点:只能实现get一种请求

(2)location.hash

原理:

1、改变hash值不会刷新页面,因此可以利用 hash 值来传递数据。

2、无论跨域与否,iframe 内可以获取自己的 location.hash

3、只要域名相同就能通信,即使ABC三层嵌套

流程:

三个页面 A B C,A 通过第三方页面 C 达到与 B 的通讯

  1. A 页面设置 iframe 指向 B 页面地址,待 iframe 加载完后修改该 hash 值从而传参给 B,
    并监听本页面 hash 的变化

  2. B 页面在页面内监听 hash 变化从而获取参数,设置 iframe 指向 C 页面并传参给 C

  3. C 页面监听 hash 变化,只要 A C 页面域名相同,可根据 B 的 hash 参数,修改父父窗口(即 A页面)的 hash 从而触发 A 页面的监听,达到参数回传给 A 的效果

实例:

www.test.com:8080/A.html

var ifr = document.createElement('iframe');
createIfr('admin');

window.onhashchange = function(){	//监听hash变化
    if(location.hash == '#status')
        callBack();
}

function callBack(){
    alert('A: data from B.html ---> ' + location.hash);
}

function createIfr(hash){
    ifr.style.display = 'none';
    ifr.src = 'www.test.com:8081/B.html';
    document.body.appendChild(ifr);
    ifr.onload = function(){ ifr.src = 'www.test.com:8081/B.html#' + hash; }
}

www.test.com:8081/B.html

var ifr = document.createElement('iframe');

window.onhashchange = function(){
    if(location.hash == '#admin')
        callBack();
        createIfr('status');
}

function callBack(){
    alert('B: data from A.html ---> ' + location.hash);
}
function createIfr(hash){
    ifr.style.display = 'none';
    ifr.src = 'www.test.com:8080/C.html';
    document.body.appendChild(ifr);
    ifr.onload = function(){ ifr.src = 'www.test.com:8080/C.html#' + hash; }
}

www.test.com:8080/C.html

window.onhashchange = function(){
    parent.parent.location.hash = location.hash;
}

缺点:因为是修改 url 的 hash ,数据直接暴露在了url中,而且数据容量和类型都有限

(3)window.name

原理:在一个窗口(window)的生命周期内,窗口载入的所有的页面(不管是相同域的页面还是不同域的页面)都是共享一个 window.name 的(可存2M),每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

流程:

  1. A 页面设置 iframe 指向 B 页面地址

  2. B 在当前页面设置 window.name 并传参

  3. C 页面不需处理

  4. 待 B 加载完成,A 页面 iframe 地址从 B 改成 C,从而可以访问在 B 页面时设置的 name

例子:

www.test.com:8080/A.html

var iframe = document.createElement('iframe');
var status = 0;
iframe.onload = function(){
    if(status == 1){
        onCallback(iframe.contentWindow.name);
    }else if(status == 0){
        status = 1;
        iframe.contentWindow.location = "www.test.com:8080/C.html";
    }
}
iframe.src = 'www.test.com:8080/B.html';
document.body.appendChild(iframe);

function onCallback(res) {
    alert('A: data from B.html ---> ' + res);
}

www.test.com:8081/B.html

window.name = 'name2.html设置的数据';

www.test.com:8080/C.html 不做操作

(4)window.domain

原理:当两个页面域名不同,但主域名相同时,可以把两个页面的 document.domain 都设成相同的域名,此时浏览器会认为他们在同一域名内,这样就可以通讯了。

www.test.com:8080/first/A.html

<iframe id="iframe" src="www.test.com:8080/test/B.html"></iframe>
<script type="text/javascript">
    document.domain = 'www.test.com';
    var user = 'admin';
</script>

www.test.com:8080/second/B.html

document.domain = 'www.test.com';
// 获取父窗口中变量
alert('data from parent ---> ' + window.parent.user);

缺点:只适用于主域名相同,而子域名不同的情况。

(5)postMessage

HTML5 中的 API,专门用来跨域操作的 Windows 属性。

window.postMessage(data,origin)

data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。

origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

www.test.com:8080/A.html

<iframe id="ifr" src="www.test.com:8081/B.html"></iframe>
<script type="text/javascript">
	var iframe = document.getElementById('ifr');
    iframe.onload = function() {
        var data = {
            name: 'panda'
        };
        iframe.contentWindow.postMessage(JSON.stringify(data), 'www.test.com:8081/B.html');
    };

    // 监听 B 返回数据
    window.addEventListener('message', function(e) {
        alert('A: data from B ---> ' + e.data);
    }, false);
</script>

www.test.com:8081/B.html

window.addEventListener('message', function(e) {
    alert('B: data from A ---> ' + e.data);
    var data = {
        pwd: '123'
    };
    window.parent.postMessage(JSON.stringify(data), 'www.test.com:8080/A.html');
}, false);

(6)跨域资源共享(CORS)

一般服务端响应报文设置 Access-Control-Allow-Origin 就可以跨域了

如要带 cookie:前端 xhr.withCredentials 设为 true,后端报文设置 Set-cookies

(6)node 中间件代理

原理:通过启一个代理服务器,实现数据的转发

展开阅读全文

没有更多推荐了,返回首页