跨域原理和解決方式

同源策略:同協議,同域名,同端口。

跨域: 發起請求的域和資源所在的域不是同一個

CSRF(Cross-site request forgery)原文链接: https://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html

2009040916453171.jpg

 解決跨域方法 

 一. JSONP 

与XMLHttpRequest/Ajax方式不同,JSONP实际上只是调用外部服务器的脚本,从而可以执行本地服务的代码

優點:可以兼容老式瀏覽器,

缺點:只支持GET请求,不能發送post,put,fetch,delete等請求

實現方式:

利用image, script, style等標簽没有跨域限制的特性,與外部資源通訊

從本地访问外部服务器上的代码脚本,帶上回調函數和其他参数,該回調函數的值是在本地中定義的函數

舉例:

http://xx.com/page1需要B服务器上面的新闻信息list

再page1中定义函数 showList

function showList (list) {

 //....

}

通过<script src="http://b.com/getList.php?callback=showList&page=2" ></script>

getList.php中最终返回的是一段JS代码,这段代码中会调用 showList 这个回调函数

 //...

getList().then((res) => {

 showList(res)

})

//...

最终就完成了跨域访问,并且拿到了list的数据

 二. CORS 

CORS本质是在服务器端进行设置,

在前端他就和XMLHttpRequest一样,不同的是在后端serve的处理(比如后端设置Access-Control-Allow-Origin等)

在发出请求时,服务器端会返回响应信息,浏览器根据响应信息,判断请求是否成功,决定是否可以跨域访问该请求

非简单请求 和 简单请求的区别

简单请求

1. 请求类型: POST / GET / HEAD

          &

2. 请求头信息不得超过以下几种:

Accept

content-type: application/x-www-form-urlencodedmultipart/form-datatext/plain

Accept-language:

content-language:

Last-Event-Id:

假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源。http://foo.example 的网页中可能包含类似于下面的 JavaScript 代码:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
   
function callOtherDomain() {
  if(invocation) {    
    invocation.open('GET', url, true);
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

这里浏览器发出了一个请求,这个请求头的报文显示如下

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example

服务器收到请求响应的报文如下

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

这里我们注意到请求报文中的orgin信息表示该请求来自 http://foo.example网站,浏览器根据请求去访问bar.other的服务器,响应报文中的 Access-Control-Allow-Origin: *表示该资源可以被任意的网站访问, 如果这个值指定了http://foo.example的话,表示他只能被foo.example访问。

如果Access-Control-Allow-Origin中指定的server没有foo.example的话,请求就会出错,抛出的错误会被XMLHttpRequest的onerror函数捕获,但这个时候请求响应的状态码可能还是200

Access-Control-Allow-Credentials: 是否允许请求时候带上cookie信息, 值为true表示允许, 不存在这个字段时候就表示拒绝带上cookie值. 但是也只有当发出请求设置withCredentials 为 true的时候,才能真正发送cookie信息给server

Access-Control-Expose-Headers: CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值

非简单请求

非简单请求在发出真正的请求之前,会发送一次预请求(pre-flight request)给服务器,判断是否可以发送实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

满足下列条件之一的就是非简单请求

如下是一个需要执行预检请求的 HTTP 请求:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); //设置了其他的header头部信息
      invocation.setRequestHeader('Content-Type', 'application/xml'); //设置了其他的Content-Type格式
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

上述代码使用POST方式发送了一个xml片段,并且自行定义了一个header头部字段 X-PINGOTHER,content-type的值设置为了application/xml格式

当要发送这个请求前就会先发送一个预请求。

server端会返回响应信息,响应头如下:

1.OPTIONS /resources/post-here/ HTTP/1.1 //请求方法为 OPTIONS 类型
 2.Host: bar.other
 3.User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
 4.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 5.Accept-Language: en-us,en;q=0.5
 6.Accept-Encoding: gzip,deflate
 7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 8.Connection: keep-alive
 9.Origin: http://foo.example  //发送的请求来源
10.Access-Control-Request-Method: POST  //告知服务器,实际请求将使用 POST 方法
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type  //告知服务器,实际请求将携带两个自定义的头部信息,服务器据此决定,该实际请求是否被允许。
12.
13.
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example //表明接受来自foo.example的请求
18.Access-Control-Allow-Methods: POST, GET, OPTIONS //表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type
20.Access-Control-Max-Age: 86400 //预请求的缓存失效,在这个86400s内浏览器无须为同一请求再次发起预检请求
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain

 三. postMessage 

postMessage是一种跨源通信(cross-origin),当页面A和页面B (同源或者跨域)的协议相同(通常为https),端口(https端口号为443)相同,且页面中设置的document.remain(文档的原始域)相同的时候,可以进行通信。

postMessage是一种异步通信方式,他会再所有其他脚本都执行完成后(包括settimeout,ajax等)执行。

postMessage主要用在跨域通信,跨文档,多窗口之间进行通信

兼容性

IE兼容性不好(IE8 和 IE9 仅支持窗口与 <frame> 和 <iframe> 之间的通信, IE10使用有限制),其他浏览器兼容性良好,对于Safari浏览器,获取iframe对象的时候必须使用document.getElementId('your-frame').contentWindow,不能使用window.frames[index]模式

postMessage API

发送信息:otherWindow.postMessage(message, targetOrigin, transfer)

otherWindow: 要接受message的窗口对象,可以是iframe的值,window.open的返回值

message:  必填,向目标origin传递的信息, 考虑到兼容性等问题,一般格式为String, 所以再传递信息的时候通过JSON.Stringfy进行序列化

targetOrigin:  必填,*表示向任意网站发送信息, ‘/’ 表示同源网站, ‘https://xxx.com, https://xxx2.com’表示指定的网站

transfer: 可选,Transferable类型,代表一个能在不同可执行上下文中相互传递的对象

接受信息: window.addEventListener('message', eventHandler, false)

function eventHandler (event) {}

event是Message类型,

event.origin:    发送信息的窗口对象的origin

event.source: 对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。

event.data

使用场景和案例

1. iframe通信(同源/跨域)

 

2. 多窗口通信

 

3. WebWorker

 

 

 

 

 

转载于:https://my.oschina.net/u/4085373/blog/3020970

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值