互联网应用开发的用户登录

图片来自pixabay.com的Pexels-2286921会员

在互联网应用开发中,用户登录是一个必要的安全组件,其涉及到认证、授权、鉴权和权限控制等各个安全环节,在实际的应用场景中有单点登录的需求。为了更加全面的介绍互联网应用开发的用户登录技术,下面将分三篇独立的文章对相关的概念和架构实现进行梳理介绍。

注:本文讨论的互联网应用开发,主要是指web应用和移动应用的开发。

1. 认证、授权、鉴权和权限控制

介绍这四个概念的定义及其之间相互关系。

请点击这里查看。

2. 单点登录的通用架构实现

单点登录(single sign on)也被称为统一登录,它不仅仅是一个用户登录功能模块,它更是一种完整的技术架构方案,提供统一的认证、授权、鉴权和权限控制功能,方便用户的使用,以及应用的开发接入。

本文将对单点登录的通用技术架构进行介绍,主要分为第一方登录、第三方登录、分布式和集中式权限控制等这四大方面的架构实现。

请点击这里查看。

3. OAuth 2.0授权协议简介

作为一种授权代理协议,OAuth 2.0被广泛用于第三方开发平台中,尤其是第三方登录的应用场景。本文将详细介绍OAuth 2.0授权协议的四种授权模式,以及各个模式的应用场景、联系和区别,最后介绍在实践中如何选择合适的授权模式。

请点击这里查看。

读《人类简史》

从猿猴到人

简单的来看人类的历史,是一个从几十万年前生物进化,到延绵至今的资源掠夺、经济发展、社会群体组织、文化交流、宗教信仰,以及到最近500年科技革命的过程,每一个过程都波澜壮阔。人类一步一步走到现在,历史创造了人,人也创造着历史,这是一部前所未有的“历史”史诗。

《人类简史》以认知革命(cognitive revolution),农业革命(agricultural revolution),科学革命(scientific revolution)三大部分梳理着人类的发展脉络。认知革命标志着人类的生物进化到可以思考的能力;农业革命让人类从采集生活到农耕文化的转变,在这个阶段,得益于思考能力,人类社会的政治、经济、文化、宗教都得到蓬勃发展;而近500年的科技革命,人类在探索无知的过程中,对自己、对世界的认识达到前所未有的高度。

1、生物进化

在大约135亿年前,在一次“宇宙大爆炸”之后,形成了宇宙的物质、能量、时间和空间,这些宇宙的基本特征,成了“物理学”。

这之后过了30万年,物质和能量形成复杂的结构,称为“原子”,再之后形成“分子”,原子和分子的故事成了“化学”。

大约50亿年前,原子和分子在一起形成物质,构成了一个叫地球的行星,这个地球的故事,成了“地质学”。

大约38亿年前,在地球上各种物质交融,其中碳氢氧(C-H-O)三种最简单的元素形成早期海洋的水和有机物,然后慢慢孕育出复杂而又精细的生命结构,这个生命结构称为“有机体”,有机体的故事就成了“生物学”。

从此一幅漫长的生物进化画卷开始了。

人是猿进化而来,在两百万年前,人类和猿其实是兄弟两,只是分家后,人类的祖先选择了到陆地上生活,而猿的祖先选择了继续呆在树上。若考虑更早远的时候,我们和猪也是属于同一祖先而来,在基因上,我们和它们有80%多的相似度,以致于到现在,医学界都在研究如何从猪身上移植器官到人身上。

人类的祖先来到了陆地之后,脑袋进化变得更大,变得会思考,开始会使用工具。从此,人类开始会使用火,驱寒、寻暖、烧烤食物;有了群落,开始了集体采猎的生活,会在区域内迁徙,根据时令采集食物;随着物产的丰富,生产物品的交易也逐渐多了起来,也有了简单的经济。

人类的脑袋,是一个了不起的进化。现在人的脑容量一般在1500ml左右,东非智人的平均脑容量在1400-1600ml,德国尼安德特人的脑容量在1200-1750ml,而类人猿仅为400-500ml,相比之下,可以看到人类的脑袋在进化过程中脑容量的巨大变化。还有一个事实是脑袋消耗的能量惊人,人的大脑占体重的2-3%,在身体休息不活动时,大脑的能量消耗却占比25%,其他类人猿的大脑在休息时消耗大约只有8%。

有了会思考的大脑,人类可以想象出虚拟的神灵,讲虚构的故事,不仅能算微积分,也能识人会意,创造宗教信仰,实现文化传承。从能量转换来说,人类历史上所吃食物的能量,大部分都转为了人类的精神活动,毫无疑问,知识艺术是人类在历史长河里积累的最大财富。

大约9000年前,在阿根廷的手洞留下了这些手印。我们只能猜测这些手印的含义,是狩猎后的欢庆?还是伸向我们的欢迎之手?

注:图片来自360Doc图书馆 - 平图拉斯河手洞,见参考资料。

2、社会群体

这里的社会群体是指一个人类群体,其有着紧密的经济和文化联系,整个群体有着一定的控制和管理,发生着有规则的行为。

社会群体的形成方式,其主要沿着三个方向各自独立发展,融合不同规模的群体。

2.1 生物遗传

生物学上,以生物遗传维系的一个群体,是一个天然形成的社会组织,其以家族为体系,以身强力壮者为首领,控制着整个群落。

人类的群落和其它生物群落相比,不一样之处在于,首领仅仅有身强力壮是不够的,还要有睿智的头脑,因为群落不仅仅是简单的控制,还会有管理。文化的传承,让年长者不再属于生物优胜劣汰的对象、处于群体的边缘,而是在群落的地位趋于让人尊敬的地位。

一个为生物遗传的人类群落,其控制规模一般在10-100个人。这个群体的发展和家族命运息息相关。

2.2 地理区域及经济辐射

在地理位置上,距离的远近天然切分着社会群体。在人类早期,交通的不便阻碍着人类群体之间的交际沟通,因此,在附近的人类更容易相识,聚集在一起生活,渐渐地,地理位置上附近的人类,会发展出相互依存的经济和文化关系。这种关系以地理位置的远近,分割出不同的人类群体。

当然,随着交通工具的发展,人类可以进行快速交流的距离限制不断被突破,这里的快速交流不仅仅是指见面聊天,更多是可以实现物物交换,发生直接的经济关系。交通工具的进化成为地理区域及经济辐射的推动力。

从人际交流和经济的角度出发,以地理划分出相互依赖的人类群体从一个小村落,发展到同一乡村、同一城市,直到现在的一小时经济圈。

以地理划分出来的人类群体,规模控制在村落、城市级别,人数从上百人的村落,到上百万的城市。这个人类群体的发展和所在地理区域的经济发展息息相关。

2.3 政治和文化

地理位置的远近阻碍着人类群体的融合,但人类群体的发展早已摆脱这种地理位置的束缚。有别于其它生物群体,人类在历史的长河里,逐渐发展为以政治手段和文化方式进行着群体的融合,这是人类群体所独有的融合方式。

以政治手段的融合方式,最典型的便是国家。一个国家,可以通过政治的手段推行统一的经济和文化政策,通过经济和文化两只手维系整个社会的控制和管理,对国家的归属感,体现了一国人民对这个国家的融合程度。

以文化方式的融合方式,最典型的便是宗教信仰。通过共同的文化宗教信仰,将散布四处的人类群体,紧紧地融合在一起,个体甚至为之付出生命都不可惜。

其实,政治和文化两个方式对群体的融合作用,相辅相成。政治更多是从外部影响群体,以强硬的手段控制群体,而文化更多是从内部影响群体,以思想的方式内化群体。两种方式对群体的形成和影响力相比,政治方式更加快速,文化方式虽较慢但更加长久稳固。一场战争能够让一个国家更朝换代,但是想要毁灭一个文化,却没有那么容易,文化流淌在这片土地上人民的血液中,传承不断,生生不息。

以政治和文化划分出来的人类群体,其规模小到十几个人的小团队,大到整个国家、跨区域,人数无上限。

2.4 对比

生物遗传 地理区域 政治和文化
群体规模 10-100 100-上百万 人数无上限
距离远近 家族圈,1公里以内 平常生活圈,10-100公里以内 远近无上限
聚集融合方式 天然的生物方式 天然的地理方式 虚构的方式
和其它生物相比 生物共有属性 生物共有属性 人类所特有

3、资源掠夺

这里的资源掠夺,是指一个人类群体,以符合群体自己利益为目的,对资源进行的强行获取行为,这是一个集体性的行为过程。

对于资源掠夺,最熟悉的莫过于发动战争,这是发生在人类群体内部的资源掠夺。早期人类过着游牧生活,为了生存,游牧的区域是必争之地,弱肉强食,更强大的人类群体击败并赶走弱小的人类群体,这种野蛮掠夺是早期的人类战争形式。

从农业社会开始,人类逐渐开始了定居生活,这时的人类驯化各种动植物,在公元前9500年到公元前3500年驯化的植物有:小麦、稻米、玉米、马铃薯、小米和大麦,到如今,这些食物提供了超过90%的人类食物热量。解决了温饱,人类社会得以不断发展,创造出各种各样的经济财富,人类对经济财富的追求和贪婪,也是在很长一段人类历史中发动战争的理由。

最近的500年,从1500年欧洲发现美洲大陆开始,欧洲对其它各大洲的资源掠夺更是达到疯狂的地步,有多少奴隶从非洲贩卖到美洲,有多少国家成为欧洲帝国的殖民地,欧洲帝国的每一步崛起,无不流淌着其它各国人民的鲜血。

这种以资源掠夺为目的,发生在人类内部的战争,其杀戮性让人不寒而栗。但是,若是看看人类对自然资源进行的更为残暴的物种灭绝行为,人类大多时候无所感觉。

在早期的人类,过着采集狩猎生活,为了温饱,具有一定规模的自然资源掠夺已经开始了。群体的围猎,选择一片草原放一把火,然后在灰烬中寻找食物。要是说这个时期的掠夺都是和平的形式进行,这是低估了人类的破坏力。有充分的动植物化石发现,这些动植物在地球上生活了千万年后,随着人类领域的扩展,但凡有人类足迹踏及之处,无数的动植物便从此灭绝。

以澳大利亚、美洲、各个岛屿的生物灭绝为例。在大约45000年前,人类首次来到澳大利亚,在此后千年间,澳洲当时24种体重在50公斤以上的动物,有23种惨遭灭绝。在14000年前,美洲的动物物种远比今天丰富,然而好景不在,在人类抵达后不到两千年的时间,北美原本有47属的各类大型哺乳动物有34属消失,南美更是60属中消失50属。在大西洋、印度洋、北冰洋和地中海星罗棋布的数千座岛屿上,考古学家都发现了生活着无数世代的昆虫和鸟类,在人类第一次出现后便从此消失。还有更多成千上万的小型动植物,都只在化石上留下自己的痕迹。越来越多的化石证明,人类所到之时,便是当地物种大灭绝的开始。

物种的三波大灭绝,和三次人类的社会活动一一牵连,第一次的人类采集扩张,第二次的农业扩张,第三次的工业和科技扩张。人类坐上生物学史上最致命物种的宝座,实至名归。

都说战争是残忍的,但是人类对自己在灭绝生物物种上,丝毫没有感到一点廉耻悯惜。

4、经济发展

从一个广义的角度来讲,经济是指资源的生产和消费过程,是一种物质资源的转移过程。若从远古时期到现在,整个经济发展将经历如下几个时期,

4.1 物物交换

最早期,人类生产各种物质资料,总会出现有些资料富足,有些资料少缺,这种场景下的问题频繁出现,一个最简单的解决方案,便是物物直接交换。于是最简单的经济便出现了。

等价交换是物物交换的准则,也是一直到现在维系经济活动的准则。

4.2 货币

随着经济发展,物物交换变得多了起来。两个物品之间,只需记1种等价交换公式,三个物品之间,则有3种等价交换公式,四个物品之间,则有6种交换公式,N个物品之间,则有n(n-1)/2个交换公式,公式数量随着物品种类以指数增长。毫无疑问,人类的脑袋是无法应付指数增长的交换公式,于是大多时候交换都比较随心随时。当经济发展到一定阶段,物物交换成了规模以后,如何等价交换成为问题。

幸好的是,人类发现了一般等价物,也就是有些物品可以作为中介,用于衡量等价交换过程。拥有的物品都先交换成一般等价物,然后再换取所需的物品。一般等价物使得交换公式变成线性增长,使得大家都可以有一个共同的换算法则。

在古希腊的历史记载中,牛、羊、谷物等都曾充当过一般等价物。下面的公式是以稻谷作为一般等价物,衡量其它物品的价值,

  • 一头牛 = 100担稻谷
  • 一头猪 = 50担稻谷
  • 一只鸡 = 10 担稻谷

一般等价物让价值换算变得简单。

但很快,人类又发现,用这些牛、羊、谷物充当一般等价物,并不是最完美答案。无论是牛、羊还是稻谷,其流通性都比较受限,不仅如此,它们还会随着地域、时节,其价值会发生着变化,例如在干旱季节粮食歉收,稻谷的价值就会变得更高。

于是,人类有开始寻找着替代品,一直到金、银、铜等重金属的出现。

重金属具有价值高、易分割、易保存、便于携带的优点,这些使得它们在成为一般等价物之后便迅速取代了其它一般等价物,牢牢地占据经济交易过程中的第一交换中介地位。当整个经济交易市场的一般等价物被重金属统一之后,它也就成为了整个市场的货币。

货币的出现,不仅解决了等价交换的换算问题,也使得物物交换便利了很多,大大促进了经济的繁荣发展。

4.3 金融机构

货币的出现,是随着经济市场的繁荣发展而来的必然产物,也促进了经济的发展。当物物交换和等价交换不再是问题,下一个待解决的是,货币本身的流通性问题。

随着国家和帝国的形成,人类的经济活动在地理疆域上也在不断的延伸,货币的政治流通性和地理流通性,都在盼望着一个对货币的统一管理机构,这便促使了金融机构的诞生。

最早期的金融机构是像现代社会的银行,其主要功能之一是向社会提供钱的存取。一个人可以在一个地方存下100两黄金,然后在异地通过凭证取出100两黄金,这样路上就免去了一路携带的问题。这个存取凭证也是现在社会现金的雏形。

银行的出现,不仅仅解决货币的流通性问题,它还培养了经济发展过程中所需的重要基石--信任。当银行存储的钱积累起来,形成资金池,它马上可以向社会提供另外一个主要功能,便是钱的借贷。当用户A存100元现金到银行,银行把这100元借给用户B,随后用户B也把这100元存入银行,银行又可以把这100元借给用户C,可以看到,同样100元,银行把它重复借出两次,甚至多次,以换取利差。对于银行来说,用户A、B、C各自存和借的钱只是银行账上的数字而已。可以看到,银行简直做的是一种无本买卖。这难道没有任何风险么?这个风险是存在的,万一所有的用户都来取现,银行收到挤兑,银行是没有那么多钱供用户取现,到时候银行就只好关门大吉。

但是可以发现,现代的银行还是过的好好的。这最重要的原因便是,用户对银行的信任,以及银行对贷款未来归还的信任。用户相信银行的风险控制,相信银行不会破产,银行相信贷出去的钱一定能收回来。整个信任形成一个链条,这个链条也是整个社会对未来经济的信任。

金融机构在构筑这个信任链条上作出关键的一步,为整个社会的信任奠定基石,这块基石为经济的发展提供源源不断的动力。

4.4 虚拟经济

实体经济和虚拟经济,在本质上,一个是基于物质资源的本身价值,一个是基于对未来经济的信任。

自从有了金融机构,有了货币,人类渐渐构建了基于信任的经济体系,随之而来,便是虚拟经济的慢慢成长,资金借贷市场,股票市场,期货市场,无一不是基于对未来价值的投资。到现代社会,虚拟经济已经是一个不可取代的经济角色,它是整个经济体系的润滑剂,大大加速了资源的整合和流转,最终促进了物物交换。

4.5 物物交换

无论是虚拟经济,金融机构,还是货币的出现,都是最终促进了物物交换,实现了资源交换速度和效率的持续升级。

人类经济发展的涟漪

5、文化中的宗教信仰

相对于政治、经济而言,文化是人类全部精神活动及其产出。

从整个人类发展史来看,以500年前开始的科技革命为界,之前的宗教信仰、哲学思想是人类文化传承的巅峰,而后的科技知识是人类文化创造的另外一个巅峰。

这里先看宗教信仰,下节将讨论科技。

一门宗教,有其独特的信仰,其或对神灵或超自然对象的崇拜信仰,如基督教的上帝耶稣、伊斯兰教的真主阿拉;或对自然法则的哲学信仰,如佛教对觉悟、解脱的世界观。

上面讲的基督教、伊斯兰教、佛教是当今世界三大宗教,这三大宗教对人类的世界观、人生观、价值观有着深远的影响。对人类的哲学三问,三大宗教有着各自的理论回答。

哲学三问 基督教 伊斯兰教 佛教
我们是谁 有原罪的人 由安拉前定的人 今世来受苦的人
我们从哪里来 上帝造万物 万事万物由安拉前定 前世轮转
我们到哪里去 信耶稣者灵魂进天堂,否则入地狱 善者进天园,恶者进火狱 或入轮回,或者解脱

三大宗教对于回答“我们到哪里去”这个问题,本质都是想告诉人类如何摆脱痛苦,追求永恒的快乐。

基督教告诉信徒需要信仰上帝耶稣,在世界毁灭之日灵魂将受审判,生前信仰基督者,靠基督进入永生,怙恶不悛者,将受公义的刑罚与灭亡,只有耶稣才能带着人类灵魂在世界毁灭之日进入天堂。而伊斯兰教则告诉信徒信仰真主阿拉,现世行善戒恶,在世界末日,阿拉根据善恶给以奖惩,善者进天园,恶者进火狱。无论是基督教,还是伊斯兰教,都在告诫人类,永生的痛苦和快乐取决于你是否信仰它。

佛教在告诉人类追求快乐的方式上,则有些不一样,它更多在探求痛苦的源头,换句话说,只有你明白了什么是痛苦,你才能达到真正的终极快乐。一切的痛苦,其实都源于主观感受。这个主观感受和生物医学上对人的精神感官感受描述是同一的,生物医学认为,外部环境的变化会导致人身体内部激素和脑电波的变化,从而导致精神的波动,使得人有快乐和痛苦的感觉。佛教也认为痛苦来自于主观感受,但是,它更深刻的见解在于,主观感受的精神波动才是痛苦的根源,当你感到快乐,你会想着更快乐,当你感到痛苦,你会想着摆脱痛苦,这种波动起起伏伏,不停地肆虐着人,欲求不断,波动不止,这才是真正的痛苦。只有摆脱主观感受,放下欲求,追求心灵的平静,才能达到真正的终极快乐,即涅槃,实现痛苦的解脱。

我们是谁?我们从哪里来?我们到哪里去?什么是快乐和痛苦?这些问题一直困惑着历史长河中的人类,宗教在这方面给予了完美的解答,这些解答像一盏灯,指引着人类在漫漫黑夜中前行的方向,一直等到500年前科技革命的到来。

6、科技革命

人类在宗教信仰中前行数万年,一直到500年前科技革命的到来,人类对世界的认识达到前所未有的高度。

欧洲是科技革命的发源地,牛顿的三大物理定律和微积分,达尔文的物种起源,爱因斯坦的相对论,等等,一大片基础领域的认知突破几乎都诞生在欧洲。为什么是在欧洲? 这是个巧合,还是必然。这个问题很难回答,但可以肯定的是,自从有了科技革命,人类越来越认识到自己的无知,对无知的探索,成了科技革命的主旋律。

这种探索精神,对于发生在15世纪末到16世纪初的大航海时代,可以说是体现的淋漓尽致。在这个期间,欧洲人开辟了横渡大西洋到达美洲、绕道非洲南端到达印度的新航线以及第一次环球航行。

回到1492年,哥伦布受西班牙国王委派,从西班牙出发向西航行,希望能够找到通往东亚的新航线。哥伦布手上看到的是世界地图上,他估计向西航行约7000公里即可到达,但实际上东亚离西班牙最近的日本也有两万多公里。在1492年10月12日,海上航行数月之久的哥伦布看到一片岛屿,他果断的认为这就是东亚附近的岛屿,属于“Indies”(印度地方,包含今日印度和东印度群岛等地),所以他把当地人成为“Indians”,这也是时至今日美国原住民被叫做印度安人的由来。

一直到一个叫做阿美利哥(Amerigo Vespucci)的意大利水手,其在1499-1504年多次航行前往这块大陆,他指出,哥伦布到访的大陆应该不是东亚,而是一个新大陆。到1507年,地图绘制大师马丁·瓦尔德泽米勒(Martin Waldseemuller)出版了新的世界地图,在这个地图上相信了这位水手的说法,他以为是这位水手发现的新大陆,并将新大陆取名为“America”,美洲大陆的名字由此而来。

1526年的萨瓦尔提世界地图(Salviati Planisphere),地图绘制出美洲新大陆。在这份地图上的左边留出大半边空白,等待着航海家的探索。

正是欧洲人对未知领域的探索和征服精神,开启了科技革命一次次的认知突破。

从蒸汽机,到内燃机,到核电站,人类现在可以上天入地,登月球,编排生物基因,人类似乎无所不能,有一个问题是,是否存在一个认知的极限?人类在不停地探求宇宙的起源,物质的基本粒子构成,力场的作用原理,生物的基因奥秘,知道的越多,似乎疑问也越多。

7、小结

翻开人类的历史,当我们的祖先选择从树上到地面生活,从最初为了适者生存,到改变世界,到创造世界,现在的人类成为了世界主宰。但人类确实没有必要为此感到骄傲,现在的世界并没有人类想象的那么美好。人类可以发展经济、盖起城市、建立帝国、搭建起随时随地通信的全球网络,与此同时,世界的工业污染、气候变暖、生物灭绝,整个世界的生物生态面临着前所未有的挑战,人类难辞其咎。

更为让人担忧的是,人类远没有想象的那么负责任,却比想象的更加贪得无厌,为了自己的舒适,我们不用对任何人负责。拥有神一样的能力,不负责任,贪得无厌,人类的危险莫过于此。

8、参考资料

  1. 《人类简史》,尤瓦尔·赫拉利 著,ISBN: 9787508660752

  2. 百度百科-语系

  3. 百度百科-平图拉斯河手洞

  4. 360Doc图书馆-平图拉斯河手洞

  5. 百度百科-人的脑容量

  6. 百度百科-尼安德特人

  7. 百度百科-七大帝国

  8. Wiki - Salviati Planisphere萨尔瓦提世界地图 - A world map from 1526 which shows the Spanish view of the Earth's surface at the time and includes the eastern coasts of North and South America.

  9. 百度百科-一般等价物

  10. 百度百科-等价交换

  11. 百度百科-等价交换

  12. 百度百科-经济

  13. 百度百科-银行

  14. 百度百科-三大宗教

  15. 百度百科-佛教

  16. 百度百科-伊斯兰教

  17. 百度百科-大航海时代

  18. 图片《从猿猴到人类》来自 这里

浏览器的跨域访问

图片来自nidan-455298会员

跨域问题是在web开发中一个常见的问题,其源自浏览器的同源安全策略,却影响着web网页对cookie/ajax/web storage等信息的跨域共享访问,本文将对同源策略、跨域问题的由来、影响的场景类别和常见解决方案进行了梳理,最后给出代码演示样例。

1. 同源安全策略

在浏览器中,有一个最核心和基本的安全策略,叫做同源策略(same-origin policy),它是指两个网页,若其协议、主机名、端口都相同的情况下,则两个网页属于同源,两者共享同源的数据存储,允许对共享数据的互通操作。

这个同源策略的概念最早由Netscape公司在1995年由其浏览器产品Netscape Navigator 2中引入,此后被所有浏览器采用。

为了理解同源策略,首先了解一个常见的网页url访问地址格式,如下,

scheme://host:port/path

其中:

  • scheme为通信协议,常见的有http/https/ftp等。
  • host为访问的主机名,常见的有域名、IP地址等。
  • port为通信端口号,若不指定的话,则为通信协议的缺省设置。
  • path为访问路径。

根据上面的概念,可以知道同源是指scheme + host + port三者都相同。下面我们看看哪些情况符合同源策略,哪些情况下不符合。

如下给出一些同源策略的规则判断样例,对标的网页:http://a.demo.com/dir/x.html

URL 是否同源 说明
http://a.demo.com/dir2/y.html 同源 只是路径不同,无影响
http://a.demo.com/dir/inner/y.html 同源 只是路径不同,无影响
http://a.demo.com:81/dir/z.html 非同源 端口不同
http://b.demo.com/dir/x.html 非同源 主机名不同,b.demo.com/td>
http://c.a.demo.com/dir/x.html 非同源 主机名不同,c.a.demo.com
https://a.demo.com/dir/x.html 非同源 通信协议不同,https
http://127.0.0.1/dir/x.html 非同源 主机名不同,即a.demo.com指向127.0.0.1的IP地址。
http://a.demo.com:80/dir/x.html 看情况 根据浏览器的实现
| URL | 是否同源 | 说明 |
| ---: | --- | --- |
| http://a.demo.com/dir/inner/y.html | 同源 | 只是路径不同,无影响|
| http://a.demo.com/dir2/y.html | 同源 | 只是路径不同,无影响 |
| http://a.demo.com:81/dir/z.html | 非同源 | 端口不同 |
| http://b.demo.com/dir/x.html | 非同源 | 主机名不同,b.demo.com |
| http://c.a.demo.com/dir/x.html | 非同源 | 主机名不同,c.a.demo.com |
| https://a.demo.com/dir/x.html | 非同源 | 通信协议不同,https |
| http://127.0.0.1/dir/x.html | 非同源 | 主机名不同,即a.demo.com指向127.0.0.1的IP地址。 |
| http://a.demo.com:80/dir/x.html | 看情况 | 根据浏览器的实现 |

2. 什么是跨域问题?

非同源的网页之间,由于同源安全策略的限制,一些数据信息(比如cookie\storage)会被浏览器隔离,JavaScript的Ajax请求也会被拦截并禁止。

但是,不同源的网页之间信息共享是很常见的场景需求,以典型的天猫电商网站为例,其有如下几个子域,

  • tmall.com 天猫电商根域名
  • list.tmall.com 天猫电商子域-列表
  • vip.tmall.com 天猫电商子域-会员
  • chaoshi.tmal.com 天猫电商子域-超市

上面各个子域页面互相属于非同源,受到同源策略的限制。

要实现这些子域之间数据信息访问共享,这就是本文要讨论的跨域问题。

3. 影响的场景类别

跨域问题本身是一个跨源的需求,根据同源策略的定义可知,这是在浏览器的应用场景中才会出现的问题。

浏览器在实现同源策略,保证网页安全的同时,也为跨源的信息共享提供各种途径和技术解决方案。为了方便讨论,根据浏览器的信息存储类别和获取方式,我们可以将跨域访问的需求划分如下三种,

  • cookie:获取另外一个域的cookie信息
  • ajax:通过ajax访问另外一个域的API接口,获取跨域接口数据
  • web storage:获取另外一个域的web storage存储信息

下面将分别对这三种应用场景下的跨域访问进行讨论。

4. Cookie的跨域访问

Cookie是在服务器端生成,发送给浏览器保存的一个键值对,其主要包括如下几个属性,

  • Key/Value:这是cookie的键名和值
  • Domain:这是cookie所属的域,比如.demo.com,则表明该cookie属于所有*.demo.com的域
  • Path:这是cookie所属的路径
  • Expires / Max-Age:过期的日期时间 / 最大存活时长
  • Secure:是否以安全方式传输cookie,以防止中间人攻击
  • HttpOnly:是否只能以Http/Https方式传输cookie,使得JS脚本无法获取,防止XSS攻击

上面Domain/Path的两个属性控制着cookie可以共享的域和路径。需要注意的是,cookie只和域+路径有关,和端口无关,例如a.demo.com:80和a.demo.com:81其实属于同一个域,两者共享cookie。

下面我们看下cookie的共享策略和规则判断。

假如对标的一个网页为:http://a.demo.com/dir/x.html,其页面的cookie属性设置为,

  • domain = .demo.com
  • path = /

那么这个网页上的cookie和如下网页的共享关系见下表,

URL 是否共享 说明
http://a.demo.com/dir2/y.html 共享 路径不同,无影响
http://a.demo.com/dir/inner/y.html 共享 路径不同,无影响
http://a.demo.com:81/dir/z.html 共享 端口不同,无影响
http://b.demo.com/dir/x.html 共享 二级域名不同,无影响
http://c.a.demo.com/dir/x.html 共享 三级域名不同,无影响
https://a.demo.com/dir/x.html 共享 通信协议为HTTPS,无影响
http://127.0.0.1/dir/x.html 不共享 主机名不同,无法共享。
http://a.demo.com:80/dir/x.html 共享
| URL | 是否共享 | 说明 |
| ---: | --- | --- |
| http://a.demo.com/dir/inner/y.html | 共享 | 路径不同,无影响 |
| http://a.demo.com/dir2/y.html | 共享 | 路径不同,无影响 |
| http://a.demo.com:81/dir/z.html | 共享 | 端口不同,无影响 |
| http://b.demo.com/dir/x.html | 共享 | 二级域名不同,无影响 |
| http://c.a.demo.com/dir/x.html | 共享 | 三级域名不同,无影响 |
| https://a.demo.com/dir/x.html | 共享 | 通信协议为HTTPS,无影响 |
| http://127.0.0.1/dir/x.html | 不共享 | 主机名不同,无法共享。 |
| http://a.demo.com:80/dir/x.html | 共享 | - |

可以对上表和上一个同源策略规则判断表,两者有很大的差别。

除了设置每个cookie的domain属性,两个网页之间还可以设置全局文档属性document.domain,实现cookie共享。若document.domain = demo.com,则下面两个网页的cookie共享,

  • http://a.demo.com/dir/x.html
  • http://b.demo.com/dir/y.html

5. Ajax的跨域请求

Ajax是一个异步请求访问后端API接口的方法,受到同源策略的影响,在请求跨域的接口时,会收到如下的错误消息(Chrome浏览器),

Failed to load http://a.demo.com/test No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.demo.com' is therefore not allowed access.

可以看到,浏览器禁止了Ajax的跨域请求。

为了实现Ajax的跨域请求,一般有如下两种途径,

  • JSONP:JSON with Padding,数据信息是JavaScript脚本获取
  • CORS:Cross-Origin Resource Sharing跨源资源共享,通过配置服务器实现跨域支持

此外,HTML5还引入了异步通信WebSocket技术,其专门用于长连接实时数据的通信,它一直作为Ajax的异步替代方案应用于异步请求。WebSocket其由于在协议中自带origin字段,所以不受同源策略限制,其跨域的安全主要由后端服务器控制。但是目前WebSocket的浏览器兼容性没有Ajax技术支持的好,应用推广还比较受限。

5.1 JSONP

英文全称为JSON with Padding,数据信息是通过一段JavaScript脚本进行传输,其信息由JS编译器识别,而不是通过JS解析器解析。

JSONP的使用方法很简单,其首先在HTML DOM中通过JavaScript代码动态添加一个script元素,

<script type="text/javascript" src="http://a.demo.com/test"></script>

这个script元素指向一个跨源的API接口地址。浏览器发现有一个新增script元素后,会向这个地址发送一个GET请求,而服务器则会返回一个JavaScript脚本的消息给浏览器,例如,

alert("Hello, This is message from a.demo.com.");

上面的"Hello, This is message from a.demo.com."就是跨域获取的数据信息。浏览器在拿到返回消息后,执行该JS脚本。上面的脚本将会在浏览器中弹出一个提示框。需要注意的是,执行的函数可以通过参数传给服务器,

<script type="text/javascript" src="http://a.demo.com/test?callback=test"></script>

则服务器可以将返回的JavaScript脚本变为,

test("Hello, This is message from a.demo.com.");

JSONP的方式简单易用,兼容所有浏览器,主要缺点是只能应用于GET请求。

5.2 CORS

英文全称为Cross-Origin Resource Sharing,中文意思为跨源资源共享,这是一个W3C的标准,其支持所有类型的HTTP请求跨域调用,包括GET/PUT/POST/DELETE等等。

CORS需要浏览器和服务器的支持,目前绝大多数的浏览器都支持该功能,所以只需要配置好服务器的支持即可。

整个CORS的大概流程为,即将发起一个跨Ajax调用时,当浏览器发现这是一个跨域请求,会在请求头中添加Origin字段,表明该请求的来源,并先向服务发起一次查询,询问是否该Ajax请求调用是否合法,若合法,则像和正常Ajax请求一样执行(请求头中会添加Origin字段)。

请求头中的Origin需要和服务器上接口配置的Access-Control-Allow-Origin/Access-Control-Allow-Methods相匹配,下面是给出几个样例的匹配判断。

CORS的接口配置 说明 请求样例
Access-Control-Allow-Origin: http://a.demo.com/test
Access-Control-Allow-Methods: GET, PUT
允许来自指定origin的GET/PUT请求 GET http://a.demo.com/test
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
允许来自任何域的任何请求 路径不同,无影响

有时候希望服务器允许多个origin的请求,按照W3C的建议,这个一般在服务器动态配置,如下是一个实现的简单代码样例,

$http_origin = $_SERVER['HTTP_ORIGIN'];

if ($http_origin == "http://a.demo.com" || $http_origin == "http://b.demo.com" || $http_origin == "http://c.demo.com")
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

6. Web Storage的跨域访问

浏览器的Web存储分为如下几种,

  • Local Storage:通过key/value形式进行的数据存储,没有时间限制
  • Session Storage:在一个会话中的数据存储,一旦重启浏览器,则数据会消失
  • IndexedDB:索引数据存储(HTML5引入)

由于同源策略的影响,非同源的网页之间的存储信息是相互隔离的。同时,同源安全策略禁止了不同域的网页之间不能进行相互的DOM直接操作,以防止xss安全攻击。这些都有效地保证网页的安全和隐私,但另一方面,网页之间的沟通交互也收到了限制,即使已知两个网页之间的通信是安全的。

为了保持同源安全策略,同时实现跨域的网页通信,在HTML5中引入了cross-document messaging(跨文档通信)的功能,使得网页之间消息通信成为可能。

一个常见的做法是,在当前页面中嵌入一个隐藏的iframe窗口,这个iframe引入另外一个域的网页。通过父窗口和iframe窗口的跨文档通信,实现存储信息的相互访问读取。整个通信主要使用到两个API,(下面是对API的简要说明)

window.postMessage(message, targetOrigin);

第一参数为发送的消息,第二参数为接受窗口的origin,例如"http://a.demo.com",或者设为"*",表示不限制接受窗口的origin,

window.addEventListener(type, receiver, userCapture);

第一个参数为消息类别,第二个参数为回调函数,第三个参数为在DOM树中是否优先于它下方的任何其它事件目标。

父窗口代码,

$("#btn_send_msg").click(function () {
   let iframe = document.getElementsByTagName('iframe')[0];
   iframe.contentWindow.postMessage('Hello world', '*');
});

iframe窗口代码,

window.addEventListener('msg', function (msg) {
  if (msg.origin == window.parent) {
    localStorage.setItem('message', msg.data);
    msg.source.postMessage('message is saved into local storage.', msg.origin);
  }        
}, false);

可以看到,我们通过cross-document messaging这个消息通道,可以实现跨域的存储信息共享。

7. 小结

下面对浏览器的跨域解决方案做个总结,

  • Cookie
    • 通过设置Cookie的共享域属性 domain = .demo.com。
    • 通过设置网页文档的共享域属性document.domain = demo.com。
  • Ajax
    • JSONP:通过加载JavaScript脚本实现跨域的GET请求。
    • CORS:通过配置服务器的CORS,接受跨域请求。
    • WebSocket:作为Ajax的替代方案,无跨域限制。
  • Web Storage
    • Cross-Document Messaging:通过隐藏的iframe窗口,打开跨域访问的通信通道,实现对跨域的存储信息访问。

8. 演示代码样例

演示代码样例见下面的git代码仓库地址,详细使用说明见工程README文件。

https://gitee.com/pphh/simple-demo/tree/master/demo-cors

9. 参考文献

  1. Wiki – Same Origin Policy
    https://en.wikipedia.org/wiki/Same-origin_policy

  2. MDN – Same Origin Policy
    https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

  3. Uniform Resource Locators (URL)
    https://tools.ietf.org/html/rfc1738

  4. Wiki - Cookie
    https://en.wikipedia.org/wiki/HTTP_cookie

  5. Wiki – Cross Site Scripting (XSS)
    https://en.wikipedia.org/wiki/Cross-site_scripting

  6. W3C – WebSockets
    https://www.w3.org/TR/websockets/

  7. W3C – CORS
    https://www.w3.org/TR/cors/

  8. W3C - 多个origin的CORS配置
    https://www.w3.org/TR/cors/#resource-implementation

  9. W3C - HTML5 Web Messaging
    https://www.w3.org/TR/webmessaging/

  10. W3C - HTML5
    https://www.w3.org/TR/html5/

  11. 阮一峰 - 浏览器同源政策及其规避方法
    http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html