ngx_http_auth_request_module
模块(1.5.4+)实现了基于一子请求的结果的客户端的授权。如果子请求返回2xx响应码,则允许访问。如果它返回401或403,则访问被拒绝并显示相应的错误代码。子请求返回的任何其他响应代码都被认为是错误的。
auth_request
使用的也是subrequest
进行子请求。
如图,当我们访问一个资源需要进行鉴权时,可以使用Nginx
的http_auth_request_module
模块进行处理
--with-http_auth_request_module
在configure
阶段使用该命令进行添加
location /private/ {
auth_request /auth;
# 此处之后可以填资源服务器的地址,表示健全成功后继续访问的资源
...
}
location = /auth {
# 此处填鉴权服务器的地址
proxy_pass ...
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
源码路径src/http/modules/ngx_http_auth_request_module.c
typedef struct {
ngx_str_t uri; // 子请求的url地址
ngx_array_t *vars; // 子请求url中的参数
} ngx_http_auth_request_conf_t; //用于存储在配置文件中的url地址
typedef struct {
ngx_uint_t done; // 标记子请求是否完成
ngx_uint_t status; // 标记子请求响应的状态码
ngx_http_request_t *subrequest; // 指向子请求的指针
} ngx_http_auth_request_ctx_t; // auth_request模块上下文
/**
* @brief auth_request的handle函数
*
* @param r
* @return ngx_int_t
*/
static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{
ngx_table_elt_t *h, *ho; // nginx的一个表结构,key-value
ngx_http_request_t *sr; // 子请求
ngx_http_post_subrequest_t *ps; // 子请求结束时的回调结构体,可注册回调函数
ngx_http_auth_request_ctx_t *ctx; // 父请求上下文,可保存一些信息
ngx_http_auth_request_conf_t *arcf; // auth_request在conf中的配置信息
// 从conf文件中获取auth_request的配置信息
arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
if (arcf->uri.len == 0) { // 如果没有配置此模块的命令,则返回NGX_DECLINED
return NGX_DECLINED;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request handler");
// 获取父请求的上下文,
ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
if (ctx != NULL) { // 父请求第n+1次数 进入ngx_http_auth_request_handler函数
if (!ctx->done) { // 如果子请求没完成,那么返回NGX_AGAIN,告诉Nginx还需要等待网络事件的完成
return NGX_AGAIN;
}
/*
* as soon as we are done - explicitly set variables to make
* sure they will be available after internal redirects
*/
if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
return NGX_ERROR;
}
/* return appropriate status */
// 子请求结束了
if (ctx->status == NGX_HTTP_FORBIDDEN) { // 返回403
return ctx->status;
}
if (ctx->status == NGX_HTTP_UNAUTHORIZED) { // 如果是401没有鉴权错误,则获取子请求的响应头中的鉴权信息,添加到父请求的响应头中去
sr = ctx->subrequest;
h = sr->headers_out.www_authenticate;
if (!h && sr->upstream) {
h = sr->upstream->headers_in.www_authenticate;
}
if (h) {
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
r->headers_out.www_authenticate = ho;
}
return ctx->status;
}
// 子请求鉴权通过返回正确资源
if (ctx->status >= NGX_HTTP_OK
&& ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
{
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"auth request unexpected status: %ui", ctx->status);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
// 父请求第一次进入ngx_http_auth_request_handler函数
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
// 初始化子请求结束回调结构体
ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
if (ps == NULL) {
return NGX_ERROR;
}
ps->handler = ngx_http_auth_request_done;
ps->data = ctx;
// 发起子强求
if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
NGX_HTTP_SUBREQUEST_WAITED)
!= NGX_OK)
{
return NGX_ERROR;
}
/*
* allocate fake request body to avoid attempts to read it and to make
* sure real body file (if already read) won't be closed by upstream
*/
sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (sr->request_body == NULL) {
return NGX_ERROR;
}
// 子请求返回时只包含相应头信息
sr->header_only = 1;
// 父请求上下文记录子请求的指针
ctx->subrequest = sr;
// 保存父请求上下文
ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
return NGX_AGAIN;
}
/**
* @brief 子请求结束时调用的函数
*
* @param r
* @param data
* @param rc
* @return ngx_int_t
*/
static ngx_int_t
ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
ngx_http_auth_request_ctx_t *ctx = data; // 获取父请求的上下文
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request done s:%ui", r->headers_out.status);
ctx->done = 1; // 标记子请求完成且记录子请求额响应头信息
ctx->status = r->headers_out.status;
return rc;
}