07_Nginx_auth_request模块

07_Nginx_auth_request模块

  • 1. auth_request模块是什么
  • 2. auth_request模块的用途
  • 3. auth_request的启用
  • 4. auth_request的官方示例
  • 5. 源码欣赏
    • 5.1 结构体
    • 5.2 相关函数

1. auth_request模块是什么

ngx_http_auth_request_module模块(1.5.4+)实现了基于一子请求的结果的客户端的授权。如果子请求返回2xx响应码,则允许访问。如果它返回401或403,则访问被拒绝并显示相应的错误代码。子请求返回的任何其他响应代码都被认为是错误的。

auth_request使用的也是subrequest进行子请求。

2. auth_request模块的用途

如图,当我们访问一个资源需要进行鉴权时,可以使用Nginxhttp_auth_request_module模块进行处理
07_Nginx_auth_request模块_第1张图片

3. auth_request的启用

--with-http_auth_request_module
configure阶段使用该命令进行添加

4. auth_request的官方示例

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;
}

5. 源码欣赏

5.1 结构体

源码路径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模块上下文

5.2 相关函数

/**
 * @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;
}



你可能感兴趣的:(nginx)