11.6.3 ngx_http_core_access_phase

ngx_http_core_access_phase方法是仅用于NGX_HTTP_ACCESS_PHASE阶段的处理方法,这一阶段用于控制用户发起的请求是否合法,如检测客户端的IP地址是否允许访问。它涉及nginx.conf配置文件中satisfy配置项的参数值,见表11-2。

11.6.3 ngx_http_core_access_phase - 图1

对于表11-2的any配置项,是通过ngx_http_request_t结构体中的access_code成员来传递handler方法的返回值的,因此,ngx_http_core_access_phase方法会比较复杂,如图11-10所示。

11.6.3 ngx_http_core_access_phase - 图2

图 11-10 ngx_http_core_access_phase方法的执行流程

下面开始分析ngx_http_core_access_phase方法的流程。

1)既然NGX_HTTP_ACCESS_PHASE阶段用于控制客户端是否有权限访问服务,那么它就不需要对子请求起作用。如何判断请求究竟是来自客户端的原始请求还是被派生出的子请求呢?很简单,检查ngx_http_request_t结构体中的main指针即可。在11.3节介绍过的ngx_http_init_request方法会把main指针指向其自身,而由这个请求派生出的其他子请求中的main指针,仍然会指向ngx_http_init_request方法初始化的原始请求。因此,检查main成员与ngx_http_request_t自身的指针是否相等即可,如下面的源代码。


if(r!=r->main){

r->phase_handler=ph->next;

return NGX_AGAIN;

}


如果当前请求只是一个派生出的子请求的话,是不需要执行NGX_HTTP_ACCESS_PHASE阶段的处理方法的,那么直接将phase_handler设为下一个阶段(实际上是NGX_HTTP_POST_ACCESS_PHASE阶段)的处理方法的序号。这时会返回NGX_AGAIN,也就是希望HTTP框架立刻执行新的HTTP阶段的处理方法。

2)如果当前请求就是来自客户端的原始请求,那么调用HTTP模块在这一阶段中实现handler方法,它的返回值将会导致出现3个分支:返回NGX_AGAIN或者NGX_DONE时跳转到第3步执行;返回NGX_DECLINED时跳转到第4步执行;返回其他值时跳转到第5步继续向下执行。同时,在第5步之后,这个返回值由于nginx.conf文件中配置项satisfy的参数值不同,也将具有不同的意义。

3)返回NGX_AGAIN或者NGX_DONE意味着当前的NGX_HTTP_ACCESS_PHASE阶段没有一次性执行完毕,所以在这一步中会暂时结束当前请求的处理,将控制权交还给事件模块,ngx_http_core_access_phase方法结束。当请求中对应的事件再次触发时才会继续处理该请求。

4)返回NGX_DECLINED意味着handler方法执行完毕且“意犹未尽”,希望立刻执行下一个handler方法,无论其是否属于NGX_HTTP_ACCESS_PHASE阶段,在这一步中只需要把phase_handler加1,同时ngx_http_core_access_phase方法返回NGX_AGAIN即可。

5)现在开始处理非第3、第4步中返回值的情况。由于NGX_HTTP_ACCESS_PHASE阶段是在NGX_HTTP_FIND_CONFIG_PHASE阶段之后的,因此这时请求已经找到了匹配的location配置块,先把location块对应的ngx_http_core_loc_conf_t配置结构体取出来,因为这里有一个配置项satisfy是下一步需要用到的。

6)检查ngx_http_core_loc_conf_t结构体中的satisfy成员,如果值为NGX_HTTP_SATISFY_ALL(即nginx.conf文件中配置了satisfy all参数),则意味着所有NGX_HTTP_ACCESS_PHASE阶段的handler方法必须共同作用于这个请求。这时,handler方法的返回值就具有不同的意义了。如果它的返回值是NGX_OK,则意味着这个handler方法所在的HTTP模块认为当前请求是具备访问权限的,需要再次检查NGX_HTTP_ACCESS_PHASE阶段的下一个HTTP模块的handler方法,于是会跳到第4步执行;反之,如果返回值不是NGX_OK,就意味着当前请求无权访问服务,这时需要跳到第8步调用ngx_http_finalize_request方法结束请求,方法的参数也就是这个返回值。

如果ngx_http_core_loc_conf_t结构体中的satisfy成员值为NGX_HTTP_SATISFY_ANY(即nginx.conf文件中配置了satisfy any参数),也就是说,并不强制要求NGX_HTTP_ACCESS_PHASE阶段的所有handler方法必须同时起作用,那么这时handler方法的返回值又具有了不同的意义。如果该返回值是NGX_OK,则表示第2步执行的handler方法认为这个请求有权限访问服务,而且不用再调用NGX_HTTP_ACCESS_PHASE阶段的其他handler方法了,直接跳到第7步执行;如果返回值是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED,则表示这个HTTP模块的handler方法认为请求没有权限访问服务,但只要NGX_HTTP_ACCESS_PHASE阶段的任何一个handler方法返回NGX_OK就认为请求合法,所以后续的handler方法可能会更改这一结果。这时将请求的access_code成员设置为handler方法的返回值,用于传递当前HTTP模块的处理结果,然后跳到第4步执行下一个handler方法;如果返回值为其他值,可以认为请求绝对无权访问服务,则跳到第8步执行。

7)上面已经解释过,在satisfy any配置下,handler方法返回NGX_OK时意味着这个请求具备访问权限,将请求的access_code成员置为0,跳到第1步执行。

8)调用ngx_http_finalize_request方法结束请求。

虽然ngx_http_core_access_phase方法有些复杂,即它为NGX_HTTP_ACCESS_PHASE阶段中的handler方法的返回值增加了过多的含义,但当我们开发的HTTP模块需要处理请求的访问权限时,就会发现ngx_http_core_access_phase方法给我们带来强大的功能,可以实现复杂的权限控制。