1. 程式人生 > >NGINX模組開發 之 驗證URL引數

NGINX模組開發 之 驗證URL引數

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               


1 需求

    要求在瀏覽器位址列中輸入"localhost/login?user=qifeng&passwd=123456",並在瀏覽器上顯示驗證結果(成功 或 失敗)。以下是在NGINX中新增一個LOGIN模組的整個處理過程。


2 修改配置

  根據需求修改配置檔案nginx.conf,在http{...}的server{...}中增加location配置資訊:


圖1 修改配置

(注意:將passwd的值"abcd"改為“123456”)


3 編寫程式碼

3.1 建立原始碼目錄

    在NGINX原始碼目錄src下新建ext資料夾,src/ext用於存放所有擴充套件模組程式碼,src/ext/login則用於存放LOGIN模組的程式碼.

    #mkdir -p src/ext/login

    #cd src/ext/login

    #vim ngx_http_login_module.c


3.2 定義配置結構

  LOGIN模組主要實現的是對使用者(user)和密碼(password)的驗證,因此,配置資訊結構中需要包含user欄位和password欄位,故其結構定義如下:(命名規則:ngx_http_模組名_(main|srv|loc)conf_t)

/* 配置項結構體:用於存放配置項和對應值 */typedef struct{    ngx_str_t user;    ngx_str_t passwd;}ngx_http_login_loc_conf_t;

程式碼1 定義配置結構

3.3 設定解析陣列

  從圖1中可知:配置項有user、passwd和check.解析這些配置項的配置項的解析是通過配置ngx_command_t陣列,配置項解析陣列如下:(命名規則:ngx_http_模組名_commands)

/* 配置項解析陣列:配置指令是通過此陣列依次解析的. */static ngx_command_t ngx_http_login_commands[] ={    {        ngx_string("user"),                            /* 配置項名 */        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,            /* 所屬模組和配置值個數 */        ngx_conf_set_str_slot,                         /* 解析該配置項的回撥 */        NGX_HTTP_LOC_CONF_OFFSET,                      /* 配置儲存位置 */        offsetof(ngx_http_login_loc_conf_t, name),     /* 配置值賦給的變數 */        NULL    },    {        ngx_string("passwd"),        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,        ngx_conf_set_str_slot,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_login_loc_conf_t, passwd),        NULL    },    {        ngx_string("check"),        NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,        ngx_http_login_check,        NGX_HTTP_LOC_CONF_OFFSET,        0,        NULL    },    ngx_null_command};

程式碼2 配置項解析陣列


3.4 設定配置回撥

    定義模組配置解析過程中各階段的處理回撥:(命名規則:ngx_http_模組名_module_ctx)

static ngx_http_module_t ngx_http_login_module_ctx =                            {                                                                                      NULL,                               /* 讀入配置前的回撥 */    NULL,                               /* 讀入配置後的回撥 */    NULL,                               /* 建立main配置時的回撥 */    NULL,                               /* 初始化main配置時的回撥 */    NULL,                               /* 建立srv配置時的回撥 */    NULL,                               /* 合併srv配置時的回撥 */    ngx_http_login_create_loc_conf,     /* 建立loc配置時的回撥 - LOGIN模組的配置在location中,因此需要註冊此函式 */    NULL                                /* 合併loc配置時的回撥 */};
程式碼3 配置解析各階段回撥

  絕大多數handler只需要設定最後面回撥函式,即:設定ngx_http_xxx_create_loc_conf和ngx_http_xxx_merge_loc_conf.前者用於特定location的記憶體分配,而後者用來設定預設值以及合併繼承過來的配置值,同時往往還負責配置值的合法性驗證,如果不合法,則退出後續處理。

3.5 設定模組屬性

  NGINX中包含了很多模組,所有模組都是通過ngx_module_t型別,但每個模組擁有不同的屬性。通過設定各模組不同的屬性來控制各模組的行為。LOGIN模組的模組屬性設定如下:

ngx_module_t ngx_http_login_module =                                            {                                                                                   NGX_MODULE_V1,                      /* 含多個欄位:一般使用此巨集賦值 */    &ngx_http_login_module_ctx,         /* 當前模組上下文回撥 */    ngx_http_login_commands,            /* 配置指令解析陣列 */    NGX_HTTP_MODULE,                    /* 模組型別 */    NULL,                               /* 初始化master時的回撥 */    NULL,                               /* 初始化module時的回撥 */    NULL,                               /* 初始化工作程序時的回撥 */    NULL,                               /* 初始化執行緒時的回撥 */    NULL,                               /* 退出執行緒時的回撥 */    NULL,                               /* 退出工作程序時的回撥 */    NULL,                               /* 退出master時的回撥 */    NGX_MODULE_V1_PADDING               /* 含多個欄位:一般使用此巨集賦值 */};

程式碼4 設定模組屬性

3.6 編寫函式程式碼

  在程式碼3中的定義配置解析各階段的回撥設定了建立loc配置時的回撥ngx_http_login_create_loc_conf(),其主要功能是為location配置分配記憶體空間,實現程式碼如下:

/****************************************************************************** **函式名稱: ngx_http_login_create_loc_conf **功    能: 為LOGIN的配置結構分配記憶體空間 **輸入引數: **     cf: 配置資訊物件 **輸出引數: NONE **返    回: 儲存LOGIN配置的記憶體地址 **實現描述: **注意事項: **作    者: # Qifeng.zou # 2014.05.26 # ******************************************************************************/static void *ngx_http_login_create_loc_conf(ngx_conf_t *cf)                  {    ngx_http_login_loc_conf_t *llcf = NULL;    llcf = ngx_palloc(cf->pool, sizeof(ngx_http_login_loc_conf_t));    if(NULL == llcf)    {        return NGX_CONF_ERROR;    }    memset(llcf, 0, sizeof(ngx_http_login_loc_conf_t));    return lcf;}

程式碼5 建立loc配置時的回撥

  在程式碼1中的定義配置項解析陣列中設定了配置項check的回撥ngx_http_login_check(),其主要功能解析配置項check,並設定解析後的處理函式,實現程式碼如下:

/****************************************************************************** **函式名稱: ngx_http_login_check **功    能: 配置項check的解析處理回撥 **輸入引數: **     cf: 配置資訊物件 **     cmd: 當前正在解析的配置項解析陣列 **     conf: 自定義配置結構體ngx_http_login_loc_conf_t的地址 **輸出引數: NONE **返    回: NGX_CONF_OK:Success Other:Failed **實現描述: **注意事項: **作    者: # Qifeng.zou # 2014.05.26 # ******************************************************************************/static int ngx_http_login_check(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ngx_http_core_loc_conf_t *clcf = NULL;    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);    clcf->handler = ngx_http_login_check_handler;    ngx_conf_set_str_slot(cf, cmd, conf);    return NGX_CONF_OK;}
程式碼6 CHECK配置項解析處理

  在ngx_http_login_check()中對配置項check進行了解析處理,同時設定瞭解析後的處理函式:ngx_http_login_check_handler(),其主要功能是檢測URL中的引數user和passwd的合法性,並返回最終的驗證結果。實現程式碼如下:

/****************************************************************************** **函式名稱: ngx_http_login_check_handler **功    能: 驗證user和passwd的合法性 **輸入引數: **     r: HTTP請求. **輸出引數: NONE **返    回: 0:Success !0:Failed **實現描述: **    1.必須是GET或HEAD請求 **    2.獲取LOGIN配置資訊 **    3.提取URL引數 **    4.驗證URL引數合法性 **    5.傳送應答資料 **注意事項: **作    者: # Qifeng.zou # 2014.05.26 # ******************************************************************************/static int ngx_http_login_check_handler(ngx_http_request_t *r){    ngx_int_t ret = 0;    ngx_str_t user, passwd, repmsg;    ngx_http_login_loc_conf_t *llcf = NULL;     /* 1. 必須是GET或HEAD請求 */    if(!(r->method &(NGX_HTTP_GET | NGX_HTTP_HEAD)))    {        return NGX_HTTP_NOT_ALLOWED;    }    ret = ngx_http_discard_request_body(r); /* 丟棄請求報文體 */    if(NGX_OK != ret)    {        return ret;    }                                                                                    /* 2. 獲取LOGIN配置資訊 */    llcf = ngx_http_get_module_loc_conf(r, ngx_http_login_module);    /* 3. 提取URL引數      在網址輸入欄輸入"localhost/query?user=qifeng&passwd=123456"          則:r->args = "user=qifeng&passwd=123456",因此提取網頁引數只需對r->args進行解析即可. */    query_string(r, &user, "user"); /* 函式:query_string()的功能是從字串r->args中找到對應的引數及值(請自己實現) */    query_string(r, &passwd, "passwd");    /* 4. 驗證URL引數合法性 */    if((user.len == llcf->user.len)        && (0 == strcmp(llcf->user.data, user.data)        && (passwd.len == llcf->passwd.len)        && (0 == strcmp(llcf->user.data, passwd.data))    {        ngx_str_set(&repmsg, "Success");    }    else    {        ngx_str_set(&repmsg, "Failed");    }    /* 5. 傳送應答資料 */    return ngx_http_send_rep(r, &repmsg);}/****************************************************************************** **函式名稱: ngx_http_send_rep **功    能: 傳送應答資料 **輸入引數: **     r: Http request. **     repmsg: 應答訊息 **輸出引數: NONE **返    回: 0:Success !0:Failed **實現描述: **    1.傳送應答頭 **    2.傳送應答體 **注意事項: **作    者: # Qifeng.zou # 2014.05.26 # ******************************************************************************/static int ngx_http_send_rep(ngx_http_request_t *r, const ngx_str_t *repmsg){    ngx_int_t ret = 0;    ngx_buf_t *b = NULL;    ngx_chain_t out;    ngx_str_t type = ngx_string("text/plain");    /* 1.傳送應答頭 */    r->headers_out.status = NGX_HTTP_OK;    r->headers_out.content_type = type;    r->headers_out.content_length_n = repmsg->len;    ret = ngx_http_send_header(r);    if((NGX_ERROR == ret) || (ret > NGX_OK) || (r->header_only))    {        return ret;    }    /* 2.傳送應答體 */    b = ngx_create_temp_buf(r->pool, repmsg->len);    if(NULL == b)    {        return NGX_HTTP_INTERNAL_SERVER_ERROR;    }    ngx_memcpy(b->pos, repmsg->data, repmsg->len);    b->last = b->pos + repmsg->len;    b->last_buf = 1;    out.buf = b;    out.next = NULL;    return ngx_http_output_filter(r, &out);}
程式碼7 合法性驗證

4 編譯工程

  Apache, IIS等其他伺服器的第三方模組是通過動態連結庫的方式加入到程式中,而NGINX的第三方模組需要加入NGINX原始碼工程一同編譯。

  NGINX提供了一種簡單的方式將第三方開發的模組編譯到NGINX中。首先,將原始碼全部放入一個目錄下,同時在該目錄下建立一個名為config的檔案(config的配置格式在4.1節描述);其次,在configure指令碼執行時加入引數--add-module=PATH(PATH為第三方模組的原始碼路徑)。


4.1 編輯編譯配置

  完成以上編輯工作後,最後的工作就是將編寫的程式碼加入NGINX工程。其處理步驟如下:

    #cd src/ext/login

    #vim config

    在config檔案中輸入如下內容:

ngx_addon_name=ngx_http_login_moduleHTTP_MODULES="$HTTP_MODULES ngx_http_login_module"NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_login_module.c"
程式碼8 編譯配置

  注意:以上config檔案中的等號(=)前後不能有空格,否則執行./configure --add-module=src/ext/login時,並不能將login模組加入到工程編譯環境中.

圖2 變數值與等號之間有空格


4.2 加入編譯工程

  完成config的編輯後,LOGIN模組還沒有加入到編譯工程中。NGINX的編譯指令碼比較複雜,功能也十分強大,在編譯之前必須告知NGINX編譯指令碼到指定的路徑去新增LOGIN模組:

    #./configure --with-debug --add-module=src/ext/login

    #make

    #make install


4.3 最終測試結果

  輸入“localhost/login?user=qifeng&passwd=123456”將會返回成功。如下圖所示:


圖3 驗證成功


  輸入“localhost/login?user=zhangsan&passwd=123456”將會返回失敗。如下圖所示:


圖4 驗證失敗



           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述