nginx中access_by_lua通过在access_phase阶段执行lua脚本实现动态权限过滤,需结合jwt解析、redis缓存权限数据、路径/角色匹配及日志审计,兼顾安全性与毫秒级性能。

在 Nginx 中,access_by_lua 是一个非常实用的指令,允许你在请求进入代理或静态服务前,用 Lua 脚本执行自定义逻辑,比如动态鉴权、黑白名单、限流、路由分发等。要实现动态权限过滤,关键在于:把权限判断逻辑下沉到 Nginx 层,避免每次请求都穿透到后端;同时确保判断依据(如用户身份、角色、资源路径、JWT 声明等)可实时获取且低延迟。
获取并解析认证凭据
权限过滤的前提是知道“谁在访问”。常见方式包括:
- 从请求头(如
Authorization: Bearer xxx)提取 JWT,并用lua-resty-jwt解析验证签名与过期时间 - 从 Cookie 中读取 session ID,再通过
resty.redis或resty.http查询中心化权限服务(注意加缓存和超时) - 对于内部调用,也可信任上游网关注入的
X-User-ID和X-Roles等可信头
示例(JWT 解析):
local jwt = require "resty.jwt"local jwt_obj = jwt:new()
local token = ngx.req.get_headers()["Authorization"]
if not token or not string.match(token, "^Bearer%s+") then
return ngx.exit(401)
end
token = string.gsub(token, "^Bearer%s+", "")
local res, err = jwt_obj:verify_jwt_obj(token, { secret = "your-secret" })
if not res then
return ngx.exit(401)
end
ngx.ctx.user = res.payload
根据路径与用户属性做细粒度权限判定
拿到用户信息后,需结合当前请求的 URI、HTTP 方法、甚至 query 参数,判断是否允许访问。建议将权限规则外置(如 Redis Hash、Consul KV 或本地 JSON 文件),避免硬编码:
- 例如:对
/api/v1/orders的POST请求,要求用户角色包含"admin"或"order-writer" - 可用
ngx.var.request_uri和ngx.var.request_method获取上下文 - 权限数据建议按
perm:<path_md5></path_md5>缓存,TTL 控制在 30–60 秒,兼顾一致性与性能
示例(简单角色匹配):
local path = ngx.var.urilocal method = ngx.var.request_method
local roles = ngx.ctx.user.roles or {}
local required_roles = {
["/api/v1/orders"] = { "admin", "order-writer" },
["/api/v1/users"] = { "admin" }
}
if required_roles[path] then
local allowed = false
for _, r in ipairs(required_roles[path]) do
if table.contains(roles, r) then
allowed = true
break
end
end
if not allowed then
return ngx.exit(403)
end
end
处理失败与审计日志
权限拒绝不能只返回错误码,还应记录必要上下文用于排查和安全审计:
- 使用
ngx.log(ngx.WARN, ...)记录被拒的 IP、URI、用户 ID、时间戳 - 敏感操作(如删除、越权访问)可额外投递到 Kafka 或写入本地 ringbuffer,供 SIEM 系统采集
- 避免在日志中打印完整 token 或密码字段,脱敏后再记录
示例(记录拒绝):
if not allowed thenngx.log(ngx.WARN,
"Access denied: ip=", ngx.var.remote_addr,
" uri=", path,
" user_id=", ngx.ctx.user.user_id or "unknown")
return ngx.exit(403)
end
注意事项与性能优化
access_by_lua 运行在 Nginx 的 access 阶段,阻塞式执行,任何耗时操作都会拖慢整个请求。务必注意:
- 禁止在其中做同步 HTTP 请求(如
http:request()默认是同步);必须用http:request_uri()+set_more_tries或改用cosocket异步调用 - Redis 查询优先用连接池(
redis:set_keepalive()),并设置短超时(如 100ms)和 failover 降级逻辑 - 高频路径(如健康检查
/healthz)建议跳过权限检查,用if ($uri = /healthz) { access_by_lua_block { } }或前置 location 匹配绕过 - 启用
lua_code_cache on;(生产环境必须开启),避免重复加载 Lua 代码影响性能











