在8.3节中曾提到过动态添加黑白名单地方案,下面对该内容做一个补充,代码如下:

lua_shared_dict white_list_ip 1m;   #声明存放白名单的共享内存
server {
    listen       80;
    server_name  testnginx.com;
    location / {
        access_by_lua_block {
            local ngx = require "ngx";
            local white_list_ip = ngx.shared.white_list_ip
            --获取请求者的IP地址,如果Nginx的前端还有其他代理或CDN,需配置real_ip来获取真实的用户IP地址
            local ip  = ngx.var.remote_addr
            --在共享内存中查找请求的IP地址是否存在
            local value, flags = white_list_ip:get(ip)
            –如果查到的IP地址在白名单中,就退出access_by_lua_block,继续执行下一个阶段
            if value then
                ngx.exit(ngx.OK)
            else
                ngx.exit(ngx.HTTP_FORBIDDEN)–如果不在白名单中,就返回403错误
            end
        }
        echo 'ok';
    }

  --提供HTTP接口,可以修改共享内存中的数据
     location = /white_list_ip_op {
         default_type 'text/plain';
         content_by_lua_block {
              local ngx = require "ngx"
              local white_list_ip = ngx.shared.white_list_ip
              local op = ngx.var.arg_op
              local ip = ngx.var.arg_ip
              --增加白名单
              if op == 'add' then
                  white_list_ip:set(ip,'1')
              --删除白名单
              elseif op == 'del' then
                  white_list_ip:delete(ip)
              end
              --执行完操作后,读取当前白名单的配置
              local ds = white_list_ip:get_keys()
              if type(ds) == 'table' then
                  for _, k in ipairs(ds) do
                      ngx.say("白名单ip :  ",k)
                  end
              end
            }
      }

}

执行操作,添加1条白名单:
# curl 'http://testnginx.com/white_list_ip_op?op=add&ip=10.19.64.210'
白名单ip : 10.19.64.210
使用白名单中的IP地址 10.19.64.210访问testnginx.com,返回结果如下:

#  curl -i http://testnginx.com/
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 29 May 2018 09:43:30 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive

ok

删除白名单:

#  curl -i 'http://testnginx.com/white_list_ip_op?op=del&ip=10.19.64.210'
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 29 May 2018 09:44:11 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive

使用白名单中的IP地址 10.19.64.210再次访问testnginx.com,会返回异常,并提示禁止访问,如下所示:

#  curl -i http://testnginx.com/
HTTP/1.1 403 Forbidden
Server: nginx/1.12.2
Date: Tue, 29 May 2018 09:45:21 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>

此方案存在一个隐患,即当Nginx的master进程重启后,共享内存中的数据会丢失,会导致黑白名单的防御失效。为了防止数据丢失,下面提供两种常见的解决方案。
A方案,将IP地址存放在MySQL上,再提供1个读/写MySQL的Lua函数,每次新增或删除共享内存中的数据时,都将修改后的数据存放到MySQL中。当Nginx的master进程重启后,在init_worker_by_lua阶段将数据从MySQL中读取出来插入共享内存即可。(本方案的使用方式在后续章节中会有示例。)
B方案,每次操作共享内存后,都将共享内存中的数据写入硬盘文件。当Nginx的master进程重启后,在init_by_lua_block阶段读取硬盘中的文件,将文件的IP地址重新插入共享内存即可。