使用 openresty 做网关认证 access_by_lua

创建日期: 2024-05-23 16:28 | 作者: 风波 | 浏览次数: 14 | 分类: openresty

来源:https://openresty-reference.readthedocs.io/en/latest/Directives/#access_by_lua

openresty 有很多适配 nginx 的段,其中 access_by_lua/access_by_lua_block 可以用于权限认证。

在 nginx 的 server 段中增加如下的 location,其中的 lua 代码用来进行验证。

Note that when calling ngx.exit(ngx.OK) within a access_by_lua handler, the nginx request processing control flow will still continue to the content handler. To terminate the current request from within a access_by_lua handler, calling ngx.exit with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures.

意思是 ngx.exit 的值在 200 <= status < 300 的时候,控制流会继续向下走,而当 ngx.exitngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) 或者其类似的错误码时,则会中止控制流。

文档中的代码示例

 location / {
     access_by_lua '
         local res = ngx.location.capture("/auth")

         if res.status == ngx.HTTP_OK then
             return
         end

         if res.status == ngx.HTTP_FORBIDDEN then
             ngx.exit(res.status)
         end

         ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
     ';

     # proxy_pass/fastcgi_pass/postgres_pass/...
 }

测试代码示例

  1. 先解决一下 lua 的 redis 无法解析本地域名的问题,使用的库是 lua-local-resolver,代码仓库 https://github.com/ysugimoto/lua-local-resolver 需要先在 server 同级别添加一个 init_by_lua_file lualibs/init.lua;,而 init.lua 中的代码是
package.path = package.path .. ";/usr/local/openresty/nginx/lualibs/?.lua"
  1. 连接 redis
  2. 从 http header 里面获取 Token
  3. redis 中获取 token 对应的值
  4. 判断值是否为空

这其中的任何一个步骤失败了,都要返回一个 ngx.exit(ngx.HTTP_FORBIDDEN),中止后面的操作。 如果都成功了,那么就继续向下走,直接走到 proxy_pass

配置代码如下

    location / {
        access_by_lua_block {
            local resolver = require "local-resolver"
            resolver = resolver.new("/etc/hosts")

            local redis = require "resty.redis"

            local red = redis:new()

            local ok, err = red:connect(resolver:resolve("openresty_kvrocks"), 6666)
            if not ok then
                --ngx.say("HTTP_UNAUTHORIZED: ", "x")
                --ngx.say("failed to connect cache: ", err)
                ngx.log(ngx.ERR, err)
                ngx.exit(ngx.HTTP_FORBIDDEN)
                return
            end

            local token = ngx.req.get_headers()['Token']
            if token == nil then
                token = "null"
            end

            local key = "resty:auth:access:token:" .. token
            local res, err = red:get(key)
            if not res then
                ngx.log(ngx.ERR, "get redis key failed: " .. key)
                ngx.exit(ngx.HTTP_FORBIDDEN)
                return
            end

            if res == ngx.null then
                ngx.log(ngx.ERR, "get redis key empty: " .. key)
                ngx.exit(ngx.HTTP_FORBIDDEN)
                return
            end

            --ngx.say(ngx.req.get_headers()['Token'])
        }

        proxy_pass http://simple_server;
        proxy_connect_timeout   18000;
        proxy_send_timeout      18000;
        proxy_read_timeout      18000;
        client_max_body_size 10240m;
    }
14 浏览
10 爬虫
0 评论