项目中实现对 web 应用程序或 web 应用程序子系统的访问控制是项目的重要
组成。实现 NGINX 的访问控制形式多样,比如从网络层面实现访问控制,允许
NGINX 采用身份校验机制,或 通过 HTTP 响应引导浏览器如何操作。本章将讨
论使用网络属性(network attributes)、身份认证、跨域资源共享(CORS:
Cross-Origin Resource Sharing)原则等安全控制知识。

问题
需要基于客户端的 IP 地址实现访问控制功能
解决方案
使用 HTTP 的 access 模块,实现对受保护资源的访问控制:

location /admin/ {
	deny 10.0.0.1;
	allow 10.0.0.0/20;
	allow 2001:0db8::/32;
	deny all;
}

给定的 location 块级指令中配置了允许除 10.0.0.1 外的所有 10.0.0.0/20 IPv4
地址访问,同时允许 2001:0db8::/32 及其子网的 IPv6 地址访问,其它 IP 地址
的访问将会收到 HTTP 状态为 403 的响应。allow 和 deny 指令可在 HTTP、server、
location 上下文中使用。控制规则依据配置的顺序进行查找,直到匹配到控制规则。

结论
需要控制访问的资源需要实现分层控制。NGINX 服务器提供对资源进行分层控
制的能力。deny 指令会限制对给定上下文的访问,allow 指令与 deny 功能
相反,它们的值可以是定值 IP 地址、IPv4 或 IPv6 地址、无类别域间路由(
CIDR: Classless Inter-Domain Routing)、关键字或 UNIX 套接字。IP 限制
的常用解决方案是,允许一个内部的 IP 地址访问资源,拒绝其它所有 IP 地
址的访问来实现对资源的访问控制。

问题
项目资源部署在其它域名,允许跨域的访问请求使用这些资源。
解决方案
通过对不同请求方法设置对应的 HTTP 消息头实现跨域资源共享:

```java
map $request_method $cors_method {
	OPTIONS 11;
	GET 1;
	POST 1;
	default 0;
}
server {
...
	location / {
		if ($cors_method ~ '1') {
			add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
			add_header 'Access-Control-Allow-Origin' '\*.example.com';
			add_header 'Access-Control-Allow-Headers'
						'DNT,
						Keep-Alive,
						User-Agent,
						X-Requested-With,
						If-Modified-Since,Cache-Control,
						Content-Type';
	}
		if ($cors_method = '11') {
			add_header 'Access-Control-Max-Age' 1728000;
			add_header 'Content-Type' 'text/plain; charset=UTF-8';
			add_header 'Content-Length' 0;
			return 204;
		}
	}
}

结论
当请求的资源不属于当前域名时,就会产生一个跨域的请求,比如 JavaScript 请求其它域名的资源变产生跨域请求。当跨域请求产生,浏览器则必须遵守跨域资源共享(CORS)规则,此时浏览器便不会引用这些跨域的资源,除非,检测到给定的允许使用非同源资源的 HTTP 消息头。为满足子域名间能够跨域使用资源,我们需要使用 add_header 指令设置对应的 CORS 消息头。如果,一个 HTTP 请求是标准的 GET、POST 或 HEAD 请求且并未设置特定的消息头,浏览器就回对请求和源域名进行检测。Other request methodswill cause the browser to make the preflight request to check the
terms of the server to which it will obey for that resource.如果没有实现对特定请求消息头的配置,浏览器在获取跨域资源时,则会抛出错误禁止应用跨域资源。

问题
基于给定的规则如 IP 地址,实现请求连接数。
解决方案
使用 limit_conn_zone 指令构建存储当前连接数的内存区域;然后,使用 limit_conn 指令设置支持的连接数:

http {
	limit_conn_zone $binary_remote_addr zone=limitbyaddr:10m;
	limit_conn_status 429;
	...
	server {
	...
		limit_conn limitbyaddr 40;
	...
	}
}

配置中创建了一个名为 limitbyaddr 的存储容量为 10 M 的共享内存,键名则为客户端二进制的 IP 地址。limit_conn 指令接收两个参数:一个是 limit_conn_zone 创建的名称 limitbyaddr,和支持的连接数40。limit_conn_status 指令定义了当连接数超过 40 个时的响应状态码。limit_conn 和 limit_conn_status 指令能够在 HTTP、server 和 location 上下文中使用。

结论
合理使用连接数限制,可以是服务器的资源被各个客户端合理使用。使用的关键在于定义一个合理的存储键名。本例中基于 IP 地址作为存储键名不是一个好的选择,因为,一旦有许多用户通过同一网络访问服务,便会限制该 IP地址的所有用户的访问连接数,这很不合理。limit_conn_zone 仅在 http 上下文中可用可以使用所有的 NGINX 变量来构建限制键名。通过使用能够识别用户会话的变量如 cookie,有利于合理使用连接控制功能。limit_conn_status 默认状态码是 503 服务不可用。例子中使用 429 因为服务是可用的,而 500 级的响应码表示服务器内部错误,而 400 级的响应码表示客户端错误。

问题
依据某些规则对用户请求进行限速,如通过用户 IP 地址进行限速。
解决方案
利用 rate-limiting 模块实现对请求限速:

http {
	limit_req_zone $binary_remote_addr zone=limitbyaddr:10m rate=1r/s;
	limit_req_status 429;
...
	server {
...
	limit_req zone=limitbyaddr burst=10 nodelay;
...
	}
}

实例中,创建了一个 10 M 存储空间的名为 limitbyaddr 的共享内存,并使用二进制的客户端 IP 地址作为键名。limit_req_zone 还设置了访问速度。limit_req 指令主要包含两个可选参数:zone 和 burst。zone 参数值即为limit_req_zone 指令中 zone 参数定义的存储空间名。当用户请求超出限速设置时,超出的请求将会存储至 burst 定义的缓冲区,直至也超出请求限速缓冲速率,这是将响应 429 状态码给客户端。burst 参数默认值为 0。此外,limit_req还有第三个参数 nodelay:它的功能是提供瞬时处理 rate + burst 个请求的能力。limit_req_status 参数用于设置超出速率请求响应给客户端的状态码,默认是 503,示例中设置为 429。limit_req_status 和 limit_req 指令适用于 HTTP、server 和
location 上下文。limit_req_zone 指令仅能在 HTTP 上下文中使用。

结论
rate-limiting 模块在项目中非常有用,通过防止瞬间爆发的请求,为每个用户提供高质量的服务。使用限速模块有诸多理由,其一是处于安全方面考虑。如在登录页面设置严格的限速控制,拒绝暴力攻击。如果没有依据用户实现限速功能,可能会导致其他用户无法使用服务或浪费了服务器资源。rate-limiting 模块有点类似上一章节中讲解的限制连接模块。限速设置可以依据每秒限速,也可依据每分钟进行限速。当用户请求满足限速条件时,请求将被记入日志中。另外,还有一条指令没有在示例中出:limit_req_log_level 指令设置限速日志级别,它默认值为 error级别,您还可以设置为 info、notice 或 warn 级别。

问题
需要依据客户端,限制它们下载速度
解决方案
使用 NGINX 服务器的 limit_rate 和 limit_rate_after 指令实现客户端响应速度:

location /download/ {
	limit_rate_after 10m;
	limit_rate 1m;
}

location 块级指令设置了对于匹配 /download/ 前缀的 URI 请求,当客户端下载数据达到 10 M以后,对其下载速度限制在 1 M 以内。不过该带宽限制功能仅仅是针对单个连接而言,因而,可能实际使用中需要配合使用连接限制和带宽限制实现下载限速。

结论
limit_rate_after 和 limit_rate 使 NGINX 能够以您指定的方式在所有客户端上共享其上传带宽。limit_rate 和 limit_rate_after 指令可在几乎所有的上下文中使用,如 http、server、location、location 指令内的 if 指令,不过 limit_rate 指令还可以通过 $limit_rate 变量来设置带宽。limit_rate_after 指令表示在客户端使用多少流量后,将启用带宽限制功能。
limit_rate 指令默认限速单位为字节(byte),还可以设置为 m (兆字节) 和g (吉字节)。这两条指令的默认值都是 0,表示不对带宽进行任何限制。另外,该模块提供以编码方式对客户端带宽进行限速。