• NGINX模块开发 之 验证URL參数


    版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/RoyalApex/article/details/26404379


    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 编译project

      Apache, IIS等其它服务器的第三方模块是通过动态链接库的方式增加到程序中。而NGINX的第三方模块须要增加NGINX源代码project一同编译。


      NGINX提供了一种简单的方式将第三方开发的模块编译到NGINX中。首先。将源代码全部放入一个文件夹下,同一时候在该文件夹下创建一个名为config的文件(config的配置格式在4.1节描写叙述);其次,在configure脚本运行时增加參数--add-module=PATH(PATH为第三方模块的源代码路径)。


    4.1 编辑编译配置

      完毕以上编辑工作后,最后的工作就是将编写的代码增加NGINXproject。

    其处理过程例如以下:

        #cd src/ext/login

        #vim config

        在config文件里输入例如以下内容:

    ngx_addon_name=ngx_http_login_module
    HTTP_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模块增加到project编译环境中.

    图2 变量值与等号之间有空格


    4.2 增加编译project

      完毕config的编辑后,LOGIN模块还没有增加到编译project中。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 验证失败



  • 相关阅读:
    远程接入系统的问题
    FastReport
    通用FASTREPORT打印模块及接口方法
    cxGrid控件过滤筛选后如何获更新筛选后的数据集
    Oracle Data Integrator 12c (12.1.2)新特性
    ODI 12c 安装
    Kafka面试题
    Hadoop面试题
    Spark面试题
    JAVA面试题-数组字符串基础
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10057498.html
Copyright © 2020-2023  润新知