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

这篇具有很好参考价值的文章主要介绍了深入理解nginx的userid模块[下]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

4. 源码分析

4.1 模块的初始化

4.1.1 ngx_http_userid_add_variables函数添加自定义变量

  本函数是在preconfirguration阶段被调用的,用来向nginx框架注册uid_gotuid_setuid_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) << 16) | ngx_pid;

    return NGX_OK;
}

  这里设置了一个start_value的变量值,未来这个值将作为userid的一部分。

4.2 ngx_http_userid_filter header过滤函数

  本模块的真正处理逻辑是在ngx_http_userid_filter函数中来实现的。当nginx开始向客户端发送HTTP响应头的时候,就会开始调用header 过滤链,而本模块就会得到处理响应信息的机会,对HTTP响应头进行处理。

  如果本模块配置了enable为v1或者v2(默认),那么就执行处理逻辑,否则直接跳过本模块的逻辑。源码如下:

static ngx_int_t
ngx_http_userid_filter(ngx_http_request_t *r)
{
    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 < NGX_HTTP_USERID_V1) {
        return ngx_http_next_header_filter(r);
    }

	/* 获取或者创建一个新的模块上下文 */
    ctx = ngx_http_userid_get_uid(r, conf);

    if (ctx == NULL) {
        return NGX_ERROR;
    }

    /* 在响应头的cookie中输出userid */
    if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
        return ngx_http_next_header_filter(r);
    }

    return NGX_ERROR;
}

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 < 22) {
	ngx_log_error(NGX_LOG_ERR, r->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 == '\0'
                || (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 + 序列值,如下:

    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;

    }

  根据配置的版本类型,生成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 < 0x03030302) {
		sequencer_v2 = 0x03030302;
	}

4.4.2 合成响应的userid cookie值

   合成的cookie内容包括如下信息:

  1. 添加cookie名字
  2. 添加userid的base64值,如果配置了mark,则末尾设置mark字符
  3. 如果user_expire中配置了超时expire,则添加 expires=xxxx的cookie超时信息
  4. 如果user_flags中配置了secure,则添加secure属性到cookie中
  5. 如果user_flags中配置了httponly,则添加httponly属性到cookie中
  6. 如果user_flags中配置了samesite=strict,则添加samesite=strict属性到cookie中
  7. 如果user_flags中配置了samesite=lax,则添加samesite=lax属性到cookie中
  8. 如果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;
    }

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

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

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

4.5.2 uid_set

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

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 < NGX_HTTP_USERID_V1) {
        v->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);
}

4.5.3 uid_reset

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

   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;    /* 设置模块快速查询该变量的索引*/

  这里设置了get_handler为ngx_http_userid_reset_variable,而该函数在被进行变量获取的调用时候直接返回null,意味着该变量不可读,如下:文章来源地址https://www.toymoban.com/news/detail-855692.html

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

到了这里,关于深入理解nginx的userid模块[下]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Nginx目录结构简介:深入理解Nginx的默认文件和目录

    第一章 Nginx的默认目录结构 当你安装Nginx后,它的默认目录结构如下: 让我们逐个了解这些目录和文件的作用。 第二章 conf目录 conf目录包含了Nginx的配置文件,其中nginx.conf是Nginx主配置文件,它包含了所有全局的Nginx配置项。mime.types文件包含了MIME类型的定义,它告诉Nginx如

    2024年02月13日
    浏览(62)
  • “深入理解Nginx的负载均衡与动静分离“

    在现代互联网应用中,高性能和可扩展性是至关重要的。Nginx作为一款高性能的Web服务器和反向代理服务器,被广泛应用于各种规模的网站和应用程序中。本文将深入探讨Nginx的负载均衡和动静分离的原理与实践,帮助读者更好地理解和应用这些功能。 Nginx是一款轻量级的高性

    2024年02月06日
    浏览(44)
  • 深入理解nginx server_name

    一、基础概念 nginx是一款轻量级的web服务器,同时也是一款高性能的反向代理服务器。server_name是nginx配置文件中比较重要的参数之一,用于指定虚拟主机的域名或IP地址。如果一个请求的Host头中的值和server_name匹配,则nginx将会使用该虚拟主机配置处理该请求。 例如: 当该服

    2024年02月10日
    浏览(39)
  • 前后端分离技术逐步深入,让你更加深入理解Nginx+Tomcat

    你提到了熟悉Tomcat和Nginx服务器的配置,以及应用前后端分离技术,请解释一下Tomcat和Nginx的主要作用是什么,以及在前后端分离中它们的角色是什么? 标准回答: Tomcat是一个Java应用服务器,主要用于运行Java Web应用程序。它负责处理HTTP请求、执行Servlet和JSP等Java代码,并将

    2024年02月07日
    浏览(41)
  • Nginx深入:nginx功能模块、目录结构及配置文件详解

    1、Nginx 核心功能模块(Core functionality) Nginx核心功能模块负责Nginx的全局应用,主要对应主配置文件的核心层(Main层)和事件(Events)层,这里有很多 Nginx 必需的全局参数配置。 有关核心功能模块的官方文档为:http://nginx.org/en/docs/ngx_core_module.html 2、标准的 HTTP 功能模块集合

    2024年02月14日
    浏览(47)
  • 深入浅出对话系统——自然语言理解模块

    首先回顾一下自然语言理解的概念。 自然语言理解(Natural Language Understanding)包含三个子模块: 其中领域识别和意图识别都是分类问题,而语义槽填充属于序列标注问题。所以,在自然语言理解中,我们要解决两个分类任务和一个序列标注任务。既然其中两个问题都属于分类任

    2024年02月08日
    浏览(90)
  • 函数探秘:深入理解C语言函数,实现高效模块化编程

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 在数学中我们就知道了函数这个概念,而C语言同样引入了函数这个概念,那C语言的函数到底是什么样的呢? 在C语言中, 函数也叫子程序,它是一段可以

    2024年03月09日
    浏览(70)
  • 深入理解Flink IntervalJoin源码

    IntervalJoin基于connect实现,期间会生成对应的IntervalJoinOperator。 并且会根据给定的自定义Function构建出对应的TwoInputTransformation,以便能够参与Transformation树的构建。 作为ConnectedStreams,一旦left or right流中的StreamRecord抵达,就会被及时处理: 两者的处理逻辑是相同的: 先取出当

    2024年02月12日
    浏览(46)
  • 【排序算法】 归并排序详解!深入理解!思想+源码实现!

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 算法—排序篇 🌄 莫道桑榆晚,为霞尚满天! ​ 什么是归并?通过归并排序就能让数据有序?分治法是怎么在归并排序上应用的?本文将对归并排序进行细致入微的讲解,庖丁解牛般让你彻底明白归并排序! 归并排序(MERGE-SORT)是建

    2024年02月06日
    浏览(46)
  • 【linux深入剖析】深入理解基础外设--磁盘以及理解文件系统

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 我们所有的文件都是与进程相关的文件–进程打开的文件 系统中是不是所有的文件都被打开了呢?如果没

    2024年04月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包