阅读 170

前端web:主流跨域方式有哪些?

主流跨域方式:cors 跨域 && jsonp 跨域

  • 不允许跨域的基本原因是:http请求头中没有允许跨域有关的参数。解决跨域请求的原理就是在拦截器中拦截请求,请在Http请求头添加允许跨域的字段

jsonp 跨域请求

  • jsonp的缺点就是只能采用get请求。
    • 为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许
    • 基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
$.ajax({
                url: 'http://localhost:1112/getPerson?callback=?',
                dataType: 'jsonp',
                jsonpCallback: 'person',
                success: function(data){
                    $('.name').html(data.name);
                    $('.company').html(data.company);
                }
            })

复制代码

cors(cross-origin-resource-share) 跨域资源共享:

cors机制:

  • 它使用额外的HTTP头来告诉浏览器,让运行在一个origin上的web应用被准许访问来自不同源服务器上的指定资源。
  • 当一个资源从与该资源本身所在的服务器不同的域/协议/端口请求一个资源的时候,资源会发起一个跨域请求;

举例:

  • 站点http://domain-a.com 的某html页面通过 src 请求http://domain-b.com/imge.jpg;
  • 网络上的许多页面加载来自不同域的css样式表/图像/和脚本等资源;

处于安全原因:

  • 浏览器限制从脚本内发起http跨域请求,或者是跨站请求可以正常发起,但是返回结果被浏览器拦截了 例如:XMLHttpRequest 和 fetch发起的请求;
  • cors机制,允许web应用服务器进行跨域访问控制,从而是的跨域访问数据传输得以安全进行;

API遵循同源策略:

  • 这意味着使用这些API的Web应用程序,只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

新增CORS标准:

  • 跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源;
  • 对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。

cors举例:

比如说,假如站点 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(); 
  }
}
复制代码

简单请求:

不进行预检请求的http请求,称为简单请求;

Request:

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

Response:

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

[XML Data]
复制代码

预检请求:

  • “需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
  • "预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
预检请求条件:
1.使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
2.人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
3.Content-Type 的值不属于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
4.请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
5.请求中使用了ReadableStream对象。
复制代码

如下是一个需要执行预检请求的 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');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

......
复制代码

Request:

 1.OPTIONS /resources/post-here/ HTTP/1.1
 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
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Response:

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
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
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
复制代码

预检请求完成之后,发送实际请求:

POST /resources/post-here/ 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
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求
复制代码

附带身份凭证的请求:

Fetch 与 CORS 的一个有趣的特性是,可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

本例中,foo.example 的某脚本向 bar.other 发起一个GET 请求,并设置 Cookies:

var invocation = new XMLHttpRequest(); var url = 'bar.other/resources/c…';

function callOtherDomain(){ if(invocation) { invocation.open('GET', url, true); invocation.withCredentials = true; invocation.onreadystatechange = handler; invocation.send();

} }

  • withCredentials 标志设置为 true,从而向服务器发送 Cookies
  • 因为这是一个简单 GET 请求,所以浏览器不会对其发起“预检请求”。但是,如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。

附带身份凭证的请求与通配符

  • 附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 foo.example,则请求将成功执行。

HTTP 响应首部字段

Access-Control-Allow-Origin

响应首部中可以携带一个 Access-Control-Allow-Origin 字段,其语法如下:

Access-Control-Allow-Origin: <origin> | *
复制代码

其中:

  • origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。

例如,下面的字段值将允许来自 mozilla.com 的请求:


Access-Control-Allow-Origin: http://mozilla.com
复制代码

如果服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。

Access-Control-Allow-Credentials

  • Access-Control-Allow-Credentials头指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
  • 当用在对preflight预检测请求的响应中时,它指定了实际的请求是否可以使用credentials。请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。
Access-Control-Allow-Credentials: true
复制代码

HTTP 请求首部字段

Origin

Origin 首部字段表明预检请求或实际请求的源站。

Origin: <origin>
复制代码

origin 参数的值为源站 URI。它不包含任何路径信息,只是服务器名称。

Access-Control-Request-Headers

Access-Control-Request-Headers 首部字段用于预检请求。其作用是,将实际请求所携带的首部字段告诉服务器。

Access-Control-Request-Headers: <field-name>[, <field-name>]*
复制代码

参考MDN

关注下面的标签,发现更多相似文章
评论