经过实例分析Web运行跨域疑问-揭秘跨域限度与处置打算 (经过实例分析的成语)
在开发Web运行的环节中,咱们经常会遇到所谓跨域疑问(CrossOriginProblem)。跨域疑问是由于阅读器的同源战略(Same-originpolicy)造成的,它限度了不同源(Origin:域名、协定或端口)之间的资源交互。在这篇文章中,我将经过一些详细的示例来把跨域疑问以及干流处置方法说分明,供大家参考。
1.什么是跨域疑问
跨域疑问指的是当一个Web运行程序在访问另一个域(Origin)的资源时,阅读器会阻止这个跨域的恳求(CrossOriginRequest)。这句针对跨域疑问的诠释里有一个术语域(Origin),它究竟是什么呢?
1.1什么是Origin
在Mozilla官网术语表中,"Origin"指的是一个Web运行/网站的标识,由协定(protocol/scheme)、域名(domn,或主机名host)和端口(port)组成。假设两个运行/网站的协定、域名和端口都相反,它们就被以为是同源的(sameorigin);否则,它们被视为不同源。咱们看到:**Origin是一个典型的三元组(protocol,domain,port)**,只要三元组相反的两个运行/站点才会被以为是同源的(sameorigin)。
上方是一些判别两个运行/站点能否同源的例子及判别理由:
知道了Origin三元组后,咱们来揪出跨域疑问面前的罪魁祸首。
1.2同源战略-跨域疑问的罪魁祸首
阅读器为了参与安保性而采取的一项关键措施,那就是同源战略。同源战略限度了一个网页中的脚本只能与同源(三元组:协定、域名、端口相反)的资源启动交互,而不能间接访问不同源的资源。
阅读器的这种同源战略限度关键蕴含以下几点:
下图(图片来自网络)展现了同源战略对恶意脚本代码对非同源数据访问的限度:
上方这张图片明晰地展现了恶意脚本代码试图访问非同源数据启动恶意登录的环节。
首先,用户经过阅读器访问反常网站domain1.com,并用用户名明码反常登录该网站,domain1.com经常使用cookie技术在用户阅读器中保留了与用户登录domain1.com关系的会话消息或token消息。
之后,用户又访问了恶意站点domain2.com,该站点首页的脚本代码在被下载到用户阅读器中后,试图访问阅读器cookie中无关domain1.com的cookie消息,并试图用该消息混充用户登录domain1.com做恶意操作。
阅读器的同源战略成功制止了恶意代码的这些恶意操作,阅读器从domain2.com下载的脚本代码只能访问与domain2.com同源的消息。
经过这个环节咱们看到:阅读器同源战略的本意是防止恶意网站经过脚本窃取用户的敏感消息,比如登录凭证、团体资料等。假设同源战略不存在,恶意网站就可以自在地读取、修正甚至窜改其余网站的数据,给用户和网站带来渺小的安保危险。
不过,这种战略的存在 给开发人员在开发环节带来诸多烦恼 ,比如:跨域数据访问限度、跨域脚本调用限度以及不可在不同域名之间共享会话消息等。为此,开发人员须要经常使用一些技术手腕来处置这些跨域疑问,这参与了开发的复杂性,并且须要额外的性能和处置,给开发人员带来了必定的费事。此外,不正确地处置跨域恳求也或许造成安保破绽,因此开发人员还须要对跨域恳求启动正当的安保管理和验证。
1.3失掉恳求中的origin
为了做同源检测,咱们须要失掉和确定恳求中的origin消息。那么如何读取和确定呢?
在HTTP恳求头中,"Origin"字段表示发送恳求的页面或资源的源消息。该字段蕴含了发送恳求的页面的完整URL或许仅蕴含协定、域名和端口的局部URL。
在主机端,咱们可以经过读取恳求头中的"Origin"字段来确定恳求的origin,详细的方法会依据经常使用的编程言语和框架而有所不同,例如在Go中可以经过r.Header.Get("Origin")来失掉"Origin"字段的值。由于"Origin"字段是由客户端提供的,主机端在处置恳求时,须要启动验证和安保性审核,以防止伪造或恶意的恳求。
![经过实例分析Web运行跨域疑问](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
但是,有些状况下,恳求或许不会携带"Origin"字段。例如,非阅读器环境下的恳求(如主机间的恳求、命令行工具等)或许不会蕴含"Origin"字段。此外,某些旧版本的阅读器或许也不会发送"Origin"字段。
在这种状况下,咱们就须要经过其余方式来确定恳求的来源。例如,服务端可以检查恳求头中的Referer字段来失掉恳求的来源。Referer字段批示了恳求的来源页面的URL。经过审核Referer字段,服务端可以判别恳求能否来自不同的域。此外,主机端还可以审核恳求头中的Host字段,该字段批示了恳求的指标主机。假设恳求的指标主机与服务端所在的主机不分歧,那么可以判别恳求是跨域的。
不过,须要留意的是,服务端的这些方法都依赖于恳求头中的消息,而恳求头可以被客户端伪造或修正。因此,为了更牢靠地判别恳求能否跨域,服务端应该综合思考多个要素,并启动适当的验证和安保措施。
上方咱们看一个可以复现跨域疑问的示例。
1.4复现跨域疑问的Go代码示例
产生跨域疑问的示例的图示如下:
在这个示例中,咱们有两个Web运行:server1.com:8081和server2.com:8082。依据前面对Origin的了解,这两个Web运行显然不是同源的。
server1.com和server2.com对应的Go代码区分如下:
//cross-origin-examples/reproduce/server1.comfuncmain(){http.HandleFunc("/",func(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","text/;charset=utf-8")html:=`<!DOCTYPEhtml><html><head><title>Cross-OriginExample</title><script>functionmakeCrossOriginRequest(){varxhr=newXMLHttpRequest();xhr.open("GET","http://server2.com:8082/api/data",true);xhr.onreadystatechange=function(){if(xhr.readyState===4&&xhr.status===200){console.log(xhr.responseText);}};xhr.send();}</script></head><body><h1>Cross-OriginExample</h1><button>MakeCross-OriginRequest</button></body></html>`fmt.Fprintf(w,html)})err:=http.ListenAndServe("server1.com:8081",nil)iferr!=nil{panic(err)}}//cross-origin-examples/reproduce/server2.compackagemainimport("fmt""/http")funcmain(){http.HandleFunc("/api/data",func(whttp.ResponseWriter,r*http.Request){fmt.Printf("recvrequest:%#vn",*r)w.Write([]byte("Welcometoapi/data"))})http.ListenAndServe("server2.com:8082",nil)}
从示用意来看,用户经常使用阅读器与两个Web运行的交互环节是这样的:
首先,用户经过阅读器访问了server1.com:8081的主页,并收到server1.com:8081前往的应对包体。该应对包体是一个html页面,如下图:
接上去,用户点击MakeCross-OriginRequest按钮,页面内经过ajax向server2.com:8082/api/data动员GET恳求。
最后,咱们在(Edge/Chrome)阅读器的管理台上将看到上方失误:
经过上方server2.com的日志,咱们看到ajax恳求曾经发到server2.com并被正确处置:
recvrequest:http.Request{Method:"GET",URL:(*url.URL)(0xc00010a480),Proto:"HTTP/1.1",ProtoMajor:1,ProtoMinor:1,Header:http.Header{"Accept":[]string{"*/*"},"Accept-Encoding":[]string{"gzip,deflate"},"Accept-Language":[]string{"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"},"Connection":[]string{"keep-alive"},"Origin":[]string{"http://server1.com:8081"},"Referer":[]string{"http://server1.com:8081/"},"User-Agent":[]string{"Mozilla/5.0(intosh;IntelMacOSX10_15_7)leWebKit/537.36(KHTML,likeGecko)Chrome/116.0.0.0Safari/537.36Edg/116.0.1938.81"}},Body:http.noBody{},GetBody:(func()(io.ReadCloser,error))(nil),ContentLength:0,TransferEncoding:[]string(nil),Close:false,Host:"server2.com:8082",Form:url.Values(nil),PostForm:url.Values(nil),MultipartForm:(*multipart.Form)(nil),Trailer:http.Header(nil),RemoteAddr:"127.0.0.1:49773",RequestURI:"/api/data",TLS:(*tls.ConnectionState)(nil),Cancel:(<-chanstruct{})(nil),Response:(*http.Response)(nil),ctx:(*context.cancelCtx)(0xc000106320)}
server2.com在服务端并没有被动判别能否是同源恳求,但即使主机没有启动跨域校验并前往成功的照应和数据,阅读器也会阻拦脚本读取跨域照应数据的尝试,这是由阅读器的同源战略所选择的。这也是咱们看到上方截图中报错的要素。
那么处置跨域疑问有哪些干流的处置方法呢?咱们继续看一下。
2.跨域疑问的干流处置方法
为了处置跨域疑问,有上方几种经常出现的处置方法:
经过在主机照应头中设置CORS访问战略以准许指定的Origin访问资源。
在同域下创立一个代理主机,将跨域恳求转发到指标主机并前往结果。代理主机对照应头一致参与Access-Control-Allow-Origin等CORS关系字段,表示准许跨域访问。
其中CORS是处置跨域疑问时运行最为宽泛的方法。CORS(跨域资源共享)关键是经过设置HTTP头来处置跨域疑问的。
假设设置为*,则表示准许恣意域动员跨域恳求:
Access-Control-Allow-Origin:*
也可以在照应中将Access-Control-Allow-Origin设置为只准许指定的Origin访问资源,比如:
Access-Control-Allow-Origin:
Access-Control-Allow-Origin头的值还支持设置多个origin,多个origin用逗号分隔:
Access-Control-Allow-Origin:
注:关于Access-Control-Allow-Origin的值能否要带上protocol和port的疑问,我实测的状况是必定带。前面说过:Origin是三元组,只要齐全相反才算是同源。
此外,域名必定详细到二级域名才干婚配成功。顶级域名如.com、.org是不准许的。
服务端照应的跨域设置还不只Access-Control-Allow-Origin一个,咱们还可以设置Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Age等字段来更细粒度的启动跨域访问管理。
注:有些值Access-Control-XXX-xxx字段仅用于PreflightRequest(预检恳求),比如:Access-Control-Allow-Methods。CORSPreflightRequest是一种CORS恳求,它经常使用特定的方法和Header审核CORS协定能否被了解和主机能否被感知。它是一个OPTIONS恳求,经常使用两个或三个HTTP恳求头:Access-Control-Request-Method(访问管理恳求方法)、Origin(来源)和可选的Access-Control-Request-Headers(访问管理恳求头)。
3.经常使用CORS处置跨域疑问的示例
上方咱们修正一下server2.com的代码来处置前面遇到的跨域疑问:
//cross-origin-examples/solve/server2.com/main.gofuncmain(){http.HandleFunc("/api/data",func(whttp.ResponseWriter,r*http.Request){fmt.Printf("recvrequest:%#vn",*r)w.Header().Set("Access-Control-Allow-Origin","http://server1.com:8081")w.Write([]byte("Welcometoapi/data"))})http.ListenAndServe("server2.com:8082",nil)}
咱们仅在server2.com/main.go中参与了一行代码,旨在准许来自的跨域恳求访问server2.com的资源:
w.Header().Set("Access-Control-Allow-Origin","http://server1.com:8081")
启动新版server2.com后,再点击页面上的MakeCross-OriginRequest按钮,咱们在阅读器的管理台上就能看到应对成功被接受并显示。
4.小结
本文引见了日常Web运行开发环节中经常遇到的跨域疑问,讨论了域(Origin)概念以及跨域疑问的实在要素:即阅读器的同源战略限度了不同源恳求资源的访问。
接上去经过Go代码示例展示了跨域疑问的体现方式,并引见了几种关键的跨域处置打算,最后对最经常出现的CORS处置打算做了粗疏说明,并用实例展现了服务端设置CORS头后跨域疑问的处置。
宿愿本文可以协助大家更深化的了解和把握Web运行跨域疑问以及处置方法。
本文触及的源码可以在这里下载。
5.参考资料
JSON和JSONP有哪些区别,PhoneGap跨域请求如何实现
什么是JSON前面简单说了一下,JSON是一种基于文本的数据交换方式,或者叫做数据描述格式,你是否该选用他首先肯定要关注它所拥有的优点。
JSON的优点:1、基于纯文本,跨平台传递极其简单;2、Javascript原生支持,后台语言几乎全部支持;3、轻量级数据格式,占用字符数量极少,特别适合互联网传递;4、可读性较强,虽然比不上XML那么一目了然,但在合理的依次缩进之后还是很容易识别的;5、容易编写和解析,当然前提是你要知道数据结构;JSON的缺点当然也有,但在作者看来实在是无关紧要的东西,所以不再单独说明。
JSON的格式或者叫规则:JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。
1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号是定义符。
2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。
3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。
4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号”,以便于不同语言的解析。
5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。
JSON实例:// 描述一个人 var person = {Name: Bob,Age: 32,Company: IBM,Engineer: true}// 获取这个人的信息 var personAge = ;// 描述几个人 var members = [{Name: Bob,Age: 32,Company: IBM,Engineer: true},{Name: John,Age: 20,Company: Oracle,Engineer: false},{Name: Henry,Age: 45,Company: Microsoft,Engineer: false}]// 读取其中John的公司名称 var johnsCompany = members[1];// 描述一次会议 var conference = {Conference: Future Marketing,Date: 2012-6-1,Address: Beijing,Members:[{Name: Bob,Age: 32,Company: IBM,Engineer: true},{Name: John,Age: 20,Company: Oracle,Engineer: false},{Name: Henry,Age: 45,Company: Microsoft,Engineer: false}]}// 读取参会者Henry是否工程师 var henryIsAnEngineer = [2];关于JSON,就说这么多,更多细节请在开发过程中查阅资料深入学习。
什么是JSONP先说说JSONP是怎么产生的:其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。
1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。
JSONP的客户端具体实现:不管jQuery也好,ExtJs也罢,又或者是其他支持jsonp的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下jsonp在客户端的实现:1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。
远程服务器根目录下有个文件代码如下:alert(我是远程文件);本地服务器下有个页面代码如下:<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//ENxmlns=type=text/javascript src=毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。
2、现在我们在页面定义一个函数,然后在远程中传入数据进行调用。
页面代码如下:<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//ENxmlns=type=text/javascript>var localHandler = function(data){alert(我是本地函数,可以被跨域的文件调用,远程js带来的数据是: + src=文件代码如下:localHandler({result:我是远程js带来的数据});运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。
很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。
3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。
看页面的代码:<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//ENxmlns=type=text/javascript>// 得到航班信息查询结果后的回调函数var flightHandler = function(data){alert(你查询的航班结果是:票价 + target=_blank>创建script标签,设置其属性var script = (script);(src, url);// 把script标签加入head,此时调用开始(head)[0](script);</script></head><body></body></html>这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。
我们看到调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。
OK,服务器很聪明,这个叫做的页面生成了一段这样的代码提供给(服务端的实现这里就不展示了,与你选用的语言无关,说到底就是拼接字符串):flightHandler({code: CA1998,price: 1780,tickets: 5});我们看到,传递给flightHandler函数的是一个json,它描述了航班的基本信息。
运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。
什么?你用的是jQuery,想知道jQuery如何实现jsonp调用?好吧,那我就好人做到底,再给你一段jQuery使用jsonp的代码(我们依然沿用上面那个航班信息查询的例子,假定返回jsonp结果不变):<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN<html xmlns=> <head> <title>Untitled Page</title><script type=text/javascript src=></script><script type=text/javascript> jQuery(document)(function(){$({ type: get, async: false, url:dataType: jsonp, jsonp: callback,//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonpCallback:flightHandler,//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写?,jQuery会自动为你处理数据 success: function(json){ alert(您查询到航班信息:票价: + + 元,余票: + + 张。
); }, error: function(){ alert(fail); } }); }); </script> </head><body></body> </html>1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;2、但ajax和jsonp其实本质上是不同的东西。
ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
4、还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。
总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变这一点! 说道这里很多人还不明白后台如何处理数据,这里稍微的说一下: 拿php来说吧 比如$items_list 是一个数组$items_list=json_encode($items_list);$callback=$_GET[callback];echo $callback.($items_list);exit;
前后端分离架构下的跨域问题
在前后端分离架构下,难免会遇到跨域问题。
但是对于跨域,很多人并没有多么深入的了解。
这里我就详细讲一下这个问题。
同源策略与跨域 所谓跨域,英文叫做cross-domain,是网络安全领域的一个专有名词。
简单点理解就是某些操作越过了域名的界限,访问了别的域名。
如果脚本可以自由访问其他域,就会产生很多安全问题。
比如,假设有一个网上银行系统,你已经登录过了,它支持一个ajax api可以进行转账;有一个论坛系统,人气很高,但是其中有恶意脚本,这个脚本会调用这个ajax api,从当前登录的用户账户中,转1000块到攻击者的账户。
这样,当你访问这个论坛的时候,就会被转走1000块,而你一点都不知道! 除此之外,跨域请求还有很多危害。
这不是一本关于安全的书,也就不展开讲了,想深入了解的可以买一本余弦编写的《Web前端黑客技术揭秘》。
为了防范跨域攻击,所有现代浏览器都遵循一套同源策略。
根据MDN上的定义,“如果两个页面拥有相同的协议(protocol),端口(如果指定),和主机,那么这两个页面就属于同一个源(origin)”。
对于违反同源策略的请求,除了img src等少数嵌入操作之外,都会被浏览器阻止。
这里需要注意的是:同源不仅仅要求相同的域名或ip,连协议和端口也必须相同。
比如和或就不是同源的,而和是同源的。
我们平常所说的“跨域”其实就是指“发起不同源请求”,而这样的跨域请求会被浏览器阻止。
同源策略对保障互联网安全有着非常重要的作用,很多安全策略都是基于同源策略的。
但是,这种同源策略会对前后端分离架构下的开发过程带来很大困扰。
比如,即使是本地服务器,也没法和前端开发服务器运行在同一个端口上,这时候,跨域是必然的。
而如果要让后端程序同时提供web服务,则很难发挥前端工具链的轻量级优势。
那么,如何解决跨域问题呢? 如何解决跨域问题?JSONP方式 最初用来解决跨域问题的方式,叫做JSONP,它的基本原理是:跨域的“资源嵌入”是被浏览器允许的。
所以,可以通过一个script标签来嵌入一段来自其他服务器的脚本。
由于这个脚本完全运行在当前域,无法访问第三方服务器的cookie等敏感信息,所以是安全的。
JSONP的缺点是它只能支持GET操作,没法支持POST等操作,但是由于兼容性好等优点,仍然有很多网站采用JSONP的方式公开自己的API供第三方调用。
在Angular中,$http内置了对JSONP的支持,它的调用接口也和其他方法没什么区别,使用起来非常简单。
反向代理方式 要想解决跨域问题,最简单彻底的方法当然是把他们拉到一个域下,而这就是该“反向代理”发挥作用的时候了。
所谓反向代理,就是在自己的域名下架设一个Web服务器,这个服务器会把请求转发给第三方服务器,然后把结果返回给客户端。
这时候,在客户端看来,自己就是在和这台反向代理服务器打交道,而不知道第三方服务器的存在。
所以,如果有一个Web服务程序,它同时提供了反向代理功能和静态文件服务功能,静态文件服务负责渲染前端页面,反向代理则提供对第三方服务器的透明访问。
那么前端和后端就变成了同源的,不再受同源策略的约束。
那么,有这样的Web服务程序吗?有,而且不止一个。
事实上,几乎所有的主流Web服务器都提供了反向代理功能。
这里仅以nginx为例来示范反向代理的配置方式,其他Web服务器请搜相应的文档自行研究。
server {listen 80; server_name ; location / { proxy_pass# 把根路径下的请求转发给前端工具链(如gulp)打开的开发服务器,如果是产品环境,则使用root等指令配置为静态文件服务器 } location /api/ { proxy_pass# 吧 /api 路径下的请求转发给真正的后端服务器 proxy_set_header Host $http_host; # 把host头传过去,后端服务程序将收到,否则收到的是localhost:8080 proxy_cookie_path /api /service; # 把cookie中的path部分从/api替换成/service proxy_cookie_domain localhost:8080 ; # 把cookie的path部分从localhost:8080替换成 } } 注意最后这两句话,由于cookie中存在一个path机制,可以对同一个域下的不同子域进行区分。
所以,如果后端所使用的路径是/service,而前端使用的路径是/api,那么前端将不能访问后端的cookie,这就导致登录等操作所写入的cookie无法正常传入传出,其表现则是登录始终没有效果。
cookie的domain机制也是类似的原理。
现实中的后端服务器,使用path机制的很多,所以这项设置非常实用。
CORS方式 这是W3C提供的另一种跨域方式。
作为一项标准的跨域规范,CORS本应该是最值得采用的。
问题在于,老式浏览器不支持CORS,而我们显然还没到可以无视老式浏览器的时候。
所以,只要有可能,就应该优先采用反向代理的方式。
CORS的原理是基于服务方授权的模式,也就是说提供服务的程序要主动通过CORS回应头来声明自己信任哪些源(协议+域名+端口)。
由于得到了服务方的授权,浏览器就可以放行来自这些域的请求了。
参考:前后台分离,nodeJS转发请求实现跨域访问 :
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。