美文网首页
关于浏览器跨域访问

关于浏览器跨域访问

作者: lionel880 | 来源:发表于2018-08-06 16:10 被阅读0次

生产上上线了展示能力输出功能,此功能将把内部的H5页面,在其他APP可以嵌入,涉及到跨域访问的问题
进行总结
参考文档:
跨域资源共享 CORS 详解
跨域的那些事儿

1.什么是跨域

别笑,之前我还真不知道
什么是跨域?
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。

所谓同源是指,域名,协议,端口均相同,不明白没关系,举个栗子:
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虽然都指向本机,但也属于跨域。
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。

2.浏览器的同源策略

同源策略又分为以下两种

  • DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
    XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
  • 只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

3.为什么我们需要跨域限制

主要是出于安全的考虑
AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:

  1. 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com向用户的cookie中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
  3. http://evil.comhttp://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
  5. 而且由于Ajax在后台执行,用户无法感知这一过程。

DOM同源策略也一样,如果iframe之间可以跨域访问,可以这样攻击:

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

4.如何做到合理的跨域访问---CORS

理论基础:CORS:”跨域资源共享”(Cross-origin resource sharing),这是一个W3C标准
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信

  • CORS 机制
    浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
    具体可参考开头的阮一峰的参考文献

简要来讲,非简单请求多了一个预检的操作

4.1 预检会是一个OPTIONS的请求,例如

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...`

4.2 服务器需要对预检请求进行回应

服务器收到"预检"请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

> HTTP/1.1 200 OK
> Date: Mon, 01 Dec 2008 01:15:39 GMT
> Server: Apache/2.0.61 (Unix)
> Access-Control-Allow-Origin: http://api.bob.com
> Access-Control-Allow-Methods: GET, POST, PUT
> Access-Control-Allow-Headers: X-Custom-Header
> Content-Type: text/html; charset=utf-8
> Content-Encoding: gzip
> Content-Length: 0
> Keep-Alive: timeout=2, max=100
> Connection: Keep-Alive
> Content-Type: text/plain

如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

(3)Access-Control-Allow-Credentials

该字段与简单请求时的含义相同。

(4)Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

4.3 浏览器的正常请求和回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
下面是"预检"请求之后,浏览器的正常CORS请求。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面头信息的Origin字段是浏览器自动添加的。

下面是服务器正常的回应。

> Access-Control-Allow-Origin: http://api.bob.com
> Content-Type: text/html; charset=utf-8

特别注意!!!

上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。

5 作为服务器开发者,我们到底怎么做的

5.1 我们需要做什么

可以从上文的流程中看到,服务器的开发者,需要做的时候分2步

  • 1.在预检请求中,添加信息,并返回200/204
    204是一个没有响应体的成功响应

  • 2.在后续的请求中,继续添加Access-Control的信息

5.2 实际操作

生产我们用Nginx作为反向代理,所以我们需要再Nginx进行处理

  • 一个普通的Nginx配置
server {
        listen       80; #监听80端口,可以改成其他端口
        server_name  localhost; # 当前服务的域名

        location ~ *.json { 
            proxy_pass  你的服务器;
         }

我们需要处理逻辑的条件是2个,域名+是否为opation请求
所以我们Nginx需要对这2个条件进行判断,本来是很简单的事情,但

!!!nginx不支持多重判断

所以,配置成了这样


server {
        listen       80; #监听80端口,可以改成其他端口
        server_name  localhost; # 当前服务的域名

        location ~ *.json { 
            proxy_pass  你的服务器;
                       set $flag 0;
                
            if ($http_origin ~ (域名A| 域名B)){
                set $flag "${flag}1";
            }
            if ($request_method = 'OPTIONS'){
                set $flag "${flag}2";
            }
            if ( $flag = "012" ){
                  add_header 'Access-Control-Allow-Origin' '$http_origin';
                                  add_header 'Access-Control-Allow-Credentials' 'true';
                                  add_header 'Access-Control-Allow-Headers' 'X-PINGOTHER,Content-Type,Accept,Origin,User-Agent,Cache-Control,isOutput';
                                  add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
                    
                      return 204;
            }


             if ( $flag = "01" ){
                                add_header 'Access-Control-Allow-Origin' '$http_origin';
                                  add_header 'Access-Control-Allow-Credentials' 'true';
                                  add_header 'Access-Control-Allow-Headers' 'X-PINGOTHER,Content-Type,Accept,Origin,User-Agent,Cache-Control,isOutput';
                                  add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
                    
                        }


         }

千万别忘了,opation预检请求后,仍然要添加信息,只是不需要直接返回204而已

6.跨域和CDN的坑

当一切在开发环境和测试环境验证无误后,上生产发现了新的问题。
以A域名为例,已经在Nginx上配置了A域名可以跨域

  • 问题的发现:
    生产验证环节,发现A域名下部分网址可以正常响应,部分网址无法正常相应。
    正常跨域请求的Options请求和Get请求都可以获得服务器配置的请求头,有Access-Control各个字段,而不正常的请求,只有Options请求有,Get请求没有,怀疑是Get请求没有达到服务器

  • 6.1 第一步
    观察后发现,不能正常响应的请求,都是.sjson结尾,此结尾代表着和CDN厂商规定的静态接口。因此怀疑是CDN的问题。
    将不好的请求,改变Url的某个时间戳参数,请求正常执行。因此CDN是肯定是原因之一

  • 6.2第二步
    如果问题都在CDN上,则是因为请求到CDN层直接击中缓存的原因,没有达到服务器层获得最新的跨域配置

继续观察:在 .sjson结尾的请求中,也有部分参数请求是好的,部分请求参数是不好的
那么第二个问题来了,请求要么全是好的,要么全是不好的,为何会有概率的发生这种情况

因此怀疑是部分Nginx配置有误,进行排查,发现所有服务器都配置都已正常替换,服务器重新时间都在版本当晚
(这里因为CDN的缓存机制不了解,因此思考出现了问题,就卡住了)

  • 6.3 第三步
    联系CDN服务厂商,了解了CDN的推送策略有2种,公司的默认是第二种
    一是物理上直接将目录下资源删除
    二是将CDN目录下的资源置为过期,CDN会回源,比较新的资源是否变化,如果没变化,则继续使用,有变化,则更新

当时立马让厂商进行了第一种方式的删除,而没有经过分析,第二次错过了发现问题的机会。
厂商删除目录下资源后,再次访问,发现.sjson 的请求,还是部分正常 部分不正常

  • 6.4第四步
    陷入僵局后,再次搜寻资料,病急乱投医,包括怀疑CDN节点不同步等等。
    这里经CDN厂商提醒,跨域请求的网址,正常不跨域的请求也会访问,2份缓存是同一份,可能存在这个问题,这样就可以解释为什么部分请求成功,部分不成功的问题。因为清楚缓存后,哪个请求先到,就会缓存哪份

  • 6.5五.如何验证
    厂商同事,用有问题的请求,分别发送了带Origin字段和不带Origin的字段,用md5计算返回报文,比较发现一致。确认是同一份缓存。

  • 6.6六.如何解决
    一是前端特殊处理,跨域的请求,加特殊的字段参数,但这需要修改代码
    二是CDN厂商提供的,根据http请求头里的Origin字段,为每个值维护一份缓存
    最终选择了第二个方案

  • 6.7继续测试
    为了不影响生产,对第二个方案进行测试
    厂商提供了一个配置了新的规则的测试CDN节点

比较请求:
curl -vo ~/tmp 'https://36.250.240.133/*****.sjson?*****&updTs=20180706004105' -H "Host:访问的域名" -k -H "Origin: 跨域的域名"

curl -vo ~/tmp 'https://36.250.240.133/*****.sjson?*****&updTs=20180706004105' -H "Host:访问的域名" -k -H "Origin: 不跨域的域名"

这样就是2份缓存了,可以比较返回的报文,最终解决了问题。

  • 6.8反思点
    这里有对CDN缓存机制不了解的原因,虽然知道CDN回源策略有定时,也有Url改变,但没有想到,这个请求头里的参数是不比较的。同样,以后CDN的接口如果有cookie等请求头信息,都要注意。不过静态请求也不应该包含那些变化的东西。
    在了解了CDN推送的策略时,其实就应该想到过期后,CDN回源比较没更新,说明了请求的返回报文就是和跨域配置改变前是一样的,这就是同一份缓存

相关文章

  • SpringMVC 进行ajax跨域请求访问

    关于 springmvc 3.x 版本对ajax跨域请求访问 ajax 请求后,浏览器出现跨域的问题那么在当前环境...

  • 跨域分析

    浏览器为什么会产生跨域访问安全问题 浏览器的限制 浏览器为了安全考虑会限制不同域下的请求资源访问. 跨域的简单请求...

  • 前端使用nginx解决浏览器跨域

    一. 浏览器跨域 浏览器跨域限制,学名浏览器同源策略,其实是为了数据安全,由Netscape提出来限制浏览器访问跨...

  • 同源策略,跨域请求处理

    跨域访问 - 跨域请求 同源策略 适用于浏览器的一种资源访问策略;同源策略(Same origin policy)...

  • H5跨域访问

    跨域访问是源于浏览器的同源策略而引申出来的概念 1、先了解什么是同源策略和跨域访问 同源策略、跨域解决方案 - R...

  • 前端ajax跨域请求方案沙里淘金

    1. 所谓跨域 跨域是一种浏览器同源安全策略,也即浏览器单方面限制脚本的跨域访问。很多人可能误认为资源跨域时无法请...

  • Ajax的跨域问题

    什么是跨域及来源 跨域问题来源于浏览器的同源策略,JavaScript只能访问和操作自己域下的资源,不能访问和操作...

  • 本地调试 -- Mac Chrome 解决跨域-CORS-问题

    一般本地调试的时候,某些资源需要开启跨域访问mac chrome浏览器解决跨域(CORS)问题, 跨域直接使用插件...

  • window.URL 与 跨域

    跨域 关于跨域问题,简单来说就是通过地址访问资源时,所用的协议不同导致无法访问目标。 网上已经有很多关于跨域的主流...

  • 跨域

    什么是跨域 所谓的域是指:域名端口协议跨域是指:访问不同域的文件 为什么会存在跨域的问题 浏览器对于javascr...

网友评论

      本文标题:关于浏览器跨域访问

      本文链接:https://www.haomeiwen.com/subject/dlrgvftx.html