关闭

Nginx缓存引发的跨域惨案(转:https://www.baidu.com/home/news/data/newspage?nid=9966642810298490574&n_type=0&p_f)

907人阅读 评论(0) 收藏 举报
分类:

【51CTO活动】8.26 带你与清华大学、搜狗、京东大咖们一起探讨基于算法的IT运维实践

1. 前言

贵金属wap版直播间上线后,偶尔有用户反馈,在进入wap直播间的时候,出现空白页面,但是重新刷新又可以正常显示了。我们曾一度认为是网络请求异常或兼容问题,直到开发PC版直播间,在进行调试中,同样遇到了“白屏”问题,才引起了足够重视,并进行了问题跟踪与分析。现在跟大家分享一下,这种偶然现象出现的原因。

我们的直播间落地页在fa.163.com 系统,而直播间内容,是通过 向直播间系统 qz.fa.163.com 发起Ajax请求获取的。在出现“白屏”的时候,可以通过浏览器的调试窗口,可以看到出现下面的报错

2. 问题分析

从上述错误提示文案中可以看到,问题首先和 跨域 有关。

何为跨域

从字面上理解为“跨域名”,浏览器不能执行其他网站的脚本,然而,跨域不仅仅局限于域名这一项。只要协议、域名、端口有任何一个不同,都被当作是不同的域。 这是由于>同源策略的限制,从一个域上加载的脚本不允许访问另外一个域的文档属性。虽然在浏览器中,<script>、<img>、<iframe>、<link>等标签都>可>以加载跨域资源,而不受同源限制,但浏览器会限制脚本中发起的跨域请求。比如,使用 XMLHttpRequest 对象和Fetch发起 HTTP 请求就必须遵守同源策略。

同源策略/SOP(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。SOP要求两个通讯地址的协议、域名、端口号必须相同,否则两个地址的通讯将被浏览器视为不安全的,并被block下来。

举个例子:从贵金属主站 http://fa.163.com 发起请求访问以下url:

解决跨域

在实际应用中有多种方式来解决跨域问题,相信在实践中都会用到其中的某些方案:

(1).JSONP (无状态连接,不能获悉连接状态和错误事件,而且只能走GET的形式)

(2).iframe形式

(3).服务器代理

页面直接向同域的服务端发请求,服务端进行跨域处理或爬虫后,再把数据返回给客户端页。

(4).CORS

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就>是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。目前,所有浏览器都支持该功能,IE浏览器不能低>于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏>览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

CORS方式实现:

浏览器在发出CORS请求时会在头信息之中增加一个Origin字段;后端返回代码中增加三个字段

header(“Access-Control-Allow-Origin”:“”); // 必选 允许所有来源访问 header(“Access-Control-Allow-Credentials”:“true”); //可选 是否允许发送cookie header(“Access-Control-Allow-Method”:“POST,GET”); //可选 允许访问的方式

nginx是一个高性能的web服务器,常用作反向代理服务器。nginx作为反向代理服务器,就是把http请求转发到另一个或者一些服务器上。通过把本地一个url前缀映射到要跨域访问的web服务器上,就可以。

为了解决跨域问题,我们选择方案d , 在直播间的过滤器中,统一添加了如下代码:

<a href='http://www.jobbole.com/members/wx610506454'>@Override</a> public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 加入响应头 String origin = request.getHeader("Origin"); if("http://fa.163.com".equals(origin) || "https://fa.163.com".equals(origin) ) { response.addHeader("Access-Control-Allow-Origin", origin); response.addHeader("Access-Control-Allow-Credentials", "true"); } return true;

从错误提示文案中,我们还可以看到错误提示的关键点 “http://fa.163.com” that is not equal to the supplied origin. Origin ‘https://fa.163.com‘ is therefore not allowed access.

目前我们的系统同时支持http访问和https访问,但是为什么使用 http访问 ,返回的header中却是 https 协议呢?

通过多次模拟,确认出现问题的请求中,Request URL使用的协议和 response返回的headers中的 Access-Control-Allow-Origin 中的 协议确实不一致,且还有一个特性,X-Cached 为 HIT,如下图:

命中了缓存的请求,出现了协议不一致?

突然想到,这个接口,我们配置了nginx 缓存,那必然和nginx缓存有关了。

Nginx 缓存

Nginx (engine x) 是一个高性能的HTTP和反向代理服务器。

首先从源服务器(内部网络上的web服务器)上获取内容,然后把内容返回给用户,同时,也会把内容保存到代理服务器上一份,这样日后再接收同样的信息请求时,他会把本地缓存里的内容直接发给用户,以此减少后端web服务器的压力,提高响应速度。这其实就是缓存服务器所实现的功能。如下图所示。

进入直播间后,首先需要查询直播内容是否有更新,而这个接口客户端会以5s间隔轮询,为了减少tomcat的压力,我们配置了nginx缓存。配置如下

其中:

proxy_cache_methods: 用来设置HTTP哪些方法会被缓存,直播间接口配置了GET、HEAD、POST;

proxy_cache_valid: 用来设置对不同HTTP状态码的不同缓存时间。直播间接口配置了对于 返回值为200的状态码,缓存5秒;

proxy_cache_min_uses: 用来设置多少次访问后,应答值会被缓存,配置为3次;

proxy_cache_key: 设置Web缓存的key

proxy_cache: 用来设置哪个缓存区将被使用,并定义缓存区的名称

通过上述配置,我们可以看到 proxy_cache_key 配置中,只配置了host + uri + 参数,但没有配置协议,所以无论用http访问,还是https访问,只要被缓存后,返回的内容都是一样的,而不会区分http或https。从而引起了跨域问题。

至此,问题分析完毕。

3. 问题解决

跟运维同学沟通后,通过修改nginx配置,将协议类型scheme加入到缓存查找的判断参数中,配置如下。

问题得到了解决。

4. 总结

上述“惨案” ,是 跨域、nginx缓存、http/https协议 这三种条件同时出现引发的。

如果不涉及跨域,混用 http/https协议 + nginx缓存,其实也是没有问题的。但是一旦出现了跨域使用,必须 在nginx 缓存配置中,配置 scheme + host + uri + 参数。

0
0
查看评论

ESB之Mule ESB的使用

概要说明:          本次的ESB使用的是Mule ESB,其官网是https://developer.mulesoft.com/,目前官网提供的社区版和企业版,企业版是收费的,功能齐全,本次开发使用的是社区版本...
  • lh2420124680
  • lh2420124680
  • 2017-07-21 17:41
  • 1344

一个比较好的网站

 https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_7063379539205117040%22%7D&n_type=0&p_from=1
  • yangniao
  • yangniao
  • 2017-11-23 08:47
  • 54

MySQL 5.7.18的安装与主从复制(转自:https://www.baidu.com/home/news/data/newspage?nid=9485770887287731252&n_typ)

CentOS6.7安装mysql5.7.18  1、 解压到/usr/local目录    # tar -zxvf mysql-5.7.18-linux-glibc2.5-i686.tar.gz -C /usr/local  2、 mysql-5.7.18-linux-glibc2.5-i686文件...
  • toto1297488504
  • toto1297488504
  • 2017-08-07 22:53
  • 1089

Nginx 缓存引发的跨域惨案

1. 前言 贵金属wap版直播间上线后,偶尔有用户反馈,在进入wap直播间的时候,出现空白页面,但是重新刷新又可以正常显示了。我们曾一度认为是网络请求异常或兼容问题,直到开发PC版直播间,在进行调试中,同样遇到了“白屏”问题,才引起了足够重视,并进行了问题跟踪与分析。现在跟大家分享...
  • jek123456
  • jek123456
  • 2017-08-22 09:09
  • 172

新闻发布项目——前台JSP界面newspages/news_read.jsp

新闻中国 function check(){ var cauthor = document.getElementById("cauthor"); var content = document.getElementById("ccon...
  • qq_34137397
  • qq_34137397
  • 2016-11-25 00:20
  • 1112

跨域问题,解决方案 - Nginx反向代理

原文地址:跨域问题,解决方案 - Nginx反向代理 博客地址:http://blog.720ui.com/ 跨域问题,解决之道 链接文章:跨域问题,解决之道 解决思路 跨域问题,是由于JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。如果,我们将不同的域名整合到一个域,换句话...
  • helloxiaoliang
  • helloxiaoliang
  • 2016-12-13 12:56
  • 1970

最简单实现跨域的方法----使用nginx反向代理

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。 现在随着RESTFUL的流行,很多应用提供http/https接口的API,通过xml/json格式对外提供服务,实现开放架构。 Web应用也在向单页面方向发展。 越来...
  • shendl
  • shendl
  • 2015-09-14 18:54
  • 24062

跨域getJson遇到的问题

在项目中遇到的这个问题.跨域要注意的两点:(1)必须是get方式;(2)必须是json格式. 跨域直接用的jquery的getJson,那么后台返回的数据必须是json格式,同时,在url添加callback参数: 例: $.getJSON('http://www.baidu.com?jso...
  • helloliuhai
  • helloliuhai
  • 2014-03-03 15:11
  • 954

nginx跨域资源访问(vue.js)

add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header '...
  • boolbo
  • boolbo
  • 2016-09-02 15:18
  • 653

用 Nginx 实现 https 转 http

缘由当前公司服务器已经采用 http 协议的方式部署成功,可 App Store 要求必须采用 https 协议,那么,能否在不改变公司服务器代码的情况下,实现 https 的要求呢?答案是肯定的,采用 Nginx 反向代理实现(以代理服务器来接受internet上的连接请求,然后将请求转发给内部网...
  • lvye1221
  • lvye1221
  • 2016-12-23 20:28
  • 10079
    个人资料
    • 访问:2393190次
    • 积分:33992
    • 等级:
    • 排名:第159名
    • 原创:969篇
    • 转载:516篇
    • 译文:0篇
    • 评论:225条
    文章分类
    博客专栏
    在线文档
    QT官网