温馨提示:这篇文章已超过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模块[下]
4. 源码分析
4.1 模块的初始化
4.1.1 ngx_http_userid_add_variables函数添加自定义变量
本函数是在preconfirguration阶段被调用的,用来向nginx框架注册uid_got、uid_set、uid_reset三个变量。
(图片来源网络,侵删)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名字
- 添加userid的base64值,如果配置了mark,则末尾设置mark字符
- 如果user_expire中配置了超时expire,则添加 expires=xxxx的cookie超时信息
- 如果user_flags中配置了secure,则添加secure属性到cookie中
- 如果user_flags中配置了httponly,则添加httponly属性到cookie中
- 如果user_flags中配置了samesite=strict,则添加samesite=strict属性到cookie中
- 如果user_flags中配置了samesite=lax,则添加samesite=lax属性到cookie中
- 如果user_flags中配置了samesite=none,则添加samesite=none属性到cookie中
合成的cookie内容包括如下信息:
/* 计算需要的内存空间的大小 */ 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,意味着该变量不可读,如下:
还没有评论,来说两句吧...