###

alias hbin me

CORS(跨域资源共享)解决方案

最近在用前端框架写 dashboard 的时候遇到跨域请求的问题。可能对很多人来说这个问题并不新鲜,可我以前并没有去深入了解,借这个机会总结一下。

为什么会有跨域请求问题?

一个网站可能包含网页,API,以及各种资源文件(css, js, images, fonts)等等,而这些可能会在不同的域名下。以 *.example.com 为例,www 是网页,api 是接口,assets 是各种资源文件。 如果在网页中嵌入 web fonts,或者使用 AJAX 跨域请求时,在同源策略的约束下,这种请求返回会被禁止。

如何解决跨域请求?

事实上,为了解决因同源策略而导致的跨域请求问题,解决方法有五种:
From wiki: https://www.wikiwand.com/en/Same-origin_policy#/Relaxing_the_same-origin_policy

  • document.domain
  • Cross-Origin Resource Sharing(CORS)
  • Cross-document messaging
  • JSONP
  • WebSockets

CORS 定义了一种浏览器和服务器之间是否允许跨站请求的标准。这种方式相对其它的来说更加灵活简单,也是 W3C 推荐的方法。

CORS 是如何工作的?

CORS 标准定义了一组新的 HTTP header,这组 header 给浏览器和服务器提供了一种判断跨域请求是否合法的依据。 因此,要实现 CORS,浏览器(client)和服务器(server)都应该遵守该约定。

  • 浏览器端需要在请求的时候增加一个 Origin 的 HTTP header,值为当前页面的域(domain)。如:http://www.foo.com 的页面要请求 http://www.bar.com 的资源,需带上的 HTTP header 为 Origin: http://www.foo.com
  • 服务器端接收请求,返回的时候需要返回一个 Access-Control-Allow-Origin 的 header 表明哪个域是允许的,如果全都允许,可以使用 * 号。如上例,http://www.bar.com 的返回需要带上 Access-Control-Allow-Origin: http://www.foo.com

服务器端解决方案

几乎所有框架都有现成的库可用,以下只列举我用过的三种:

1) Ruby Framework(Rails, Sinatra, etc)

所有基于 Rack 的 Ruby 框架都可以使用 rack-cors。文档非常详细,不再累述。

2) Flask

Flask 也有一个插件 flask-cors,但是文档很差,Python 社区通病。

1
2
3
4
5
6
7
8
// 1. 安装
$ pip install -U flask-cors

// 2. app.py
from flask.ext.cors import CORS

app = Flask(__name__)
CORS(app)

CORS(app) 方法可接收很多 options 而文档并没有给出,其中比较常用的几个:

  • send_wildcard:True 返回 *False 则返回 Origin 的值
  • supports_credentials:是否允许访问 cookies, 默认为 False
  • resources:可作用的范围,默认为 r'/*';如只允许 /api/* 开头的 URL,则 resources=r'/api'

3) Nginx

Nginx 只需要修改对应 server 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
    set $cors "";

    if ($http_origin ~* (.*\.foo.com)) {
        set $cors "true";
    }

    location / {
        if ($cors = "true") {
            add_header 'Access-Control-Allow-Origin' "$http_origin";
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
        }

        if ($request_method = OPTIONS) {
            return 204;
        }
    }
}

更完整的配置参考这个 Gist: https://gist.github.com/hbin/957e8a8df9eef8ecd36c

以上

Comments