对跨域的认识

by chiyiw on 2016/12/8

原则

实例

a.com/index.html中的app.js中发起$.getJSON('b.com/data.json')

a.com:8080/index.html中的app.js发起对a.com:8000下资源的请求

反例

以下情况不会发生跨域问题:

一个典型案例

资源部署如下:


动态资源 ->localhost:8080: index.html

静态资源 ->localhost:8000: app.js, app.css, bg.png, data.json


  1. index.html
<html>
<head>
    <meta charset="UTF-8">
    <link href="//localhost:8000/app.css" type="text/css" rel="stylesheet">
</head>
<body>
    <script src="//localhost:8000/app.js"></script>
</body>
</html>

这是一个html页面,它部署在localhost:8080下,页面中请求了一个app.css和一个app.js, 这两个文件在localhost:8000下,属于不同域。

页面能够正确加载这两个文件,说明html没有跨域限制。

  1. app.css
body { background-image: url("./bg.png");}  // 设置页面背景图片
@font-face {     
  font-family: 'MyFont';
  src: url('./script.ttf');
}
body { font-family: 'MyFont'; }

从app.css中加载了一张图片,这张图片在localhost:8000下,而当前页面是localhost:8080/index.html, 属于不同域,可以正确加载。

但是加载的字体出现跨域问题,报错如下:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.

这个实验说明对于图片css没有跨域限制,但是对字体文件存在。

  1. app.js
$(function () {
    $.getJSON('//localhost:8000/data.json', function (data) {  // 此处使用'./data.json'无法访问
        console.log(data)
    })
})

在app.js中发起一个请求,请求localhost:8000下的data.json,由于当前页面在localhost:8080下,属于不同域。 data.json无法加载成功,浏览器报如下错误:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.

这是典型的跨域错误。

而解决跨域问题,也是从这个报错信息开始的,错误显示请求的资源的Response的header中没有’Access-Control-Allow-Origin’这个值,当前源’http://localhost:8080’的请求无法得到许可。这个信息初看有些茫然,好像是说,服务端返回了Response, 但是Response中header没有给本域名权限。

解决方案

CORS(Cross-origin resource sharing)协议是现代浏览器都支持的方式,它定义了跨域的规范,其实真实的跨域处理是两次请求(图来自https://www.html5rocks.com/static/images/cors_flow.png)。

解决办法

了解了跨域限制出现的原因,就可以容易解决问题了。

在静态资源服务器中,向response的header添加以下属性即可:

'Access-Control-Allow-Origin: *'

这种配置允许任意来源的js请求获取到该资源,对于一些公共的资源服务器,这样配置是没有问题的。但是有些资源是私有的,为了达到安全的控制,这里应该配置具体的来源域名:

'Access-Control-Allow-Origin: http://localhost:8080, http://localhost:8088' 

可以通过逗号对多个值进行分隔。

其次,为了更加具体的实现安全控制,还可以配置如下头信息:

Access-Control-Allow-Methods: GET, POST, PUT			// 限定请求方式
Access-Control-Max-Age: 1800						  // 限定跨域允许时间(1800s=3min)
Access-Control-Allow-Headers: X-Custom-Header			// 限定实际请求中可以包含的头信息
Access-Control-Allow-Credentials: true				    // 是否允许携带cookie, 默认为false

同时,利用浏览器的第一次询问,服务端还可以使用Access-Control-Allow-Credentials: true允许浏览器在跨域请求的时候携带cookie,更多CORS的详细用法,可以看这篇文章:https://www.html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request

关于Referfer

bg.png的Referer是http://localhost:8000/app.css,解释了为什么可以通过'./bg.png'访问。

app.json的Referer是http://localhost:8080/index.html,而不是app.js, 因此通过'./app.json'无法访问,因为它代表//localhost:8080/app.json, 而通过'//localhost:8000/app.json'会出现跨域的问题。

结论:js中的请求无论是自动发起的还是手动发起的,其Referer都是当前页面,而不是本身js的来源;而css中发起的请求所带的Referer是css本身的来源地址。

跨域问题存在的意义

跨域既然为开发者带来了诸多问题,它为什么会存在呢?

为了安全。

前端浏览器中运行的js,能从cookie中获得很多的敏感信息,试想在登录了支付宝的浏览器中,浏览器包括了当前的用户的很多私密信息,如果任何一个来源的网页都可以利用其js,向支付宝进行请求操作,就会带来巨大安全隐患。

参考: