如何解决跨域问题

最近项目中碰到了这个问题,“如何解决跨域问题?”,我根据网上的资料和实际代码中的应用大概总结如下。

首先我们要理解什么跨域,为什么会出现跨域呢?

跨域

        跨域是由浏览器的同源策略引起的,跨域访问,简单来说就是 A 网站的 js脚本代码试图去访问 B 网站(代码中包括提交内容和获取内容)。但是由于安全原因,跨域访问是被各大浏览器所默认禁止的,而跨域与否的判断是根据同源策略来的。

同源策略
        是由Netscape提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源(origin)中加载文本或者脚本与来自其他源(origin)中资源的交互方式,这之中的同源是指协议、域名、端口这三者都要相同(即便两个不同的域名指向同一个ip地址,也非同源)。当浏览器执行一个js脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域。

这里同源策略又分为以下两种:
  1. DOM同源策略:禁止对不同源页面的DOM进行操作。主要的场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
  2. Ajax(XmlHttpRequest)同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。

接下来我们举例区分是否跨域:

只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)

http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)

http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)

http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)

http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。


那么跨域会导致什么呢?

跨域限制主要是为了安全考虑,所以如果网站没有跨域限制,会有什么后果呢?:

  • Ajax(XmlHttpRequest)同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:
  1. 用户登录了自己的银行页面 mybank.commybank.com向用户的cookie中添加用户标识。
  2. 用户浏览了恶意页面 evil.com。执行了页面中的恶意AJAX请求代码。
  3. evil.commybank.com发起AJAX HTTP请求,请求会默认把mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
  5. 而且由于Ajax在后台执行,用户无法感知这一过程。


  • DOM同源策略也一样,如果iframe之间可以跨域访问,可以这样攻击:
  1. 做一个假网站,里面用iframe嵌套一个银行网站 mybank.com
  2. 把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

所以说有了跨域跨域限制之后,我们才能更安全的上网了。


那么我们开发中如果解决跨域呢?

还有其他几种办法,但是感觉这几种相对来说比较常用。

一、 通过jsonp跨域

二、 跨域资源共享(CORS)

三、 nginx代理跨域
四、 nodejs中间件代理跨域
五、 WebSocket协议跨域

具体解决方案

一、 通过jsonp跨域 

    这是最常见的一种跨域方式,其背后原理就是 : 利用了<script>标签不受同源策略的限制,在页面中动态插入了script,script标签的src属性就是后端api接口的地址,并且是通过get的方式将前端回调处理函数名称告诉后端,后端在响应请求时会将回调返还,并且将数据以参数的形式传递回去。

简单来说:就是通过动态创建script标签,然后利用src属性进行跨域。

代码解释:

1.)原生实现:

 <script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参并指定回调执行函数为onBack
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
    document.head.appendChild(script);

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

服务端返回如下(返回时即执行全局函数):

onBack({"status"true"user""admin"})

2.)jquery ajax:

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

好了总结一下用jsonp请求数据的基本流程
  1. 首先在客户端注册一个callback, 然后把callback的名字传给服务器。

  2. 服务器先生成 json 数据。

  3. 然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.

  4. 将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

  5. 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

jsonp的缺点
  1. 没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求.

  2. JSONP 的另一个主要缺陷是被不信任的服务使用时会很危险。因为 JSONP 服务返回打包在函数调用中的 JSON 响应,而函数调用是由浏览器执行的,这使宿主 Web 应用程序更容易受到各类攻击。

要注意JSONP只支持GET请求,不支持POST请求。所以使用该方式的缺点也就是:请求方式只能是get请求。

二、 跨域资源共享(CORS)
setHeader("Access-Control-Allow-Origin", "*") # 允许所有域名访问,或者

setHeader("Access-Control-Allow-Origin", "http://test.com")# 允许test.com这个域名发过来的所有请求。

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。如果想实现当前页cookie的写入,可参考下:3、nginx反向代理中设置proxy_cookie_domain 和 4、NodeJs中间件代理中cookieDomainRewrite参数的设置。

目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

这里我就简单的说一说大体流程。

  1. 对于客户端,我们还是正常使用xhr对象发送ajax请求。
    唯一需要注意的是,我们需要设置我们的xhr属性withCredentials为true,不然的话,cookie是带不过去的哦,设置: xhr.withCredentials = true;
  2. 对于服务器端,需要在 response header中设置如下两个字段:
    Access-Control-Allow-Origin: yourhost.com
    Access-Control-Allow-Credentials:true
    这样,我们就可以跨域请求接口了。
三、 nginx代理跨域
1、 nginx配置解决iconfont跨域

浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。

location / {
  add_header Access-Control-Allow-Origin *;
}
2、 nginx反向代理接口跨域

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

nginx具体配置:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}
四、 Nodejs中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

五、 WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。


总结:最常用的是Jsonp和CORS跨域方式。

我这里放一篇文章https://segmentfault.com/a/1190000011145364,写的很详细,有兴趣的可以去看看,让我对于跨域的理解更深入了,膜拜大神!


  • 10
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值