同源策略和跨域的解决方案

您所在的位置:网站首页 ricolee什么品牌 同源策略和跨域的解决方案

同源策略和跨域的解决方案

2023-12-21 23:31| 来源: 网络整理| 查看: 265

什么是同源?

所谓同源是指:域名、协议、端口 相同。

检测以下地址和http://www.cnblog.com/ricolee是否同源:

URL 结果 原因 http://www.cnblog.com/ricolee 成功 域名、协议、端口相同 https://www.cnblog.com/ricolee 失败 协议不同 http://www.cnblog.com:8888/ricolee 失败 端口不同 http://www.cnblog.cn/ricolee 失败 域名不同 为什么制定同源策略?

同源策略(Same origin policy)存在于浏览器端是一种约定,由Netscape(网景)提出,用来保护浏览器的数据安全。如果没有同源策略,A网站可以随意访问B网站的Cookie等信息是不安全的,现在所有支持JavaScript 的浏览器都会使用这个策略。

同源策略有什么影响,哪些需要跨域操作? 调用XMLHttpRequest有时候需要跨域,同源策略是禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。 fetchAPI通过跨站点方式访问资源,网络字体,例如Bootstrap(通过CSS使用@font-face 跨域调用字体)。 通过canvas标签,绘制图表和视频。 DOM操作,同源策略禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。 跨域有风险吗?

跨域请求和Ajax技术都会极大地提高页面的体验,但同时也会带来安全的隐患,其中最主要的隐患来自于CSRF(Cross-site request forgery)跨站请求伪造。

image

CSRF攻击的大致原理是:

用户通过浏览器,访问正常网站A(例如某银行),通过用户的身份认证(比如用户名/密码)成功A网站; 网站A产生Cookie信息并返回给用户的浏览器; 用户保持A网站页面登录状态,在同一浏览器中,打开一个新的TAB页访问恶意网站B;网站B接收到用户请求后,返回一些攻击性代码,请求A网站的资源(例如转账请求); 浏览器执行恶意代码,在用户不知情的情况下携带Cookie信息,向网站A发出请求。 网站A根据用户的Cookie信息核实用户身份(此时用户在A网站是已登录状态),A网站会处理该请求,导致来自网站B的恶意请求被执行。 跨域请求出现的错误

例如端口1080的网站请求1090的接口会出现如下错误提示:

`Access to XMLHttpRequest at 'http://localhost:1090/S02CrossDomain/HunterByGet' from origin 'http://localhost:1080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.` 跨域资源共享(CORS)[推荐]

CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通(需要客户端和服务端协同处理)。

CORS背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信

CORS浏览器支持情况

image

客户端需要做什么?

基于上述的CSRF的风险,各主流的浏览器都会对动态的跨域请求进行特殊的验证处理。验证处理分为简单请求验证处理和预先请求验证处理。

两种请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。

请求方法是下列之一:

GET HEAD POST

请求头中的Content-Type请求头的值是下列之一:

Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

简单请求 基本流程

简单请求时,浏览器会直接发送跨域请求,并在请求头中携带Origin 的header,表明这是一个跨域的请求。

服务器端接到请求后,会根据自己的跨域规则,通过Access-Control-Allow-Origin和Access-Control-Allow-Methods响应头,来返回验证结果。 如果验证成功,则会直接返回访问的资源内容。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应:

image

浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

image

一般错误控制台会有如下类似提示: image

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; withCredentials 属性

默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。

一方面,开发者必须在AJAX请求中打开withCredentials属性

xhr.withCredentials = true;

另一方面,如果服务器接收带凭据的请求,会用下面的HTTP头部来响应表示同意。 Access-Control-Allow-Credentials: true 服务器还可以在Preflight响应中发送这个HTTP头部,表示允许源发送带凭据的请求。

image

如果发送的是带凭据的请求,但服务器的响应中没有包含这个头,那么浏览器就不会把响应交给JavaScript(responseText中将是空字符串,size为0)。

image

注意,当withCredentials属性设置为true,需要response header中的'Access-Control-Allow-Origin'为一个确定的域名,而不能使用'*'这样的通配符。

image

非简单请求 预检请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。浏览器发出的Preflighted requests是一个OPTION请求

OPTIONS请求头部中会包含以下头部:

Origin:表示请求来自哪个源。 Access-Control-Request-Method:必填,用来列出浏览器的CORS请求会用到哪些HTTP方法。 Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。 预检请求的回应

服务器收到"预检(OPTIONS)"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,设置比如Access-Control-Allow-Method、Access-Control-Allow-Headers头部与浏览器沟通来判断是否允许这个请求。

image

如果Preflighted requests验证通过,浏览器才会发送真正的跨域请求。

image

如果Preflighted requests验证失败,则会返回403状态,浏览器不会发送真正的跨域请求。

image

Console查看具体的验证失败原因

image

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

XMLHttpRequest cannot load http://xxx.xxx.com. Origin http://xxx.xxx.com is not allowed by Access-Control-Allow-Origin. Request header 有哪些

Origin

头在跨域请求或预先请求中,标明发起跨域请求的源域名。

Access-Control-Request-Method

头用于表明跨域请求使用的实际HTTP方法

Access-Control-Request-Headers

用于在预先请求时,告知服务器要发起的跨域请求中会携带的请求头信息

Response header 有哪些

Access-Control-Allow-Origin

头中携带了服务器端验证后的允许的跨域请求域名,可以是一个具体的域名或是一个*(表示任意域名)。简单请求时,浏览器会根据此响应头的内容决定是否给脚本返回相应内容,预先验证请求时,浏览器会根据此响应头决定是否发送实际的跨域请求。

Access-Control-Expose-Headers

头用于允许返回给跨域请求的响应头列表,在列表中的响应头的内容,才可以被浏览器访问。

Access-Control-Max-Age

用于告知浏览器可以将预先检查请求返回结果缓存的时间,在缓存有效期内,浏览器会使用缓存的预先检查结果判断是否发送跨域请求。

Access-Control-Allow-Credentials

用于告知浏览器当withCredentials属性设置为true时,是否可以显示跨域请求返回的内容。简单请求时,浏览器会根据此响应头决定是否显示响应的内容。预先验证请求时,浏览器会根据此响应头决定在发送实际跨域请求时,是否携带认证信息。

它的值是一个布尔值,表示是否允许发送Cookie.默认情况下,不发生Cookie,即:false。对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json,这个值只能设为true。如果服务器不要浏览器发送Cookie,删除该字段即可。

Access-Control-Allow-Methods(必须)

用于告知浏览器可以在实际发送跨域请求时,可以支持的请求方法,可以是一个具体的方法列表或是一个*(表示任意方法)。简单请求时,浏览器会根据此响应头的内容决定是否给脚本返回相应内容,预先验证请求时,浏览器会根据此响应头决定是否发送实际的跨域请求。

一个逗号分隔的列表,表明服务器支持的请求类型,比如:GET, POST

Access-Control-Allow-Headers

用于告知浏览器可以在实际发送跨域请求时,可以支持的请求头,可以是一个具体的请求头列表或是一个*(表示任意请求头)。简单请求时,浏览器会根据此响应头的内容决定是否给脚本返回相应内容,预先验证请求时,浏览器会根据此响应头决定是否发送实际的跨域请求。

提供一个逗号分隔的列表表示服务器支持的请求数据类型。假如你使用自定义头部,比如:x-authentication-token 服务器需要在返回OPTIONS请求时,要把这个值放到这个头部里,否则请求会被阻止。

服务端需要做什么?

服务器端对于跨域请求的处理流程如下:

首先查看http头部有无origin字段; 如果没有,或者不允许,直接当成普通请求处理,结束; 如果有并且是允许的,那么再看是否是preflight(method=OPTIONS); 如果不是preflight(简单请求),就返回Allow-Origin、Allow-Credentials等,并返回正常内容。 如果是preflight(预先请求),就返回Allow-Headers、Allow-Methods等,内容为空;

.NET 后端实现CORS 一

在web.config的节点下加上以下配置(作用于整个网站):

.NET 后端实现CORS 二

或者在代码中加上如下代码(只作用于当前方法):

public ActionResult HunterAddHeadByCode() { // * 表示允许任何域名跨域访问 Response.Headers.Add("Access-Control-Allow-Origin", "*"); Response.Headers.Add("Access-Control-Allow-Headers", "*"); Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); var json = JsonConvert.SerializeObject(Hunters); return Json(json, JsonRequestBehavior.AllowGet); } .NET 后端实现CORS 三

更好的方式觉得是通过特性实现,哪个接口需要标记特性即可。

待续...... nginx上的CORS配置 location / { if ($request method = 'OPTIONS') { add_ header ' Access -Control- Allow-0rigin' *'; add_ header ” Access Control- Allow-Methods' 'GET, POST, OPTIONS' ; add header Access Control Max-Age ' 86400;6 add header ' Content-Type’” text/plain' ; add header ' Content-Length’0; return 204; } if ($request_ method = 'GET') { add_ header ' Access -Control-Allow-0rigin' 本'; add header ' Access-Control-Al low-Methods' 'GET, POST, OPTIONS' ; add_ header' Access Control -Allow-Headers”'User-Agent , X- Requested -With , Cache - Control , Content -Type; } } 优点 CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。 支持所有类型的 HTTP 请求。 缺点 存在兼容性问题,特别是 IE10 以下的浏览器。 第一次发送非简单请求时会多一次请求。 jsonp 跨域 前端实现

Ajax请求加参数dataType: "jsonp"。如需指定特定回调函数就配置jsonpCallback参数。

注意 回调的函数要在window作用域内,否则调用不到。

$.ajax({ type: "get", async: false, dataType: "jsonp", //指定服务器返回的数据类型, //jsonpCallback: "showData", //指定回调函数名称 url: 'http://localhost:1090/S02CrossDomain/HunterByJsonp', success: function (res) { console.log('success'); var result = JSON.stringify(res); $("#result").html(result); }, error: function (data, textStatus, jqXHR) { console.log(data); $('#result').html(data.statusText); } }); 服务器端实现

服务器端返回值也需要做些修改

public ActionResult HunterByJsonp() { var callback = Request.Params["callback"].ToString(); Response.ContentType = "application/json;charset=utf-8"; var json = JsonConvert.SerializeObject(Hunters); var result = callback + "(" + json + ")"; //注:不能用json返回,会报错 //return Json(result, JsonRequestBehavior.AllowGet); return Content(result); }

提示 ASP.NET MVC 中不能用Json返回否则会报类似错误:jQuery33105546587291303868_1542953995969 was not called

原理解析

页面虽然不允许发起跨域的ajax请求,但引用不同域名的js脚本是可行的。

执行跨域的ajax请求时会自动发起一个Script请求,请求文件名为callback=jQueryxxx,jQueryxxx是jquery随机生成的一个回调函数名称。 该次请求返回来的结果则是jQueryxxx()函数调用字符串,执行这个函数完成跨域请求。

一句话来概括就是,通过动态创建script标签,然后利用 src 属性进行跨域,而每一次跨域就是一个script脚本的引入。

下图可以看到下图执行jsonp请求后引用了另一个域的script文件,每次跨域就引用一次:

image

下图为浏览器端收到返回值,执行返回的数据完成跨域操作

image

优点 使用简便,没有兼容性问题 **缺点 ** 只支持 GET 请求。 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。 要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。(未验证过) 服务器代理

服务器端是没有跨域限制的,由服务器端请求所需资源再返回客户端。

参考

跨域资源共享 CORS 详解【阮一峰】

浏览器同源策略及跨域的解决方法【推荐】

CORS——跨域请求那些事儿【阿里云】

前端解决跨域的九种方法

跨域的另一种解决方案——CORS(Cross-Origin Resource Sharing)跨域资源共享

跨域的那些事儿

ajax跨域,这应该是最全的解决方案了



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3