美文网首页
第九章 Nginx的http模块介绍

第九章 Nginx的http模块介绍

作者: hubery_zhao | 来源:发表于2023-03-03 15:14 被阅读0次

    http请求11个处理阶段

    nginx将一个http请求分为多个阶段,以模块为单位进行处理,其将http请求的处理过程分为11个阶段,各个阶段可以包含任意多个heep的模块并以流水线的方式处理请求。 这11个http阶段如下:

    POST_READ

    POST_READ:在 read 完请求的头部之后,在没有对头部做任何处理之前,想要获取到一些原始的值,就应该在这个阶段进行处理。这里面会涉及到一个 realip 模块。

    SERVER_REWRITE

    SERVER_REWRITE:和下面的 REWRITE 阶段一样,都只有一个模块叫 rewrite 模块,一般没有第三方模块会处理这个阶段。

    FIND_CONFIG

    FIND_CONFIG:做 location 的匹配,暂时没有模块会用到。

    REWRITE

    REWRITE:对 URL 做一些处理。

    POST_WRITE

    POST_WRITE:处于 REWRITE 之后,也是暂时没有模块会在这个阶段出现。
    接下来是确认用户访问权限的三个模块:

    PREACCESS

    PREACCESS:是在 ACCESS 之前要做一些工作,例如并发连接和 QPS 需要进行限制,涉及到两个模块:limt_conn 和 limit_req

    ACCESS

    ACCESS:核心要解决的是用户能不能访问的问题,例如 auth_basic 是用户名和密码,access 是用户访问 IP,auth_request 根据第三方服务返回是否可以去访问。

    POST_ACCESS

    POST_ACCESS:是在 ACCESS 之后会做一些事情,同样暂时没有模块会用到。
    最后的三个阶段处理响应和日志:

    PRECONTENT

    PRECONTENT:在处理 CONTENT 之前会做一些事情,例如会把子请求发送给第三方的服务去处理,try_files 模块也是在这个阶段中。

    CONTENT

    CONTENT:这个阶段涉及到的模块就非常多了,例如 index, autoindex, concat 等都是在这个阶段生效的。

    LOG

    LOG:记录日志 access_log 模块

    Nginx是一个处理流程

    网络上有人做了流程图片,可以很清晰看出处理流程


    image.png
    image.png
    image.png

    Post_read阶段

    Realip模块

    realip 模块是在 post_read 阶段生效的,它的作用是:当本机的 nginx 处于一个反向代理的后端时获取到真实的用户 ip。 如果没有 realip 模块,Nginx 中的$remote_addr 可能就不是客户端的真实 ip 了,而是代理主机的 ip。realip 模块的配置实例如下,通过这样的手段,最后拿到用户的真实 ip。

    set_real_ip_from  192.168.1.0/24;
    set_real_ip_from  192.168.2.1;
    set_real_ip_from  2001:0db8::/32;
    real_ip_header    X-Forwarded-For;
    # real_ip_recursive off;
    real_ip_recursive on;
    

    set_real_ip_from 是指定我们信任的后端代理服务器
    real_ip_header 是告诉 nginx 真正的用户
    real_ip_recursive 设置为 off 时,nginx 会把 real_ip_header 指定的Http 头中的最后一个 ip 当成真实 ip;
    real_ip_recursive 为 on 时,nginx 会把 real_ip_header 指定的 Http头中的最后一个不是信任服务器的 ip (前面设置的 set_real_ip_from)当成真实 ip

    SERVER_REWRITE 和 REWRITE 阶段

    rewrite 模块

    rewrite 模块可以看到它在 SERVER_REWRITE 和 REWRITE 阶段都有介入。rewrite 模块的主要功能是改写请求的 uri。它是 Nginx 默认安装的模块。rewrite模块会根据正则匹配重写 uri,然后发起内部跳转再匹配 location, 或者直接做30x 重定向返回客户端。rewrite 模块的指令有 break, if, return, rewrite, set 等,都是我们常用到的

    return指令

    Syntax :    return code [text];
    # return code URL;
    # return URL;
    Default: -
    Context : server , location ,if
    

    return 指令返回后,Http 请求将在 return 的阶段终止,后续阶段将无法
    进行,所以许多模块得不到执行。

    return 200  “hello”
    

    rewrite指令

    通过ngx_http_rewrite_module模块,实现Url的重写和转发
    地址重写与地址转发是两个不同的概念。

    地址重写 是为了实现地址的标准化,比如我们可以在地址栏中中输入 www.baidu.com. 我们也可以输入 www.baidu.cn. 最后都会被重写到 www.baidu.com 上。浏览器的地址栏也会显示www.baidu.com。
    地址转发:它是指在网络数据传输过程中数据分组到达路由器或桥接器后,该设备通过检查分组地址并将数据转发到最近的局域网的过程。
    

    因此地址重写和地址转发有以下不同点:

    1. 地址重写会改变浏览器中的地址,使之变成重写成浏览器最新的地址。而地址转发他是不会改变浏览器的地址的。
    2. 地址重写会产生两次请求,而地址转发只会有一次请求。
    3. 地址转发一般发生在同一站点项目内部,而地址重写且不受限制。
    4. 地址转发的速度比地址重定向快。
    

    该指令是通过正则表达式的使用来改变URI。可以同时存在一个或多个指令。需要按照顺序依次对URL进行匹配和处理。
    该指令可以在server块或location块中配置,其基本语法结构如下:

    Syntax : rewrite regex replacement [flag];
    Default : --
    Context : server , location , if
    

    rewrite的含义:该指令是实现URL重写的指令
    regex的含义:用于匹配URI的正则表达式。
    replacement:将regex正则匹配到的内容替换成 replacement。
    flag: flag标记, 替换之后的url根据flag指定方式进行处理
    flag有如下值:

    last: 本条规则匹配完成后,继续向下匹配新的location URI 规则。(不常用)
    break: 本条规则匹配完成即终止,不再匹配后面的任何规则(不常用)。
    redirect: 返回302临时重定向,浏览器地址会显示跳转新的URL地址。
    permanent: 返回301永久重定向。浏览器地址会显示跳转新的URL地址。
    例如 :rewrite ^/(.*) http://www.baidu.com/$1 permanent;
    说明:
    rewrite 为固定关键字,表示开始进行rewrite匹配规则。
    regex 为 ^/(.*)。 这是一个正则表达式,匹配完整的域名和后面的路径地址。
    replacement就是 http://www.baidu.com/1这块了,其中1是取regex部分()里面的内容。如果匹配成功后跳转到的URL。
    flag 就是 permanent,代表永久重定向的含义,即跳转到 http://www.baidu.com/$1 地址上。
    

    测试

    在测试机上添加8088端口,跳转到www.baidu.com

        server {
          listen 8088;
          server_name  localhost;
          location / {
            proxy_pass http://127.0.0.1:3001;
            rewrite ^/(.*) http://www.baidu.com permanent;
          }
        }
    

    测试浏览器成功跳转到百度表示成功。

    if指令

    if指令简介

    Nginx 的 if 指令是属于 rewrite 模块的,所以对于 if 来讲,会和其他的 rewrite 模块执行全部执行完之后再进行下一阶段。如果 if 指令的结果是 match 的,那么 if 会创建一个内嵌的 location 块,只有这里面的 content 处理指令(NGX_HTTP_CONTENT_PHASE 阶段)会执行。
    if 指令用来判断条件为 true 时要执行的指令,条件 false 时不执行相应的指令,if 指令只能用在 server、location内。

    Syntax: if (condition) {…}
    Default: --
    Context: server , location
    

    if指令的条件表达式

        检查变量是否为空 或者 为0
        将变量与字符串做匹配, 使用 = 或者 !=
                ~ 或者 !~ 大小写敏感
                ~* 或者 !~* 大小写不敏感
        检查文件是否存在 -f  或者 !-f
        检查目录是否存在-d  或者 !-d
        检查文件 、 目录、 软连接是否存在-e   !-e
        是否为可执行文件 -x  或者 !-x
    

    if 指令不支持多条件、不支持嵌套、不支持 else,与常见的条件表达式不同的是if指令等值比较使用单尽量别在 location 中使用 if,在 server 中使用 if 相对会安全些。个等号=而不是双等号 ==
    如果要在 location 中使用 if,最好用如下模版格式:

    location / {
        error_page 418 = @other;
        recursive_error_pages on;
        if ($something) {
            return 418;
        }
        # some configuration
        ...
    }
    location @other {
        # some other configuration
        ...
    }
    

    Nginx if比较

    正则匹配

    正则 -- 描述
    ==  等值比较
    ~   与指定正则表达式模式匹配时返回“真”,判断匹配与否时区分字符大小写
    ~*  与指定正则表达式模式匹配时返回“真”,判断匹配与否时不区分字符大小写
    !~  与指定正则表达式模式不匹配时返回“真”,判断匹配与否时区分字符大小写
    !~* 与指定正则表达式模式不匹配时返回“真”,判断匹配与否时不区分字符大小写
    

    文件及目录匹配

    正则 -- 描述
     -f , !-f   --    判断指定的路径是否为存在且为文件
    -d , !-d    --    判断指定的路径是否为存在且目录
    -e , !-e    --    判断指定路径是否存在,文件或者目录均可
    -x , !-x    --    判断指定路径的文件是否存在且可执行
    

    Nginx if关键字

    break
    遇到break就跳出,后面指令不在执行,例如

    if (!-f $reque_filename){
        set $id = 1; #有效的指令
        break;
        limit_rate 10k; #无效的指令
    }
    

    return
    完成对请求的处理。直接向客户端返回响应状态码。比如:

    return code URL ;
    return [text];
    

    说明:
    code : 返回给客户端的HTTP状态码
    URL : 返回给客户端URL地址
    text : 返回给客户端的响应体内容,支持变量

    set
    设置变量, 语法如下

    set $id = 2
    

    FIND_CONFIG 阶段

    location匹配

    location匹配是在FIND_CONFIG阶段进行的,我们需要掌握location的匹配规则和匹配顺序

    location匹配规则

    规则  -- 匹配
    =   精准匹配,如果有请求匹配这个location,那么将停止搜索并立即处理此请求
    ~   区分大小写匹配 (可用正则表达式)
    ~*  不区分大小写匹配(可用正则表达式)
    !~  区分大小写不匹配
    !~* 不区分大小写不匹配
    ^~  前缀匹配
    @   “@”定义一个命名的location,使用在内部定向时
    /   通用匹配,任何请求都会匹配到
    

    location匹配顺序

    • “=” 精准匹配,如果匹配成功,则停止其他匹配
    • 普通字符串指令匹配,优先级是从长到短(匹配字符越多,则选择该匹配结果)。匹配成功的 location 如果使用^~,则停止其他匹配(正则匹配)
    • 正则表达式指令匹配,按照配置文件里的顺序(从上到下),成功就停止其他匹配
    • 如果正则匹配成功,使用该结果;否则使用普通字符串匹配结果
    

    简单总结如下:

    Location=  》  location完整路径  》  location ^~路径  》  location .*正则顺序  》  正则匹配  》  最长字符串匹配 , 不完全匹配  》  location通配
    

    即:

    精准匹配 》 最长字符串匹配,但完全匹配 》 非正则匹配 》 正则匹配 》 最长字符串匹配,不完全匹配 》 location通配
    
    image.png

    实验

    realip模块使用

    ps : set_real_ip_from 只在1.2.1以上可以使用,其他版本可能报错
    realip 模块默认没有被编译进 Nginx 的,我们需要在源码编译阶段使用–with-http_realip_module,将 realip 模块编译进来后方可使用。接下来,我们做个简单测试,首先准备一个 server 块如下:

        server {
            listen 8007;
            server_name  localhost;
            set_real_ip_from 192.168.232.13;
            real_ip_recursive off;
            # real_ip_recurslive on;
            real_ip_header X-Forwarded-For;
            location /{
                return 200 "client real ip: $remote_addr \n";
            }
        }
    

    其他设备执行curl 获取得到执行curl设备的ip信息。

    curl  -H "X-Forwarded-For: 1.1.1.1,192.168.232.13" http://192.168.232.13:8007
    

    宿主机上执行结果 ,实际上是端口没有其他内容,直接执行curl ip:端口 也是会返回curl执行设备的ip


    image.png
    image.png

    这里返回的是头部参数 X-Forwarded-For 中最后一个 ip,如果将real_ip_recursive 设置为 on,此时,由于 set_real_ip_from 中设置192.168.232.13 为信任的方向代理 ip,那么 Nginx 会往前找一位,认为1.1.1.1 是用户的真实 ip

    nginx -s reload
    curl  -H "X-Forwarded-For: 1.1.1.1,192.168.232.13" http://192.168.232.13:8007
    client real ip: 1.1.1.1
    

    return指令 和 if指令联合使用

    写如下简单配置到nginx

        server {
            server_name return_and_if.test.com;
            listen 8008;
            root html;
            # 404 错误跳转到403.html 页面,根据路径由root指令指定
            error_page 404  /403.html;
            # return 405 '405 Not Allowed!\n';
            location / {
                if ( $request_method = POST ) {
                    return 200 "Post Request!\n";
                }
            }
        }
    

    重启执行

    [root@centos13 conf]# curl  -XPOST http://192.168.232.13:8008
    Post Request!
    

    先测试 if 指令,当请求方法为 POST 时,我们能得到 ‘post request!’ 这样的字符串输出。GET 请求时候,针对 404 情况,会跳转到/403.html,我们准备一个 403.html 页面,里面写上’403, forbidden!’ 这一行内容,开始下面的 Http请求

    [root@localhost ~]#  curl  -XPOST http://192.168.232.13:8008
    Post Request!
    [root@localhost ~]#  curl  http://192.168.232.13:8008
    403,forbidden!
    

    如果我们打开 return 405 这行指令,则 error_page 将不会生效,连同后面的 location 匹配也不会生效。无论我们发送如何请求,都会返回 405 的错误信息。这是因为 server 中的 return 指令是在 SERVER_REWRITE 中执行的,而location 匹配则是在下一个阶段 FIND_CONFIG 中执行的,所以上一个阶段在return 后,根本不会进入后面的阶段执行

    [root@centos13 conf]# curl http://192.168.232.13:8008
    405 Not Allowed!
    

    rwrite模块使用

    准备环境,首先是新建一个目录 third(全路径为/root/test/third),再该目录下新建一个文件 3.txt, 里面只有一行内容 ‘hello, world’。接下来,我们准备一个 server 块,加到 Http 指令块中

        server {
            listen 8009;
            server_name  rewrite.test.com;
            # 打开rewrite日志 ,可以看到对应的rewrite结果
                    rewrite_log on;
                    error_log logs/rewrite_error.log notice;
    
                    root /root/test/;
    
                    location /first {
                            rewrite /first/(.*) /second/$1 last;
                            return 200 'first!';
                    }
    
                    location /second {
                            rewrite /second/(.*) /third/$1 break;
                            # rewrite /second/(.*)/third/$1;
                            return 200 'second!';
                    }
    
                    location /third {
                            return 200 'third!';
                    }
            }
    

    执行测试 ,当我们在 /second 配置中,使用 break 时,请求命令

    [root@centos13 conf]# curl 192.168.232.13:8009/first
    first!
    [root@centos13 conf]# curl 192.168.232.13:8009/second
    second!
    [root@centos13 conf]# curl 192.168.232.13:8009/third
    third!
    [root@centos13 conf]# curl 192.168.232.13:8009/first/3.txt
    hello,world
    

    使用192.168.232.14执行curl 192.168.232.13:8009/first/3.txt,然后查看rewrite日志可以看到跳转流程:首先是 /first/3.txt 请求在 /first 中匹配,并被替换为 /second/3.txt, last标识表示将继续进行匹配,在 /second 中,uri 又被 rewrite 成 /third/3.txt, 如果后面跟了 break 标识,表示 rewrite 到此结束,不会执行后面的 return 指令,直接请求静态资源 /third/3.txt,得到其内容’hello, world’;如果是没有 break 标识,则会在执行 return 指令后直接返回,并不会继续执行下去,最后返回’second!' 字符串。

    2022/11/07 05:39:28 [notice] 23173#0: *110 "/first/(.*)" matches "/first/3.txt", client: 192.168.232.14, server: rewrite.test.com, request: "GET /first/3.txt HTTP/1.1", host: "192.168.232.13:8009"
    2022/11/07 05:39:28 [notice] 23173#0: *110 rewritten data: "/second/3.txt", args: "", client: 192.168.232.14, server: rewrite.test.com, request: "GET /first/3.txt HTTP/1.1", host: "192.168.232.13:8009"
    2022/11/07 05:39:28 [notice] 23173#0: *110 "/second/(.*)" matches "/second/3.txt", client: 192.168.232.14, server: rewrite.test.com, request: "GET /first/3.txt HTTP/1.1", host: "192.168.232.13:8009"
    2022/11/07 05:39:28 [notice] 23173#0: *110 rewritten data: "/third/3.txt", args: "", client: 192.168.232.14, server: rewrite.test.com, request: "GET /first/3.txt HTTP/1.1", host: "192.168.232.13:8009"
    

    如果是不使用 break 标识,则请求

    [root@centos13 conf]# curl 192.168.232.13:8009/first/3.txt
    second!
    

    location匹配

    nginx配置文件新增内容,测试location匹配规则

        server {
            server_name location.test.com;
            listen 8010;
            
            location  = / {
                return 200 "精准匹配";
            }
            
            location ~* /ma.*ch{
                return  200 "正则匹配/ma.*ch";
            }
            
            location  ~ /mt.*ch {
                return 200 "正则匹配/mt.*ch";
            }
            
            location = /test {
                return 200 "精准匹配/test";
            }
            
            location  ^~ /test/ {
                return 200 "前缀匹配/test";
            }
            
            location ~ /test/he*o {
                return  200 "正则匹配/test/he*o";
            }
            
            location  / {
                return 200 "通配/,上面没有匹配规则就会匹配这一条";
            }
        }
    

    实验结果如下
    = / 精准匹配

    [root@localhost ~]#  curl 192.168.232.13:8010
    精准匹配
    

    ~* /ma.ch 正则匹配/ma.ch , ~* 不区分大小写

    [root@localhost ~]# curl 192.168.232.13:8010/MA.xch
    正则匹配/ma.*ch
    [root@localhost ~]# curl 192.168.232.13:8010/ma.xch
    正则匹配/ma.*ch
    

    ~ /mt.ch 正则匹配/mt.ch , ~ 区分大小写

    [root@localhost ~]# curl 192.168.232.13:8010/mt.xch
    正则匹配/mt.*ch
    [root@localhost ~]# curl 192.168.232.13:8010/MT.xch
    通配/,上面没有匹配规则就会匹配这一条                       #匹配到了最后一条
    

    = /test 精准匹配/teest

    [root@localhost ~]# curl 192.168.232.13:8010/test
    精准匹配/test
    

    ^~ /test/ 前缀匹配/test

    [root@localhost ~]# curl 192.168.232.13:8010/test/ceshi
    前缀匹配/test
    

    ~ /test/he*o 正则匹配,因为前缀匹配优先级高于正则匹配, 所以返回前缀匹配到的内容。

    [root@localhost ~]# curl 192.168.232.13:8010/test/hero
    前缀匹配/test
    

    / 通配,上面都没有匹配到,就会匹配到这一条。

    [root@localhost ~]# curl 192.168.232.13:8010/abcd
    通配/,上面没有匹配规则就会匹配这一条
    

    精准匹配优先级最高

    [root@localhost ~]# curl 192.168.232.13:8010
    精准匹配
    [root@localhost ~]# curl 192.168.232.13:8010/test
    精准匹配/test
    

    前缀匹配优先级高于正则匹配

    [root@localhost ~]# curl 192.168.232.13:8010/test/abc
    前缀匹配/test
    [root@localhost ~]# curl 192.168.232.13:8010/test/hexo
    前缀匹配/test
    

    正则匹配,按照先后顺序匹配,如果同时匹配两个正则,前面的优先匹配

    [root@localhost ~]# curl 192.168.232.13:8010/matxxxch
    正则匹配/ma.*ch
    

    什么都没有匹配到,最后匹配通配/

    [root@localhost ~]# curl 192.168.232.13:8010/abcd
    通配/,上面没有匹配规则就会匹配这一条
    

    preaccess阶段

    在preaccess阶段在access阶段之前,主要是限制用户的请求,比如并发链接数量(limit_conn模块) 和 每秒请求数量(limit_req模块)等。 这两个模块对于预防一些攻击请求是很有效的。

    limit_conn模块

    ngx_http_limit_conn_module 模块限制单个 ip 的建立连接的个数,该模块内有6 个指令。分别如下:
    • limit_conn_zone: 该指令主要的作用就是分配共享内存。下面的指令格式中 key 定义键,这个 key 往往取客户端的真实 ip,zone=name 定义区域名称,后面的limit_conn 指令会用到的。size 定义各个键共享内存空间大小

    Syntax: limit_conn_zone key zone=name:size;
    Default:    —
    Context:    http
    

    limit_conn_status: 对于连接拒绝的请求,返回设置的状态码,默认是 503;

    Syntax: limit_conn_status code;
    Default:    limit_conn_status 503;
    Context:    http, server, location
    

    limit_conn: 该指令实际限制请求的并发连接数。指令指定每个给定键值的最大同时连接数,当超过这个数字时被返回 503 (默认,可以由指令limit_conn_status 设置)错误

    Syntax: limit_conn zone number;
    Default:    —
    Context:    http, server, location
    

    limit_conn_log_level: 当达到最大限制连接数后,记录日志的等级;

    Syntax: limit_conn_log_level info | notice | warn | error;
    Default:    
    limit_conn_log_level error;
    Context:    http, server, location
    This directive appeared in version 0.8.18.
    

    limit_conn_dry_run: 这个指令是 1.17.6 版本中才出现的,用于设置演习模式。在这个模式中,连接数不受限制。但是在共享内存的区域中,过多的连接数也会照常处理

    Syntax: limit_conn_dry_run on | off;
    Default:    
    limit_conn_dry_run off;
    Context:    http, server, location
    This directive appeared in version 1.17.6.
    

    limit_zone 该指令在 1.1.8 版中已过时,并在 1.7.6 版中被删除。应改用具有更改语法的等效 limit_conn_zone 指令
    实例

    http {
        ...
        limit_conn_zone $binary_remote_addr zone=addr:10m
        
        server {
            ...
            location / {
                limit_conn_status 500;
                limit_conn_log_lwvel warn
                # 限制向用户返回的速度,每秒50个字节
                limit_rate 50;
                limit_conn addr 10;
            }
        }
    }
    

    limit_req模块

    ngx_http_limit_req_module 模块主要用于处理突发流量,它基于漏斗算法将突发的流量限定为恒定的流量。如果请求容量没有超出设定的极限,后续的突发请求的响应会变慢,而对于超过容量的请求,则会立即返回 503(默认)错误该模块模块中比较重要的指令有: •
    limit_req_zone 指令,定义共享内存, key 关键字以及限制速率

    Syntax: limit_req_zone key zone=name:size rate=rate [sync];
    Default:    —
    Context:    http
    

    Limit_req指令,限制并发连接数

    Syntax: limit_req zone=name [burst=number] [nodelay | delay=number];
    Default:    —
    Context:    http, server, location
    

    limit_req_log_level指令,设置服务拒绝请求发生时打印的日志级别

    Syntax: limit_req_log_level info | notice | warn | error;
    Default:    
    limit_req_log_level error;
    Context:    http, server, location
    This directive appeared in version 0.8.18.
    

    limit_req_status 指令,设置服务拒绝请求发生时返回状态码

    Syntax: limit_req_status code;
    Default:    
    limit_req_status 503;
    Context:    http, server, location
    This directive appeared in version 1.3.15.
    

    access阶段

    限制某些iP地址的访问权限
    在access阶段,我们可以通过allow 和 deny 指令来允许和拒绝某些ip的访问权限,指令的用法如下

    允许
    Syntax: allow address | CIDR | unix: | all;
    Default:    —
    Context:    http, server, location, limit_except
    拒绝
    Syntax: deny address | CIDR | unix: | all;
    Default:    —
    Context:    http, server, location, limit_except
    

    示例

    location / {
        deny  192.168.1.1;
        allow 192.168.1.0/24;
        allow 10.1.1.0/16;
        allow 2001:0db8::/32;
        deny  all;
    }
    

    auth_basic 模块

    auth_basic 模块是基于 HTTP Basic Authentication 协议进行用户名和密码的认证,它默认是编译进 Nginx 中的,可以在源码编译阶段通过--without-http_auth_basic_module 禁用该模块。它的用法如下
    是否启用使用“HTTP 基本身份验证”协议验证用户名和密码 # 默认是关闭

    Syntax: auth_basic string | off;     # 默认是关闭
    Default:    auth_basic off;
    Context:    http, server, location, limit_except
    

    指定保存用户名和密码的文件

    Syntax: auth_basic_user_file file;
    Default:    —
    Context:    http, server, location, limit_except
    

    用户名与密码文件格式,使用:冒号隔开

    # comment
    name1:password1
    name2:password2:comment
    name3:password3
    

    接下来,我们只需要配置好 auth_basic 指令,即可对相应的 http 请求做好认证
    在 centos 系统上,想要生成这样的密码文件,我们可以使用 httpd-tools 工具完成,直接使用 yum install 安装即

    [root@localhost data]# yum install httpd-tools
    [root@localhost data]# htpasswd  -bcp user_file user2 passwd2    # 密码不加密
    Warning: storing passwords as plain text might just not work on this platform.
    Adding password for user user2
    [root@localhost data]# cat user_file 
    user2:passwd2
    

    第三方访问控制

    第三方的访问控制是用到了 auth_request 模块,该模块的功能是向上游服务转发请求,如果上游服务返回的相应码是 2xx,则通过认证,继续向后执行;若返回的 401 或者 403,则将响应返回给客户端。
    auth_request 模块默认是未编译进 Nginx 中的,因此我们需要使用--with-http_auth_reques_module将该模块编译进 Nginx 中,然后我们才能使用该模块。

    Syntax: auth_request uri | off;
    Default:    
    auth_request off; Context:  http, server, location
    
    Syntax: auth_request_set $variable value;
    Default:    —
    Context:    http, server, location
    

    示例

    location /private/ {
        auth_request /auth;
        ...
    }
    
    location = /auth {
        proxy_pass ...
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
    }
    

    实验

    limit_conn 模块实验

    本次案例将使用 limit_conn 模块中的指令完成限速功能实验。
    Nginx默认配置在加上如下内容

    ...
        http {
            ...
            # 没有这个会报错,前提是需要自定义limit_conn_zone 共享内存
            limit_conn_zone $binary_remote_addr zone=addr:10m
            ...
            server {
                server_name localhost;
                listen 8011 ;
                
                location / {
                    limit_rate 50;
                    limit_conn addr 1 ;
                }
            }
            ...
        }
    ...
    

    使用谷歌浏览器无痕模式快速刷新iP:8011端口
    使用 limit_rate 指令用于限制 Nginx 相应速度,每秒返回 50 个字节,然后是限制并发数为 1,这样方便展示效果。当我们打开一个浏览器请求该端口下的根路径时,由于相应会比较慢,迅速打开另一个窗口请求同样的地址,会发现再次请求时,正好达到了同时并发数为 2,启动限制功能,第二个窗口返回 错误提示页面 (老版本可能返回503 , nginx新版本默认是返回…/html/50.html , 这就是“Nginx错误页面优雅显示”,把错误页面匹配到另一优雅的提示页面)。
    第一次刷新可以正常进入页面,但是速度很慢,因为限制返回字节为50 。


    image.png

    第二次刷新,返回50x错误优雅提示页面,现在多是自定义的有趣页面


    image.png

    Access模块实验

    配置如下。在 /root/test/web 下有 web1.html 和
    web2.html 两个静态文件。访问/web1.html 时,使用 allow all 指令将所有来源
    的 ip 请求全部放过(当然也可以不写);使用 deny all 会拒绝所有,所以访问
    /web2.htm l 时,会出现 403 的报错页面
    配置文件

    …
        server {
            listen       8011;
            server_name  localhost;
            root /root/test/web;
            location /web1.html {
            default_type text/html;
            allow all;
            }
    …
    

    Web文件

    [root@centos13 conf]# cat /root/test/web/web1.html 
    WEB  1 
    WEB  1
    [root@centos13 conf]# cat /root/test/web/web2.html 
    web 2 
    web 2
    

    浏览器访问web1.html 和 web2.html


    image.png
    image.png

    return 指令 ,重新加载 Nginx 后,可以看到无论是访问 /web1.html 还是 /web2.html,我们
    都可以看到想要的 return 指令中的结果。这是因为 return 指令所在的 rewrite模块先于 access 模块执行,所以不会执行到 allow 和 deny 指令就直接返回了。但是对于访问静态页面资源,则是在 content 阶段执行的,所以会在经过 allow和 deny 指令处理后才获取静态资源页面的内容,并返回给用户。
    修改配置文件

        server {
            listen       8011;
            server_name  localhost;
            root /root/test/web;
            location /web1.html {
            default_type text/html;
            allow all;
            return 200 "return web 1";
            }
            
            location /web2.html {
            default_type text/html;
            deny all;
            return 200 "return web 2";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    

    分别访问web1 和 web2


    image.png
    image.png

    auth_basic模块实验

    修改nginx配置

        server {
            listen       8012;
            server_name  localhost;
            server_name auth_basic.test.com;
            location / {
                auth_basic  "my site";
                auth_basic_user_file /data/user.pass;
                root   html;
                index  index.html index.htm;
            }
    }
    

    设置user.pass文件

    [root@centos13 data]# htpasswd  -bc /data/user.pass  testautn auth@basic
    Adding password for user testautn
    [root@centos13 data]# cat user.pass 
    testautn:$apr1$z2UkPIxO$xtDeeRvW5CCthp5.7YfXC1
    

    浏览器访问会提示登录


    image.png
    image.png

    try_files 阶段

    该阶段为生成内容的前一阶段,主要是用于处理 try_files 指令的配置,如果没有配置 try_files 指令,这个阶段会跳过,该阶段不支持 nginx 模块注册处理程序
    try_files 指令接受两个以上任意数量的参数,每个参数都指定一个 uri,比如设置 N 个参数,nginx 会在 precontent 阶段依次把前 N-1 个参数映射为文件系统上的文件或目录,逐个检查这些文件或目录是否存在。一旦 Nginx 匹配到某个文件或目录存在,就会在 precontent 阶段,把当前请求的 uri 改写为该对象所对应的参数 uri,如果前 N-1 个参数所对应的文件或目录都不存在,则 precontent 阶段会发起内部跳转,按照最后一个参数所指定的 uri 进行 find-config 阶段
    Syntax: try_files file ... uri;
    try_files file ... =code;
    Default: —Context: server, location

    Content阶段

    该阶段是所有阶段中最重要的一个阶段,该阶段负责内容生成,并输出 http 响应。通过 nginx 配置文件中的配置指令,生成响应内容,返回给客户端,这个阶段的配置指令例如 echo、proxy_pass 和 root 和 alias 这两个常用 的指令。
    Alias: http://nginx.org/en/docs/http/ngx_http_core_module.html#alias

    Syntax: alias path;
    Default:    —
    Context:    location
    

    实例

    location /i/ {
        alias /data/w3/images/;
    }
    

    Root : http://nginx.org/en/docs/http/ngx_http_core_module.html#root

    Syntax: root path;
    Default:    
    root html;Context:  http, server, location, if in location
    

    实例

    location /i/ {
        root /data/w3;
    }
    

    单从指令用法上就可以看到不少区别,首先是 alias 指令没有默认值,而且该指令只能在 location 中使用。而 root 可以存在与 http、server、location 等多个指令块中,还可以出现在 if 指令中。另外,最最主要的不同是两个指令会以不同的方式将请求映射到服务器文件上。root 指令会用[root路径 + location 路径]的规则映射静态资源请求,而 alias 会使用 alias 的路径替换 location 路径。此外 alias 后面必须要用“/”结束,否则会找不到文件的,而 root 则可有可无。来看下面一个例子:
    添加以下location

            location ^~ /web {
                root /root/test/;
            }
    
            location ^~ /web2/ {
                alias /root/test/web/;
            }
    

    对于 http 请求: http://ip:端口/web/web1.html 访问的是主机 上全路径为/root/html/test/web1.html 的静态资源;而请求 http://ip:端口/web2/web1.html 访问的是全路径为/root/html/web1.html 的静态资源,/test2/已经被替换掉了

    image.png
    image.png
    nginx是通过alias设置虚拟目录,在nginx的配置中,alias目录和root目录是有区别的:
    1)alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alias目录下查找的;
    2)root指定的目录是location匹配访问的path目录的上一级目录,这个path目录一定要是真实存在root指定目录下的;
    3)使用alias标签的目录块中不能使用rewrite的break(具体原因不明);另外,alias指定的目录后面必须要加上"/"符号!!
    4)alias虚拟目录配置中,location匹配的path目录如果后面不带"/",那么访问的url地址中这个path目录后面加不加"/"不影响访问,访问时它会自动加上"/";
        但是如果location匹配的path目录后面加上"/",那么访问的url地址中这个path目录必须要加上"/",访问时它不会自动加上"/"。如果不加上"/",访问就会失败!
    5)root目录配置中,location匹配的path目录后面带不带"/",都不会影响访问。
    

    在 static 模块中,还提供了 3 个变量供我们使用,分别是: •
    request_filename: 访问静态资源的完整路径
    document_root: 访问静态资源文件所在目录
    realpath_root: 如果 document_root 是软链接,则改变量会将其替换成真正的地址同样是上面的例子,稍做改动

            location /web {
                default_type text/html;
                alias /root/test/web;
                return 200 '$request_filename:$document_root:$realpath_root\n';
            }
    

    执行ip:端口/web/web1.html

    /root/test/web/web1.html:/root/test/web:/root/test/web
    

    在 content 阶段,在 static 模块之前,还会执行的模块有 index 和autoindex 模块。index 模块提供了 index 指令,用于指定/访问时返回 index文件内容。
    autoindex 模块会根据配置决定是否以列表的形式展示目录下的内容

    Syntax: index file ...;
    Default:    index index.html;
    Context:    http, server, location
    

    示例:

    1. 访问 url=/ 时 ,返回静态资源index.html文件 中的内容
    Location / {
        Index index.html;
    }
    
    1. 是否开起目录显示,默认nginx是不会显示目录下的所有文件
    Syntax: autoindex on | off;
    Default:    autoindex off;
    Context:    http, server, location
    
    1. 显示出文件的实际大小
    Syntax: autoindex_exact_size on | off;
    Default:    autoindex_exact_size on;
    Context:    http, server, location
    
    1. 显示格式, 默认是html形式显示
    Syntax: autoindex_format html | xml | json | jsonp;
    Default:    autoindex_format html;
    Context:    http, server, location
    This directive appeared in version 1.7.9.
    
    1. 显示时间,设置为on后,按照服务器的时钟为准
    Syntax: autoindex_localtime on | off;
    Default:    autoindex_localtime off;
    Context:    http, server, location
    

    Log阶段

    log 阶段是 http 请求 11 个阶段中的最后一个阶段,这个阶段主要的任务就是记录请求的访问日志。这个阶段主要涉及的是 ngx_http_log_module 这个模块。该模块提供了几个常用指令,如 access_log 和 log_format 指令,分别定义了请求日志的记录文件以及记录的日志格式。
    官方示例:

    log_format compression '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $bytes_sent '
                           '"$http_referer" "$http_user_agent" "$gzip_ratio"';
    
    access_log /spool/logs/nginx-access.log compression buffer=32k;
    
    #  nginx默认配置: 
    [root@centos13 conf]# nginx  -v
    nginx version: nginx/1.22.1     # 当前nginx版本
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
        #access_log  logs/access.log  main;
    
    #  Access_log指令用法
    Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
    access_log off;
    Default:    access_log logs/access.log combined;
    Context:    http, server, location, if in location, limit_except
    
    #  Log_format指令
    Syntax: log_format name [escape=default|json|none] string ...;
    Default:    log_format combined "...";
    Context:    http
    
    #  是否打开日志缓存
    Syntax: open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
    open_log_file_cache off;
    Default:    open_log_file_cache off;
    Context:    http, server, location
    

    测试

    try_files模块示例

    在测试机器的 /root/test/web 目录下有 2 个 html 文件,分别为 web.html 和web2.html, 没有 web3.html。我们编写如下的 server 块,监听 8013 端口。首先访问 http://主机 ip:8013/web 时,根据配置情况,Nginx 首先查找是否存在/root/test/web/web3.html 文件,没有找到会继续向下,找$uri,也就是/root/test/web 文件,不存在。继续找 KaTeX parse error: Expected 'EOF', got ',' atposition 15: uri/index.html̲即/root/test/web…uri/web1.html 时文件存在,故返回/root/test/web/web1.html 文件内容。如果该文件还不存在,则还会继续匹配@lasturi,最后返回’lasturi!'这样的字符串。而在访问 http://主机ip:8013/return_code 时,由于无法匹配静态资源,根据配置最后返回 404 错误码,出现 Nginx 默认的 404 错误页面。

    user root;
    
    worker_processes 1;
    
    events {
    
     worker_connections 1024;
    
    }
    
    http {
    
     include mime.types;
    
     default_type application/octet-stream;
    
     sendfile on;
    
     keepalive_timeout 65;
    
    server {
    
     server_name localhost;
    
     listen 8013;
    
     root /root/test/;
    
     default_type text/plain;
    
     location /web {
    
     # 找/root/test/index.html
    
     # try_files /index.html
    
     try_files /web/web3.html
    
     $uri $uri/index.html $uri/web1.html
    
     @lasturi; # 最后匹配这个
    
     }
    
     location @lasturi {
    
     return 200 'lasturi!\n';
    
     }
    
     location /return_code {
    
     try_files $uri $uri/index.html $uri.html =404;
    
     }
    
     }
    
    }
    

    192.168.232.13:8013/web


    image.png

    http://192.168.232.13:8013/return_code

    image.png

    access_log指令用法

    我们只需要在http指令块中配置log_format指令 和 access_log指令即可,配置如下:

    ...
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  logs/access.log  main;
        
        server {
            listen 8000;
            server_name  localhost;
            return 200 '8000端口 server'
        }
    ...
    

    log_format 指令是指定打印日志的格式,access_log 指令指定日志输出的路径
    以及指定使用前面定义的日志格式。在配置好日志相关的指令后,重启 Nginx,
    并发送一个 Http 请求,就可以在对应的路径上看到相关的日志信息了。

    [root@centos13 conf]# curl 192.168.232.13:8000
    8000端口 server
    [root@centos13 conf]# curl -l -H "X-Forwarded-For:1.1.1.1" http://192.168.232.13:8000
    8000端口 server
    [root@centos13 conf]# tail  -2 ../logs/access.log 
    192.168.232.13 - - [10/Nov/2022:17:19:39 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "-"
    192.168.232.13 - - [10/Nov/2022:17:21:24 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.29.0" "1.1.1.1"
    

    相关文章

      网友评论

          本文标题:第九章 Nginx的http模块介绍

          本文链接:https://www.haomeiwen.com/subject/ietgkdtx.html