文档:https://github.com/openresty/lua-resty-redis Lua学习:https://www.yuque.com/oyyh/lua

安装lua-resty-redis模块

  1. 将模块从github下载到本地

wget https://github.com/openresty/lua-resty-redis/archive/refs/tags/v0.29.tar.gz
tar -zxvf v0.29.tar.gz
cd lua-resty-redis-0.29
pwd

  1. 修改nginx.conf配置文件,将上面的路径添加到lua_package_path

lua_package_path “/home/data/lua-resty-redis-0.29;;”;

  1. 重启nginx

/usr/local/nginx/nginx -s reload

Nginx配置

  1. http {
  2. ...
  3. # 设置一块共享内存空间,可以被各个worker进程
  4. lua_shared_dict blacklist 5m;
  5. location /lua {
  6. # lua_code_cache off;
  7. access_by_lua_file /home/data/lua_code/test.lua;
  8. default_type 'text/html';
  9. content_by_lua 'ngx.say("hello world")';
  10. }
  11. }

黑名单服务类

下述这个BlackList.lua文件放置的目录,记得配置到nginx.conf中的lua_package_path属性中。
例如:
# 文件位置
/home/data/lua_code/BlackList.lua
# 那么在nginx.conf中,我应该这么配:
lua_package_path “/home/data/lua-resty-redis-0.29;/home/data/lua_code/?.lua;;”;
如果不按照上面这么配置,那么在其他地方require该文件的时候,会找不到这个模块

  1. local redis = require "resty.redis"
  2. local red = redis:new() -- 实例化Redis对象
  3. -- 获取nginx中配置的共享内存的dict对象
  4. local blacklist = ngx.shared.blacklist
  5. -- Redis相关配置
  6. local conf = {
  7. host = "127.0.0.1",
  8. port = 6379,
  9. pwd = "",
  10. }
  11. -- 定义黑名单服务类
  12. local BlackList = {}
  13. -- 初始化对象
  14. function BlackList:new(host, pwd, port)
  15. o = {}
  16. setmetatable(o, self)
  17. self.__index = self
  18. conf.host = host or conf.host
  19. conf.pwd = pwd or conf.pwd
  20. conf.port = port or conf.port
  21. return o
  22. end
  23. -- 连接Redis
  24. function BlackList:connect()
  25. -- 设置连接超时时间 1s
  26. red:set_timeout(1000)
  27. local ok, err = red:connect(conf.host, conf.port)
  28. if not ok then
  29. self:close()
  30. return
  31. end
  32. if conf.pwd ~= "" and conf.pwd ~= nil then
  33. -- 验证redis密码
  34. local res, err = red:auth(conf.pwd)
  35. if not res then
  36. self:close()
  37. return
  38. end
  39. end
  40. return
  41. end
  42. -- 关闭Redis连接
  43. function BlackList:close()
  44. -- 将连接归还连接池,并配置连接池大小
  45. local time = 10000 -- 指定连接在连接池中最大空闲时间,单位毫秒(ms)
  46. local size = 100 -- 连接池大小
  47. local ok, err = red:set_keepalive(time, size)
  48. if not ok then
  49. -- 放入连接池失败,直接关闭连接
  50. redis:close()
  51. end
  52. end
  53. -- redis中获取ip黑名单列表
  54. function BlackList:getList()
  55. -- 连接redis
  56. self:connect()
  57. local resp, err = red:smembers("blacklist")
  58. if not resp then
  59. self:close()
  60. return
  61. end
  62. -- 得到的数据为空处理
  63. if resp == ngx.null then
  64. resp = nil
  65. end
  66. -- 随手关闭redis连接,好习惯,反正在这里关闭连接,也只是将连接回归连接池,下次连接也不需要重新握手
  67. self:close()
  68. return resp
  69. end
  70. -- 刷新黑名单
  71. function BlackList:reflush_blacklist()
  72. -- 获取当前时间
  73. local current_time = ngx.now()
  74. -- 获取nginx共享内存块的更新时间
  75. local last_update_time = blacklist:get("last_update_time")
  76. -- 判断共享内存的更新时间是否超过1分钟,如果超过一分钟则更新共享内存的数据为redis中的最新数据
  77. if last_update_time == nil or last_update_time < (current_time - 60) then
  78. -- redis中获取最新的黑名单列表
  79. local new_blacklist = self:getList()
  80. if not new_blacklist then
  81. return
  82. end
  83. -- 将共享内存中的数据清除
  84. blacklist:flush_all()
  85. -- 循环插入新的黑名单列表到共享内存中
  86. for key, ip in ipairs(new_blacklist) do
  87. blacklist:set(ip, true)
  88. end
  89. -- 更新:共享内存中的更新时间
  90. blacklist:set("last_update_time", current_time)
  91. end
  92. end
  93. -- 校验IP是否存在黑名单中
  94. function BlackList:checkIp(ip)
  95. if blacklist:get(ip) then
  96. return true
  97. end
  98. return false
  99. end
  100. -- 将类返回
  101. return BlackList

在另一个文件中调用该黑名单服务类:test.lua

  1. local BlackList = require "BlackList"
  2. local bl = BlackList:new()
  3. -- 先调用刷新
  4. bl:reflush_blacklist()
  5. -- 然后判断当前IP是否存在黑名单列表中
  6. local ip = ngx.var.remote_addr
  7. local inList = bl:checkIp(ip)
  8. if inList then
  9. -- 将请求终止,并返回403状态码
  10. return ngx.exit(ngx.HTTP_FORBIDDEN)
  11. end

在Redis中添加黑名单

添加黑名单

  1. SADD blacklist "127.0.0.1"

移除黑名单

  1. SREM blacklist "127.0.0.1"

访问测试

  1. curl http://localhost/lua