资讯专栏INFORMATION COLUMN

理解跨域及常用解决方案

only_do / 585人阅读

摘要:跨域的产生不用多讲,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。浏览器引入同源策略主要是为了防止,攻击。其指明了实际请求所允许使用的方法。

跨域,相信大家无论是在工作中还是在面试中经常遇到这个问题,常常在网上看到别人所整理的一些方法,看似知道是怎么回事,但如果没有动手实践过,总觉得自己没有真正的掌握,在这里,通过自己认真思考整理一些常用的方法。
跨域的产生

不用多讲,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。浏览器引入同源策略主要是为了防止XSS,CSRF攻击。

CSRF(Cross-site request forgery),跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

在同源策略影响下,域名A向域名B发送Ajax请求,或操作Cookie、LocalStorage、indexDB等数据,或操作dom,js就会受到限制,但请求css,js等静态资源不受限制

跨域的解决方案 1 通过jsonp跨域

首先说一下jsonp的原理,例如我们平时写html的时候常常会使用
这种方式去取放在另外服务器上的静态资源,这个是不受同源策略所限制的,所以我们利用这一点可以解决跨域的问题。

主要代码如下:

1.1原生实现
在www.a.com域名写下如下代码,去请求www.b.com域名的数据

这里,我们利用动态脚本的src属性,变相地发送了一个http://www.b.com/getdata?call...。这时候,b.com页面接受到这个请求时,如果没有JSONP,会正常返回json的数据结果,像这样:{ msg: "helloworld" },而利用JSONP,服务端会接受这个callback参数,然后用这个参数值包装要返回的数据:demo({msg: "helloworld"});

这时候,如果a.com的页面上正好有一个demo 的函数:

function demo(res){

  console.log(res);

}

当远程数据一返回的时候,随着动态脚本的执行,这个demo函数就会被执行。

1.2 jquery ajax请求实现
$.ajax({
    url:"http://www.b.com/getdata",
    type:"get",
    dataType: "jsonp",  // 请求方式为jsonp
    jsonpCallback: "demo", // 自定义回调函数名
    data: {}
});

服务端代码实现:

以nodejs为例

var http = require(http);
//引入url模块解析url字符串
var url = require("url);
//引入querystring模块处理query字符串
var querystring = require("querystring");

var server = http.createServer();

server.on("request",function(req,res){
    var urlPath = url.parse(req.url).pathname;
    var param = querystring .parse(req.url.split("?")[1]);
    
    if(urlPath === "/getData" && param.callback) {
    
        res.writeHead(200,{"Content-Type","application/json;charset=utf-8"});
        
        var data = { msg: "helloworld" };
        data = JSON.stringify(data );
        
        var callback = param .callback+"("+data+");";
        res.write(callback);
        
        res.end();
    } else {
        res.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
        
        res.write("Hell World
");
        res.end();    
    
    }


})
jsonp缺点:只能使用get请求,不推荐使用
2 CORS 跨域资源共享
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器  让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

Cross-Origin Resource Sharing跨域资源共享,应该算是现在比较推荐的跨域处理方案.不仅适用于各种Method,而且更加方便和简单
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

2.1 简单请求和非简单请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求同时满足以下条件,只要不满足以下条件的则为非简单请求

非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。
2.2 进行带有身份凭证的CORS 请求

默认情况下的跨域请求都是不会把cookie发送给服务器的,在需要发送的情况下,如果是xhr,那么需要设置xhr.withCredentials=true,

如果是采用fetch获取的话,那么需要在request里面设置 credentials:"include",

但是如果服务器在预请求的时候没返回Access-Control-Allow-Crenditials:true的话,那么在实际请求的时候,cookie是不会被发送给服务器端的,要特别注意对于简单的get请求,不会有预请求的过程,

那么在实际请求的时候,如果服务器没有返回Access-Control-Allow-Crenditials:true的话那么响应结果浏览器也不会交给请求者

对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin
的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为
http://www.a.com,则请求将成功执行。

2.3 HTTP 响应首部字段

Access-Control-Allow-Origin: | *

Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单

Access-Control-Max-Age 头指定了preflight请求的结果能够被缓存多久

Access-Control-Allow-Credentials
头指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。

Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。

Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

2.4 以nodejs express为例,说明如何使用cors解决跨域
var express=require("express");
var url=require("url");
var app=express();
var allowCrossDomain = function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "http://localhost:63342");
    res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
    res.header("Access-Control-Allow-Headers", "Content-Type");
    res.header("Access-Control-Allow-Credentials","true");
    next();
};
app.use(allowCrossDomain);
app.get("/getData",function (req,res,next) {
    var queryValue=url.parse(req.url).query;
    if(queryValue==="fortunewheel@sina.com"){
        res.send(true);
    }else {
        res.send(false);
    }

});
app.listen(3001);
实际开发过程中,为了安全,会和token一起使用
3 window.postMessage

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

iframe嵌套页面跨域通信

页面和其打开的新窗口的通信

多窗口之间消息传递

用法:
postMessage(data,origin)方法接受两个参数,

data:需要传递的数据,html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin:协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

代码示例:
http://www.a.com/a.html


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

4 document.domain

这种方式只适合主域名相同,但子域名不同的iframe跨域。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

使用方式:
http://www.a.com/a.html


"http://www.child.a.com/b.html

5 window.name
window.name 传输技术的基本原理: 
当在浏览器中打开一个页面,或者在页面中添加一个iframe时即会创建一个对应的window对象,当页面加载另一个新的页面时,window.name的属性是不会变的。这样就可以利用在页面动态添加一个iframe然后加载数据页面,在数据页面将需要的数据赋值给window.name。然而此时承载的iframe的parent页面还是不能直接访问不在同一域下的iframe的那么属性,这时,只需要将iframe再加载一个与承载页面同域的空白页面,即可对window.name进行数据读取。
通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

具体实现:
http://www.a.com/a.html 主页面
http://www.b.com/b.html 数据页面
http://www.a.com/proxy.html 代理页面

http://www.a.com/a.html代码:

http://www.b.com/b.html代码:

    window.name = "123"

http://www.a.com/proxy.html空白

6 nginx代理跨域
server{
    # 监听8080端口
    listen 8080;
    # 域名是localhost
    server_name localhost;
    #凡是localhost:8080/api这个样子的,都转发到真正的服务端地址http://www.b.com:8080 
    location ^~ /api {
        proxy_pass http://www.b.com:8080;
    }    
}
配置之后就不需要前端做什么修改了,一般我们在前后端分离项目中开发阶段会采用这种方式,但不是所有场景都能这样做,例如后端接口是一个公共的API,比如一些公共服务获取天气什么的。
7 WebSocket协议跨域

websoket协议天然支持跨域,你只需要学会如何使用它即可,关于websocket协议请看我的另外一篇文章WebSocket网络通信协议

参考文章:
https://developer.mozilla.org...
https://segmentfault.com/a/11...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/53418.html

相关文章

  • 理解域及常用解决方案

    摘要:跨域的产生不用多讲,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。浏览器引入同源策略主要是为了防止,攻击。其指明了实际请求所允许使用的方法。 跨域,相信大家无论是在工作中还是在面试中经常遇到这个问题,常常在网上看到别人所整理的一些方法,看似知道是怎么回事,但如果没有动手实践过,总觉得自己没有真正的掌握,在这里,通过自己认真思考整理一些常用的方法。 跨域的产生 ...

    paney129 评论0 收藏0
  • 理解域及常用解决方案

    摘要:跨域的产生不用多讲,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。浏览器引入同源策略主要是为了防止,攻击。其指明了实际请求所允许使用的方法。 跨域,相信大家无论是在工作中还是在面试中经常遇到这个问题,常常在网上看到别人所整理的一些方法,看似知道是怎么回事,但如果没有动手实践过,总觉得自己没有真正的掌握,在这里,通过自己认真思考整理一些常用的方法。 跨域的产生 ...

    wemallshop 评论0 收藏0
  • 前端域及解决方案

    摘要:但是如果是一级域名相同,二级及以上域名不同的网页可以通过设置来共享。设置有两种方式前端脚本中设置服务器接口设置时指定所属的域名为一级域名。服务器检查过预检请求头之后,确认允许跨域请求,就可以做出回应。 一、跨域问题产生的原因 根本原因是由于浏览器的同源政策。 1.1.同源政策 同源政策由网景公司(Netscape)1995年引入浏览器。目前所有浏览器都实行这个政策。所谓同源是指三个相同...

    wayneli 评论0 收藏0
  • 关于javascript域及JSONP的原理与应用

    摘要:因为同源策略的限制,我们不能在与外部服务器进行通信的时候使用。这个是跨域服务器取数据的接口,参数为回调函数的名字,返回的格式为原理首先在客户端注册一个然后把的名字传给服务器。 一、同源策略 同源策略,它是由Netscape提出的一个著名的安全策略,现在所有的可支持javascript的浏览器都会使用这个策略。 为什么需要同源策略,这里举个例子: 假设现在没有同源策略,会发生什么事...

    CoderBear 评论0 收藏0
  • jsonp跨域获取数据实现百度搜索

    摘要:同源策略做了很严格的限制,但是在实际的场景中,又确实有很多地方需要突破同源策略的限制,也就是我们常说的跨域。使用跨域由于同源策略,一般来说位于的网页无法与不是的服务器沟通,而的元素是一个例外。 本菜鸡最近在写某个页面请求数据时,报了如下的错误。 Failed to load https://...:No Access-Control-Allow-Origin header is pre...

    Tikitoo 评论0 收藏0

发表评论

0条评论

only_do

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<