深入理解nginx的userid模块[下],深入理解nginx的userid模块(下篇)

马肤

温馨提示:这篇文章已超过442天没有更新,请注意相关的内容是否还可用!

摘要:本文深入探讨了nginx的userid模块(下),详细介绍了该模块的功能和工作原理。通过解析该模块的配置指令和指令参数,深入解析了如何在实际应用中配置和使用该模块。文章还介绍了该模块与其他nginx模块之间的交互和协作方式,为读者提供了全面的nginx userid模块应用指南。

目录

  • 4. 源码分析
    • 4.1 模块的初始化
      • 4.1.1 ngx_http_userid_add_variables函数添加自定义变量
      • 4.1.2 ngx_http_userid_init函数
      • 4.1.3 ngx_http_userid_init_worker worker初始化函数
      • 4.2 ngx_http_userid_filter header过滤函数
      • 4.3 ngx_http_userid_get_uid获取或者创建模块上下文
      • 4.4 ngx_http_userid_set_uid 在http响应头中输出userid
        • 4.4.1 ngx_http_userid_create_uid 创建userid
        • 4.4.2 合成响应的userid cookie值
        • 4.4.3 将userid cookie添加到http响应头中
        • 4.5 自定义变量的获取和设置
          • 4.5.1 uid_got
          • 4.5.2 uid_set
          • 4.5.3 uid_reset

            深入理解nginx的userid模块[上]

            深入理解nginx的userid模块[下],深入理解nginx的userid模块(下篇) 第1张
            (图片来源网络,侵删)

            深入理解nginx的userid模块[下]

            4. 源码分析

            4.1 模块的初始化

            4.1.1 ngx_http_userid_add_variables函数添加自定义变量

              本函数是在preconfirguration阶段被调用的,用来向nginx框架注册uid_got、uid_set、uid_reset三个变量。

            深入理解nginx的userid模块[下],深入理解nginx的userid模块(下篇) 第2张
            (图片来源网络,侵删)
            • uid_got: cookie的名字和接收到的客户端的标识。

            • uid_set: cookie的名字和发送的客户端的标识。

            • uid_reset: 如果这个变量被设置为不是"0"的非空字符串,客户端的标识将被重置。如果被设置为"log",那么将导致重置客户端标识的日志被写入error_log。

              4.1.2 ngx_http_userid_init函数

                本函数是在postconfiguration阶段被调用的,让ngx_http_userid_module作为filter形式挂载到filter链中。源码如下:

              static ngx_int_t
              ngx_http_userid_init(ngx_conf_t *cf)
              {
                  ngx_http_next_header_filter = ngx_http_top_header_filter;
                  ngx_http_top_header_filter = ngx_http_userid_filter;
                  return NGX_OK;
              }
              

                 从源码可以看到,本模块作为一个header filter挂载到了header过滤链中。在nginx向客户端发送http头响应的时候,会调用这是挂载的ngx_http_userid_filter函数进行相应的处理。

              4.1.3 ngx_http_userid_init_worker worker初始化函数

                本函数是在nginx对worker进行进行初始化的时候进行调用的,调用顺序是发生在ngx_http_userid_init函数之后的。源码如下:

              static ngx_int_t
              ngx_http_userid_init_worker(ngx_cycle_t *cycle)
              {
                  struct timeval  tp;
                  ngx_gettimeofday(&tp);
                  /* use the most significant usec part that fits to 16 bits */
                  start_value = (((uint32_t) tp.tv_usec / 20) 
                  ngx_http_userid_ctx_t   *ctx;
                  ngx_http_userid_conf_t  *conf;
                  /* 如果是subrequest,则跳过本模块的逻辑 */
                  if (r != r-main) {
                      return ngx_http_next_header_filter(r);
                  }
                  conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
                  /* 如果enable不是v1和on(就是v2),则跳过本模块的逻辑 */
                  if (conf->enable  
              

              4.3 ngx_http_userid_get_uid获取或者创建模块上下文

                本函数检查是否模块上下文已经存在,如果没有创建则创建一个新的模块上下文,最后返回上下文的指针,源码如下:

                  ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
                  if (ctx) {
                      return ctx;
                  }
                  if (ctx == NULL) {
                      ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
                      if (ctx == NULL) {
                          return NULL;
                      }
                      ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
                  }
              

                接着,分析http请求头中的cookie是否有配置文件中指定名字的cookie,如果存在,则提取出这个cookie的值,并且进行base64解码,源码如下:

              /* 从请求头中获取cookie的值 */
              cookie = ngx_http_parse_multi_header_lines(r, r->headers_in.cookie,
              &conf->name, &ctx->cookie);
              if (cookie == NULL) {
              	return ctx;
              }
              ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
              			   "uid cookie: \"%V\"", &ctx->cookie);
              /* 本模块限制了userid的cookie字段值的长度不能少于22位 */
              if (ctx->cookie.len connection->log, 0,
              				  "client sent too short userid cookie \"%V\"",
              				  &cookie->value);
              	return ctx;
              }
              src = ctx->cookie;
              /*
               * we have to limit the encoded string to 22 characters because
               *  1) cookie may be marked by "userid_mark",
               *  2) and there are already the millions cookies with a garbage
               *     instead of the correct base64 trail "=="
               */
              src.len = 22;
              dst.data = (u_char *) ctx->uid_got;
              /* 对userid的值进行base64解码,得到解码后的userid */
              if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {
              	ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
              				  "client sent invalid userid cookie \"%V\"",
              				  &cookie->value);
              	return ctx;
              }
              ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
              			   "uid: %08XD%08XD%08XD%08XD",
              			   ctx->uid_got[0], ctx->uid_got[1],
              			   ctx->uid_got[2], ctx->uid_got[3]);
              return ctx;
              

              4.4 ngx_http_userid_set_uid 在http响应头中输出userid

              4.4.1 ngx_http_userid_create_uid 创建userid

                首先,在本函数中检查如果已经设置过userid了(可能是在获取uid_set变量的时候已经创建过了),那么则直接跳过。

                  if (ctx->uid_set[3] != 0) {
                      return NGX_OK;
                  }
              

                其次,如果客户端带了userid字段,并且userid_reset变量没有打开重置的标记,则直接返回客户端设置的userid字段的内容,源码如下:

                 if (ctx->uid_got[3] != 0) {   /* 表示客户端请求的时候带了userid过来 */
                      
                      /* 检查userid_reset变量的标记是否需要重置本次响应的userid */
                      vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);
                      if (vv == NULL || vv->not_found) {
                          return NGX_ERROR;
                      }
                      if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {
              			/* userid_reset变量为空,或者是'0',则表示不重置userid,
              			   而是使用客户端请求的userid
              			 */
                          if (conf->mark == '
                  if (conf->enable == NGX_HTTP_USERID_V1) {
                      if (conf->service == NGX_CONF_UNSET) {
                          ctx->uid_set[0] = 0;
                      } else {
                          ctx->uid_set[0] = conf->service;
                      }
                      ctx->uid_set[1] = (uint32_t) ngx_time();
                      ctx->uid_set[2] = start_value;
                      ctx->uid_set[3] = sequencer_v1;
                      sequencer_v1 += 0x100;
                  }
              
              ' || (ctx->cookie.len > 23 && ctx->cookie.data[22] == conf->mark && ctx->cookie.data[23] == '=')) { return NGX_OK; } /* 这里通过赋值,将返回客户端设置的userid */ ctx->uid_set[0] = ctx->uid_got[0]; ctx->uid_set[1] = ctx->uid_got[1]; ctx->uid_set[2] = ctx->uid_got[2]; ctx->uid_set[3] = ctx->uid_got[3]; return NGX_OK; } else { ctx->reset = 1; /* userid_reset = log,则记录日志到error_log */ if (vv->len == 3 && ngx_strncmp(vv->data, "log", 3) == 0) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "userid cookie \"%V=%08XD%08XD%08XD%08XD\" was reset", &conf->name, ctx->uid_got[0], ctx->uid_got[1], ctx->uid_got[2], ctx->uid_got[3]); } } }

                根据配置的版本类型,生成v1版本的userid, userid为 service + time + start_value + 序列值,如下:

              4.4.2 合成响应的userid cookie值

                根据配置的版本类型,生成v2版本的userid, userid的第一部分取自IP地址的一部分,或者用配置的userid_service值,第二部分是时间,第三部分是start_value,第四部分是序列值,源码如下:

              	if (conf->service == NGX_CONF_UNSET) {
              		c = r->connection;
              		if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
              			return NGX_ERROR;
              		}
              		switch (c->local_sockaddr->sa_family) {
              #if (NGX_HAVE_INET6)
              		case AF_INET6:
              			sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
              			p = (u_char *) &ctx->uid_set[0];
              			*p++ = sin6->sin6_addr.s6_addr[12];
              			*p++ = sin6->sin6_addr.s6_addr[13];
              			*p++ = sin6->sin6_addr.s6_addr[14];
              			*p = sin6->sin6_addr.s6_addr[15];
              			break;
              #endif
              #if (NGX_HAVE_UNIX_DOMAIN)
              		case AF_UNIX:
              			ctx->uid_set[0] = 0;
              			break;
              #endif
              		default: /* AF_INET */
              			sin = (struct sockaddr_in *) c->local_sockaddr;
              			ctx->uid_set[0] = sin->sin_addr.s_addr;
              			break;
              		}
              	} else {
              		ctx->uid_set[0] = htonl(conf->service);
              	}
              	ctx->uid_set[1] = htonl((uint32_t) ngx_time());
              	ctx->uid_set[2] = htonl(start_value);
              	ctx->uid_set[3] = htonl(sequencer_v2);
              	sequencer_v2 += 0x100;
              	if (sequencer_v2  
              
            • 添加cookie名字
            •    合成的cookie内容包括如下信息:

              1. 添加userid的base64值,如果配置了mark,则末尾设置mark字符
              2. 如果user_expire中配置了超时expire,则添加 expires=xxxx的cookie超时信息
              3. 如果user_flags中配置了secure,则添加secure属性到cookie中
              4. 如果user_flags中配置了httponly,则添加httponly属性到cookie中
              5. 如果user_flags中配置了samesite=strict,则添加samesite=strict属性到cookie中
              6. 如果user_flags中配置了samesite=lax,则添加samesite=lax属性到cookie中
              7. 如果user_flags中配置了samesite=none,则添加samesite=none属性到cookie中
              8.     /* 计算需要的内存空间的大小  */
                    len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
                    if (conf->expires) {
                        len += sizeof(expires) - 1 + 2;
                    }
                    if (conf->domain.len) {
                        len += conf->domain.len;
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {
                        len += sizeof("; secure") - 1;
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {
                        len += sizeof("; httponly") - 1;
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {
                        len += sizeof("; samesite=strict") - 1;
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {
                        len += sizeof("; samesite=lax") - 1;
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {
                        len += sizeof("; samesite=none") - 1;
                    }
                    /* 为cookie分配内存 */
                    cookie = ngx_pnalloc(r->pool, len);
                    if (cookie == NULL) {
                        return NGX_ERROR;
                    }
                    /* 往cookie内存中存入内容 */
                    p = ngx_copy(cookie, conf->name.data, conf->name.len);
                    *p++ = '=';
                    if (ctx->uid_got[3] == 0 || ctx->reset) {
                        src.len = 16;
                        src.data = (u_char *) ctx->uid_set;
                        dst.data = p;
                        ngx_encode_base64(&dst, &src);
                        p += dst.len;
                        if (conf->mark) {
                            *(p - 2) = conf->mark;
                        }
                    } else {
                        p = ngx_cpymem(p, ctx->cookie.data, 22);
                        *p++ = conf->mark;
                        *p++ = '=';
                    }
                    if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
                        p = ngx_cpymem(p, expires, sizeof(expires) - 1);
                    } else if (conf->expires) {
                        p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
                        p = ngx_http_cookie_time(p, ngx_time() + conf->expires);
                    }
                    p = ngx_copy(p, conf->domain.data, conf->domain.len);
                    p = ngx_copy(p, conf->path.data, conf->path.len);
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {
                        p = ngx_cpymem(p, "; secure", sizeof("; secure") - 1);
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {
                        p = ngx_cpymem(p, "; httponly", sizeof("; httponly") - 1);
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {
                        p = ngx_cpymem(p, "; samesite=strict", sizeof("; samesite=strict") - 1);
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {
                        p = ngx_cpymem(p, "; samesite=lax", sizeof("; samesite=lax") - 1);
                    }
                    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {
                        p = ngx_cpymem(p, "; samesite=none", sizeof("; samesite=none") - 1);
                    }
                    set_cookie = ngx_list_push(&r->headers_out.headers);
                    if (set_cookie == NULL) {
                        return NGX_ERROR;
                    }
                

                在合成之前,本模块首先需要计算实际需要的内存空间大小,然后再逐个字段生成到目标cookie中,源码如下:

              4.4.3 将userid cookie添加到http响应头中

                  
                  /* 添加一个新的http header */
                  set_cookie = ngx_list_push(&r->headers_out.headers);
                  if (set_cookie == NULL) {
                      return NGX_ERROR;
                  }
                  /* 将cookie值写入上面添加的http header中, header名字为Set-Cookie */
                  set_cookie->hash = 1;
                  ngx_str_set(&set_cookie->key, "Set-Cookie");
                  set_cookie->value.len = p - cookie;
                  set_cookie->value.data = cookie;
                  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                 "uid cookie: \"%V\"", &set_cookie->value);
                  if (conf->p3p.len == 0) {
                      return NGX_OK;
                  }
                  /* 如果配置了p3p,那么再添加一个P3P的HTTP头 */
                  p3p = ngx_list_push(&r->headers_out.headers);
                  if (p3p == NULL) {
                      return NGX_ERROR;
                  }
                  p3p->hash = 1;
                  ngx_str_set(&p3p->key, "P3P");
                  p3p->value = conf->p3p;
              

              源码如下:

              4.5 自定义变量的获取和设置

              4.5.1 uid_got

              static ngx_int_t
              ngx_http_userid_got_variable(ngx_http_request_t *r,
                  ngx_http_variable_value_t *v, uintptr_t data)
              {
                  ngx_http_userid_ctx_t   *ctx;
                  ngx_http_userid_conf_t  *conf;
                  conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
              	/* 如果没有启用本模块,则返回未找到标记 */
                  if (conf->enable == NGX_HTTP_USERID_OFF) {
                      v->not_found = 1;
                      return NGX_OK;
                  }
              	/* 获取模块上下文或者创建新的模块上下文 */
                  ctx = ngx_http_userid_get_uid(r->main, conf);
                  if (ctx == NULL) {
                      return NGX_ERROR;
                  }
              	/* 如果客户端请求头中带了userid的cookie信息,则返回这个信息 */
                  if (ctx->uid_got[3] != 0) {
                  	/* 对ud_got进行打印输出,格式name=00001111222233334444555566667777*/
                      return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);
                  }
              	/* 客户端请求头中没有userid的cookie信息,返回未找到 */
                  v->not_found = 1;
                  return NGX_OK;
              }
              

                 uid_got是一个只读变量,用来获取当前请求的客户端请求头中发送过来的userid的信息。源码如下:

              4.5.2 uid_set

              static ngx_int_t
              ngx_http_userid_set_variable(ngx_http_request_t *r,
                  ngx_http_variable_value_t *v, uintptr_t data)
              {
                  ngx_http_userid_ctx_t   *ctx;
                  ngx_http_userid_conf_t  *conf;
                  conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
              	/* 如果没有启用本模块,则返回未找到标记 */
                  if (conf->enable not_found = 1;
                      return NGX_OK;
                  }
                  
              	/* 获取模块上下文或者创建新的模块上下文 */
                  ctx = ngx_http_userid_get_uid(r->main, conf);
                  if (ctx == NULL) {
                      return NGX_ERROR;
                  }
              	/* 创建一个新的userid */
                  if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {
                      return NGX_ERROR;
                  }
                  if (ctx->uid_set[3] == 0) {
                      v->not_found = 1;
                      return NGX_OK;
                  }
              	/* 对ud_set进行打印输出,格式name=00001111222233334444555566667777*/
                  return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);
              }
              

                 uid_got是一个只读变量,用于获取当前请求的响应头中设置的userid的信息,源码如下:

              4.5.3 uid_reset

                 var->get_handler = ngx_http_userid_set_variable;
                  var = ngx_http_add_variable(cf, &ngx_http_userid_reset,
                                              NGX_HTTP_VAR_CHANGEABLE);   /* 设置可修改 */
                  if (var == NULL) {
                      return NGX_ERROR;
                  }
                  var->get_handler = ngx_http_userid_reset_variable;  /* 设置不可读 */
                  n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);
                  if (n == NGX_ERROR) {
                      return NGX_ERROR;
                  }
                  ngx_http_userid_reset_index = n;    /* 设置模块快速查询该变量的索引*/
              

                该变量是一个可写变量,含义见 4.1.1节的说明,配置代码如下:

              static ngx_int_t
              ngx_http_userid_reset_variable(ngx_http_request_t *r,
                  ngx_http_variable_value_t *v, uintptr_t data)
              {
                  *v = ngx_http_variable_null_value;
                  return NGX_OK;
              }
              

                这里设置了get_handler为ngx_http_userid_reset_variable,而该函数在被进行变量获取的调用时候直接返回null,意味着该变量不可读,如下:


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码