同源和跨域

同源与跨域

    • window.orgin 或者 location.origin 可以得到当前源
    • 源 = 协议 + 域名 + 端口
  • 同源
    • 协议相同
    • 域名相同
    • 端口号相同
  • 同源限制
    • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
    • 无法接触非同源网页的 DOM。
    • 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。
  • 跨域
    • 明确了同源限制之后,就能明白跨域其实就是突破同源限制向非非同源服务器请求数据

JSONP 方法实现跨域

JSONP 即 JSON WITH PADDING,特点是简单易用,服务器端改造小,没有兼容问题,IE 可以使用。

它的做法如下。

第一步,网页添加一个 <script> 元素,向服务器请求一个脚本,这不受同源政策限制,可以跨域请求。

1
<script src='http://api.foo.com?callback=bar'></script>

注意,请求的脚本网址有一个 callback 参数(?callback=bar),用来告诉服务器,客户端的回调函数名称(bar)。

第二步,服务器收到请求后,拼接一个字符串,将 JSON 数据放在函数名里面,作为字符串返回(bar({…}))。

第三步,客户端会将服务器返回的字符串,作为代码解析,因为浏览器认为,这是 <script> 标签请求的脚本内容。这时,客户端只要定义了 bar() 函数,就能在该函数体内,拿到服务器返回的 JSON 数据。

下面看一个实例。首先,网页动态插入 <script> 元素,由它向跨域网址发出请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.src = src;
document.body.appendChild(script);
}

window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
};

function foo(data) {
console.log('Your public IP address is: ' + data.ip);
}

上面代码通过动态添加 <script> 元素,向服务器 example.com 发出请求。注意,该请求的查询字符串有一个 callback 参数,用来指定回调函数的名字,这对于 JSONP 是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

1
2
3
foo({
ip: '8.8.8.8',
});

由于 <script> 元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了 foo 函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用 JSON.parse 的步骤。

CORS 实现跨域

CORS 即“跨域资源共享”(Cross-origin resource sharing),CORS 分为简单请求和非简单请求,方便起见先描述简单请求

  • 简单请求
    • 请求方法:
      • HEAD
      • GET
      • POST
    • HTTP 头信息
      • Accept
      • Accept-Language
      • Content-Language
      • Last-Event-ID
      • Content-Type:只限于三个值 application/x-www-form-urlencoded 、 multipart/form-data、text/plain

对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是浏览器自动在头信息之中,增加一个 Origin 字段,如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

1
2
3
4
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与 CORS 请求相关的字段,都以Access-Control-开头。

(1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为true,即表示服务器明确许可,浏览器可以把 Cookie 包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送 Cookie,不发送该字段即可。

(3)Access-Control-Expose-Headers

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

  • 对于复杂请求,则需要在响应端添加其他的的响应头:

    • 响应 OPTIONS 请求,在响应中添加如下的响应头

      1
      2
      3
      Access-Control-Allow-Origin: https://甲站点
      Access-Control- Allow-Methods: POST, GET, OPTIONS, PATCH
      Access-Control-Allow- Headers: Content-Type
    • 响应 POST 请求,在响应中添加 Access-Control-Allow-Origin 头。

  • 如果需要附带身份信息,JS 中需要在 AJAX 里设置xhr.withCredentials=true

    详情可参考 跨源资源共享(CORS) - HTTP | MDN (mozilla.org)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!