前端跨域解决学习

详见https://segmentfault.com/a/1190000011145364

CORS的学习参见:http://www.cnblogs.com/onepixel/articles/7568001.html
tingandpeng.com/2016/09/05/前端跨域请求原理及实践/

什么是跨域

一个域下的文档或脚本试图去请求另一个域下的资源,这里的跨域是广义的

资源跳转:重定向,a连接 表单提交
资源嵌入:link script img frame background:url() @font-face()
脚本请求: ajax dom js对象的跨域操作

什么叫同源?
协议域名端口相同,哪怕ip相同但是域名不同也不是同源
限制以下几种行为:
cookie localstorage indexDb无法读取
DOM js对象无法获取
ajax请求不能发送

解决方案:

  1. 通过jsonp跨域
  2. document.domain +iframe 跨域
  3. location.hash + iframe
  4. window.name+iframe
  5. postMessage跨域
  6. 跨域资源共享(CORS)
  7. nginx代理跨域
  8. nodejs中间件代理跨域
  9. WebSocket协议跨域

一、通过jsonp跨域

为了减轻web服务器的负载,我们把js css img等静态资源分离到另一台独立域名的服务器上,html页面通过相应的标签从不同的域名下加载静态资源被浏览器允许,我们可以动态的创建script再请求一个带参网址是子安跨域通信

缺点:只有get方式

1、原生实现:


var script = document.createElement("script");
script.type = "text/javascript";
script.src  = "https://www.domain2.com:9090/login?user=adming&callback=onback";
document.head.appendChild(script);
//回调函数,家挂
function onBack(res){
    alert(JSON.stringify(res));
}

这时候定义了全局的函数onBack回来后会自动调用
onBack({test:"aa"})
2、jquery ajax :

jQuery
$.ajax({
    url:"http://www.domain.com:8080/login",
    type:"get",
    dataType:"jsonp",
    jsonpCallback:"onback",
    data:{}

})

3、vue的用法

vue
this.$http.jsonp("http://www.domain.com:8080/login",{
    param:{},
    jsonp:"onBack"
}).then((res)=>{
    console.log(res);
})

4.nodejs的用法

5.nodejs
var querystring = require("querystring");
var http = require("http");
var server  = http.createServe();
server.on("request",function(req,res){
    var params = qs.parse(req.url.split("?")[1]);
    var fn = param.callback;

    res.write(fn+"("+JSON.stringify(params)+")")
    res.end();
});
serve.listen("8080");
console.log("server is runing at port 8080...");

二、 document.domain +iframe 跨域

主域相同

原理:两个页面都通过js强制设置document,domain为基础主域,就实现了同域

1、父窗口:(http://www.domain.com/a,html

<iframe id="iframe" src="http://www.child.domain.com/b,html"></iframe>
<script>
document.domain = "dimain.com"
var user = "admin"
</script>

2、子窗口:(http://child.domain.com/b,html

<script>    
document.domain = "dimain.com"
console.log("get js data from parent ====>"+window.parent.user)
</script>

三、 location.hash + iframe

原理:a欲与b跨域相互通信,通过中间页面c来实现,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信

A的a.html =>B的b.html -》A的c.html
1.a.html(http://www.domain1.com/a,html)

<iframe id="iframe" src="http://www.child.domain.com/b,html"></iframe>
<script>
var iframe = document.getElementById("iframe");
//向b传hash值
    setTimeout(function(){
        iframe.src = iframe.src+"#user =admin";
    },1000)
    //开放给c>html的回调方法
    function onCallback(res){
        alert("data from c.html====>"+res);
    }
</script>

2.b.html(http://www.domain2.com/b,html)

<iframe id="iframe" src="http://www.domain1.com/c.html"></iframe>
<script>
    var iframe = document.getElementById("iframe");
    window.onhashchange = functin(){
        iframe.src= iframe.src +location.hash;
    }
</script>

3.c.html(http://www.domain1.com/c.html)

<script>
    window.onhashchange = functin(){
       window.parent.parent.oncallback("hello"+location.hash.replace("#user=","" ))
    }
</script>

四、 window.name+iframe

window.name的属性的独特之处:name值在不同的页面(甚至不同的域名)加载后依旧存在,并且可以支持非常长的name值

a.html http://www.domian1.com/a.html

var proxy = function(url,callback){
    var state = 0;
    var ifram =document.createElement("iframe");
    ifram.src = url;
    ifram.onload = function () {
        if(state===1){
            callback(iframe.contentWindow.name);
            destroyFrame();
        }else if(state === 0){
            ifram.contentWindow.location = "http://www.domain1.com/proxy.html";
            statte =1;
        }
    }

    document.body.append(ifram)
}
proxy("http://www.domain2.com/b.html".function(data){
    alert(data);
})

proxy.html:http://www.domain1.com/proxy
中间代理页面,和a.html相同就好了,内容为空就行

b.html http://www.domain2.com/b.html

<script>
    window.name = "this is domain2 data"
</script>

五、 postMessage跨域

postMessage 是ihtml5XMLHttpRequeset level 2 中的API,且是为数不多可以跨越操作的window属性之一,它可用于解决一下方面的问题:

页面和其他的打开的新的窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个场景的跨域数据传递

用法:postMessage(data,origin)
data:JSON.stringify()序列化
origin :协议+主机+端口号

html1 http://www.domain1.com/a.html

<iframe id="iframe" src="http://www.domain2.com/b.html"></iframe>

var iframe = document.getElementById("iframe");

ifram.onload = function () {
    var data = {
        name:"aym"
    }
    //向domain跨域传输数据
    iframe.contentWindow.postMessage(JSON.stringfy(data),"http://www.domain2.com");
}
window.addEventListener("message", function (e) {
    alert("data from domain2 ---->"+ e.data);
},false);

b.html http://www.domain2.com/b.html


window.addEventListener("message",function(e){
    alert("data from domain1");
    var data  = JSON.parse(e.data);
    if(data){
        data.number =14;
        window.parent.postMessage(JSON.stringify(data),"http://www.domain1.com")
    }

},false)

六、 跨域资源共享(CORS)

普通的跨域请求:只要服务端设置access-control allow origin既可,前端无需设置
带cookie请求:前后端都要设置字段,另外注意:所需cookie为跨域请求接口所在域的cookie而非当前页
目前所有浏览器都支持该功能cors也成为主流的跨域解决方案

1、前端设置:

//前端设置哎是不是带cookie
xhr.withCredentials = true;

//show:
//前端设置哎是不是带cookie
xhr.withCredentials = true;

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("post","http://www.domain2.com:8080/login",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send("user=damin");
xhr.onreadystatechange = function () {
    if(xhr.readyState==4&&xhr.status == 200){
        alert(xhr.responseText)
    }
}

//jQuery
$.ajax(
    xhrFilds:{
    withcredentials:true//前端设置是否带有cookie
}
)
//Vue
vue.http.options.credentials = true;

2、服务端的设置:
如果服务端设置成功了,前端不会出现跨域的异常情况
java

导包
//import javax.servelet.http.HttpServletResponse
//接口参数中定义了HttpServletResponse response

response.setHeader("Access-Control-Allow-Orign","http://www.domain1.com");//如果有端口需要写全
response.setHeader("Access-Control-Allow-Credentials","true");

node的用法:

var http = require("http");
var qs= require("quertstring");
var server  = http.createServe();
server.on("request",function(req,res){
   var postData = ""
    req.addListener("data",function(chunk){
        postData+=chunk;
    })
    req.addListener("end",function(){
        postData= qs.parse(postData);
        res.writeHead(200,{
            "Access-Control-Allow-Orign":"http://www.domain1.com",
            "Access-Control-Allow-Credentials":"true",
            "Set-Cookie":"l=a1123123123;Path = /:Domain = www.domain2.commLHttpOnly"
        });
        res.write(JSON.stringify(postData));
        res.end()

    })
});
serve.listen("8080");
console.log("server is runing at port 8080...");

七、 nginx代理跨域

1、解决iconfont跨域
浏览器跨域访问js css img 等常规静态资源悲痛元策略许可,但是iconfont字体文件例外,这时候在nginx添加如下的配置就好了

location/{
add_header Access-COntrol-Allow-origin*;
}

2、反向代理接口跨域
跨域原理:同源策略是浏览器的安全策略,不是http协议的一部分,服务器端调用http接口只是使用了http协议不会执行js脚本,不需要同源策略,也不存在跨域问题

实现思路:
通过nginx配置一个代理服务器,域名domain1相同,端口不同,当成条班级,反向代理访问domain2接口,且可以顺便修改cookie中的domain信息方便当前域cookie写入,实现跨域登陆

配置文件

   server {
        listen       81;
        server_name  www.domain1.com;
        access_log  logs/host.access.log main;#这个是啥?不知道

    #不使用缓存配置 expires 0s;
        location / {
           # root   ../gateWay/;
           proxy_pass http://www.domain2.com:8080;
           proxy_cookie_domain  www.domain2.com www.dmain.com;#修改cookie里的域名
           index  index.html index.html;
       #当用webpack-dev-server等中间代理接口访问nignx时候,这个时候没有浏览器参与,所以没有同源限制,下面的跨域配置可不启用
       add-header Access-control-Allow-Origin http://www.domain1.com ;#当前只有跨域不带cookie的时候可以为*
       add_header Aeecss-Control-Allow-Credentials true;
        }
  }

前端代码的写法:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("post","http://www.domain1.com:81/login",true);//访问ajax
xhr.send():

后台node配合

var http = require("http");
var qs= require("quertstring");
var server  = http.createServe();
server.on("request",function(req,res){
   var param = qs.parse(postData);
     res.writeHead(200,{
            "Set-Cookie":"l=a1123123123;Path = /:Domain = www.domain2.comm;HttpOnly"
        });
     res.write(JSON.stringify(postData));
     res.end()
});
serve.listen("8080");
console.log("server is runing at port 8080...");

八、 nodejs中间件代理跨域

类似于ngninx都是通过一个中间服务器实现转发,懒得看了,待会儿复制

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发。

1、 非vue框架的跨域(2次跨域)

利用node + express + http-proxy-middleware搭建一个proxy服务器。

1.)前端代码示例:

var xhr = new XMLHttpRequest();
 
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
 
// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();

2.)中间件服务器:

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
 
app.use('/', proxy({
    // 代理跨域目标接口
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,
 
    // 修改响应头信息,实现跨域并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },
 
    // 修改响应信息中的cookie域名
    cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
}));
 
app.listen(3000);
console.log('Proxy server is listen at port 3000...');

3.)Nodejs后台同(六:nginx)

2、 vue框架的跨域(1次跨域)

利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。

webpack.config.js部分配置:

module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true,
        proxy: [{
            context: '/login',
            target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

九、 WebSocket协议跨域

webSocket protocol是html5的一种新协议,实现了浏览器与服务器全双工通信,同时允许跨域通讯模式server push一种良好的实现

原生的websockey api使用起来不放哪gia你,我们使用socket.io分装了webSocket接口简单灵活向下兼容
前端代码

<div> user input: <input type="text"></div>
<script src="./sockey.io.js"></script>
<script>
    var socket= io("http://www.domain2.com:8080");
//连接成功处理
socket.on("connent",function(){
    //监听服务端的信息
    socket.on("message",function(msg){
        console.log("data from server ----->"+msg);
    });
    //监听到关闭
    socket.on("disconnect",function(){
        console.log("server sockt has closed");
    })
})

document.getElementsByTagName("input")[0].onblur = function () {
    socket.send(this.value);
}
</script>

Nodejs socket后台


var http = require("http");
var socket= require("socket.io");
var server  = http.createServe(function (req,res) {
    res.writeHead(200,{
        "Content-type":"text/html"
    });
    res.end()
});
serve.listen("8080");
console.log("server is runing at port 8080...");


//监听socket连接

socket.linsten(server).on("connent",function(client){
    //监听服务端的信息
    client.on("message",function(msg){
        console.log("data from client ----->"+msg);
        client.send("htllo :"+msg);
    });
    //监听到关闭
    client.on("disconnect",function(){
        console.log("client sockt has closed");
    })
})

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容