nginx处理跨域问题

November 15, 2019

什么是跨域

同源:当协议、域名、端口一致的时候2个域名是同源的。只要有一项不一致,即为跨域。(子域名或二级域名不同也是跨域)

为什么要设置同源策略限制

假如你登陆了一个银行网站,在没有退出的情况下登录了另一个网站,这个网站悄悄的携带了你银行网站的cookie对银行网站发起了请求,这样就能冒充你做所有的操作,因为银行网站并不知道到底是不是你发起的请求,它看到了你的cookie就认为是你了。所以要有这个限制。这也是CSRF(cross site request forgery) 跨站请求伪造攻击。

CORS(cross origin resource sharing) 跨域资源共享

这是一个w3c标准,用于控制跨域请求。它定义了一些headers:

  • Access-Control-Allow-Origin

​ 响应首部中可以携带这个头部表示服务器允许哪些域可以访问该资源,其语法如下:

Access-Control-Allow-Origin: <origin> | * ​ 其中,origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。


  • Access-Control-Allow-Methods

​ 该首部字段用于预检请求的响应,指明实际请求所允许使用的HTTP方法。其语法如下:

Access-Control-Allow-Methods: <method>[, <method>]*


  • Access-Control-Allow-Headers

​ 该首部字段用于预检请求的响应。指明了实际请求中允许携带的首部字段。其语法如下:

Access-Control-Allow-Headers: <field-name>[, <field-name>]* 我们就可以控制服务器上哪些资源可以被什么样的跨域请求访问,以达到跨域资源共享的目的。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。对于非简单请求,浏览器会先发出一个预检请求(OPTIONS方法),如果服务端返回包含 Access-Control-Allow-* 的相关响应头,则会继续发出第二个真实的请求。这一切都是浏览器自动完成的。

所以nginx只要返回相应响应头,即可实现CORS跨域资源共享,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
  listen 80;
  server_name corssingle.linuxds.com;
  location / {
    root /usr/share/nginx/corssingle;
    index index.html;
    access_log /var/log/nginx/corssingle.access.log main;
    error_log /var/log/nginx/corssingle.error.log warn;
        
    add_header 'Access-Control-Allow-Origin' 'http://source01.odocker.com' always;
    add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS' always;
        
    if ($request_method = 'OPTIONS') {
        # 204表示no-content
        return 204;
    }
    if ($request_method != 'GET') {
        # 因为该示例是作为一个静态文件服务,而nginx不允许使用Get以外的方法访问静态资源,否则报405 Method not allowed 错误。所以这里对Get以外的请求做了简单响应。
        return 200;
    }
  }  
}

参考

跨域资源共享 CORS 详解