Skip to content

Access Control

基于 Lua 的访问控制设计

整体逻辑:基于 group_ip 获取规则组,白名单或黑名单的模式匹配请求上下文,获取访问控制结果

规则管理器 manager模块化提供,匹配时提供核心方法

  • function get_rules(group_id):基于 group_id 获取的规则列表方法
  • function get_value_func(rule): 基于 rule 自生获取对应的 function
  • 设计的 operatorfunction 时,会寻找规则中的 方法规则进行匹配
  • 方法规则的设计目的是为了,对于规则中无法通过简单的字符串匹配的场景,实现更复杂的匹配
  • function get_internal_function(rule): 基于 rule 自生获取对应的内置 function
  • 设计的 operator@function 时,会寻找规则中的 内置方法规则进行匹配
  • 内置方法规则的设计目的是为了,提供一些常用的匹配方法,方便用户直接使用
local _M = {}

local ac_utils = require("lua_modules.http_req.access_control.ac_utils")

-- 匹配规则
-- @param field_val 字段值
-- @param operator 操作符
-- @param value 规则值
-- @param rule 规则本身,可以适配更多复杂场景
-- @param manager 可选的存储对象,默认为 rules_manager
-- @return boolean 是否匹配
local function match_rule(field_val, operator, value, rule, manager)
    if not field_val or not operator then
        return false
    end

    -- 方法,获取方法函数
    if operator == "function" then
        -- 获取计算值的函数
        local value_func, err = ac_utils.get_value_func(rule, manager)
        if not value_func then
            ngx.log(ngx.ERR, "failed to get function rule: ", err or "unknown error")
            return false
        end

        if type(value_func) ~= "function" then
            ngx.log(ngx.ERR, "value_func must be a function when operator is 'function'")
            return false
        end
        -- 调用函数并确保返回布尔值
        local ok, res = pcall(value_func, field_val)
        if not ok then
            return false
        end
        if type(res) ~= "boolean" then
            return false
        end
        return res
    end

    -- 方法,获取内置的方法
    if operator == "@function" then
        local func, err = ac_utils.get_internal_function(rule, manager)
        if not func then
            ngx.log(ngx.ERR, "failed to get function: ", err or "unknown error")
            return false
        end

        if type(func) ~= "function" then
            ngx.log(ngx.ERR, "func must be a function when operator is '@function'")
            return false
        end

        -- 调用函数并确保返回布尔值
        local ok, res = pcall(func, field_val)
        if not ok then
            return false
        end
        if type(res) ~= "boolean" then
            return false
        end
        return res
    end

    --一些通用的操作符
    if operator == "equals" then
        return field_val == value
    elseif operator == "not_equals" then
        return field_val ~= value
    elseif operator == "prefix" then
        return field_val:sub(1, #value) == value
    elseif operator == "suffix" then
        return field_val:sub(-#value) == value
    elseif operator == "contains" then
        return field_val:find(value, 1, true) ~= nil
    elseif operator == "regex" then
        return ngx.re.find(field_val, value, "jo") ~= nil
    elseif operator == "in" then
        -- 支持逗号分隔字符串或 table
        if type(value) == "string" then
            for v in value:gmatch("[^,]+") do
                if field_val == v then
                    return true
                end
            end
        elseif type(value) == "table" then
            for _, v in ipairs(value) do
                if field_val == v then
                    return true
                end
            end
        end
        return false
    else
        -- 未知操作符
        return false
    end
end

-- 核心检查函数
-- 白名单检查是只要有一条规则命中就放行
-- 黑名单检查是只要有一条规则命中就拒绝
-- @param ctx 请求上下文
-- @param rules 规则列表 一个规则示例 {field="host", operator="equals", value="example.com", effect="deny"}
-- @param mode 检查模式 "whitelist" 或 "blacklist"
-- @param manager 可选的规则管理器,默认为 rules_manager
-- @return boolean 是否允许通过
local function check_ctx(ctx, rules, mode, manager)
    mode = mode or "blacklist"

    for _, rule in ipairs(rules) do
        local field_val = ctx[rule.field] -- 获取指定字段的值
        if field_val and match_rule(field_val, rule.operator, rule.value, rule, manager) then
            if mode == "blacklist" and rule.effect == "deny" then
                return false
            elseif mode == "whitelist" and rule.effect == "allow" then
                return true
            end
        end
    end

    -- 如果没有任何规则命中
    if mode == "blacklist" then
        return true -- 默认放行
    else
        return false -- 默认拒绝
    end
end

-- 通用检查函数
-- @param ctx 请求上下文
-- @param rules 规则列表
-- @param mode 检查模式 "whitelist" 或 "blacklist"
-- @param manager 可选的规则管理器,默认为 rules_manager
-- @return boolean 是否允许通过
function _M.check_ctx(ctx, rules, mode, manager)
    return check_ctx(ctx, rules, mode, manager)
end

-- 白名单检查
-- 只要有一条规则命中就放行
-- @param ctx 请求上下文
-- @param rules 规则列表
-- @param manager 可选的规则管理器,默认为 rules_manager
-- @return boolean 是否允许通过
function _M.check_ctx_whitelist(ctx, rules, manager)
    return check_ctx(ctx, rules, "whitelist", manager)
end

-- 黑名单检查
-- 只要有一条规则命中就拒绝
-- @param ctx 请求上下文
-- @param rules 规则列表
-- @param manager 可选的规则管理器,默认为 rules_manager
-- @return boolean 是否允许通过
function _M.check_ctx_blacklist(ctx, rules, manager)
    return check_ctx(ctx, rules, "blacklist", manager)
end

return _M