access_by_lua* 阶段最适合执行自定义鉴权逻辑,它在uri重写之后、反向代理之前运行,可中断请求并返回错误响应;需确保nginx支持ngx_http_lua_module,推荐使用openresty,并通过access_by_lua_block或access_by_lua_file嵌入脚本实现令牌校验、jwt解析等操作。

Nginx 通过 ngx_lua 模块(通常集成在 OpenResty 中)支持在请求处理的多个阶段嵌入 Lua 脚本,其中访问阶段(access_by_lua*)最适合执行自定义鉴权逻辑——它在 URI 重写之后、静态文件处理或反向代理之前运行,可中断请求并返回错误响应。
确认环境已支持 Lua 运行
确保你使用的是 OpenResty 或已编译 ngx_lua 模块的 Nginx。验证方式:
- 执行
nginx -V 2>&1 | grep -o with-http_lua_module,有输出即支持 - 或在配置中添加
content_by_lua_block { ngx.say("OK") }测试能否响应 - 推荐直接使用 OpenResty,开箱即用且持续更新
在 access 阶段编写鉴权 Lua 脚本
使用 access_by_lua_block 或 access_by_lua_file(更推荐后者,便于维护)在 server 或 location 块中插入逻辑。典型结构如下:
- 提取请求头、参数或 Cookie(如
ngx.var.arg_token、ngx.req.get_headers()["Authorization"]) - 校验签名、解析 JWT、查 Redis/数据库(需配合
resty.redis或resty.http) - 鉴权失败时调用
ngx.exit(ngx.HTTP_UNAUTHORIZED)或自定义状态码(如403) - 成功则自然向下流转,无需显式放行
示例(/auth_check.lua):
local jwt = require "resty.jwt"
local jwt_obj = jwt:new()
local token = ngx.var.arg_token or (ngx.req.get_headers()["Authorization"] and string.match(ngx.req.get_headers()["Authorization"], "Bearer%s+(.+)"))
<p>if not token then
ngx.status = 401
ngx.say('{"error":"missing token"}')
ngx.exit(401)
end</p><p>local res, err = jwt_obj:verify_jwt_obj(token, { secret = "my_secret" })
if not res then
ngx.status = 401
ngx.say('{"error":"invalid token"}')
ngx.exit(401)
end</p><p>-- 可选:检查 payload 中的 scope、exp、白名单等
local payload = res.payload
if payload.scope ~= "admin" then
ngx.exit(403)
end</p>关联到 Nginx 配置中
在对应 location 中引用脚本:
location /api/admin {
access_by_lua_file /path/to/auth_check.lua;
proxy_pass http://backend;
}注意:access_by_lua* 会阻塞当前 worker,高并发下建议:
- 避免同步耗时操作(如直连 MySQL),优先用非阻塞的
resty.redis或resty.http - 合理设置超时(
lua_socket_connect_timeout、lua_socket_send_timeout等) - 对鉴权结果做简单缓存(如用
lua_shared_dict缓存 token 解析结果,减少重复解析)
调试与日志技巧
Lua 日志默认输出到 Nginx error log,级别由 lua_log_level 控制:
- 开发时设为
info或debug,配合ngx.log(ngx.INFO, "token:", token) - 避免在生产环境打印敏感信息(如完整 token、用户 ID)
- 用
ngx.ctx在同一请求的不同阶段共享变量(如把解析后的用户 ID 存入ngx.ctx.user_id,后续 handler 可复用)











