用 Python 实现 JWT Token 校验、Nginx 转发与 Lua 插件结合实战
1 搞定 API 鉴权,从 Python 到 Nginx 一条龙
最近帮一个朋友优化他的 API 服务,核心需求是安全鉴权,还要性能拉满。我选了 JWT Token 来实现,因为它简单高效,配合 Python 后端和 Nginx 前端转发,再加上 Lua 插件优化,完全可以打造一个又快又安全的鉴权系统。这篇教程是我从头到尾的实战记录,带你用 Python 生成和校验 JWT,结合 Nginx 和 Lua 插件搞定 API 鉴权,代码、配置、踩坑全都有,跟着做准没错!
2 用 Python 搞定 JWT 的生成与校验
先从后端开始,Python 处理 JWT 超级方便,我用的是 PyJWT
库,简单又可靠。以下是我的实现思路和代码。
2.1 生成 JWT Token
我们需要生成一个带用户信息的 JWT Token,包含 user_id
和 expire_time
,用 HS256 算法签名。代码如下:
import jwt
import datetime
SECRET_KEY = "your-secret-key-keep-it-safe" # 别真用这个,换成复杂的!
def generate_token(user_id):
payload = {
"user_id": user_id,
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=24),
"iat": datetime.datetime.utcnow()
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
return token
# 示例:生成一个 token
token = generate_token(123)
print(token)
小贴士:SECRET_KEY
一定要复杂且安全,别直接写在代码里,建议用环境变量(比如 os.environ
)。我一开始没管这个,结果测试代码泄露,差点被朋友笑死。
2.2 校验 JWT Token
校验 Token 的时候,需要验证签名和过期时间。我写了个装饰器,方便在 Flask API 里用:
from functools import wraps
from flask import request, jsonify
def verify_token(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization")
if not token:
return jsonify({"error": "Missing token"}), 401
try:
data = jwt.decode(token.replace("Bearer ", ""), SECRET_KEY, algorithms=["HS256"])
request.user_id = data["user_id"]
except jwt.ExpiredSignatureError:
return jsonify({"error": "Token expired"}), 401
except jwt.InvalidTokenError:
return jsonify({"error": "Invalid token"}), 401
return f(*args, **kwargs)
return decorated
# 示例:受保护的 API
from flask import Flask
app = Flask(__name__)
@app.route("/protected")
@verify_token
def protected():
return jsonify({"message": f"Hello, user {request.user_id}!"})
踩坑经验:我一开始没处理 Bearer
前缀,直接 decode 报错,折腾了半小时才发现是 header 格式问题。记得检查 Authorization
头,确保去掉 Bearer
前缀再解码。
3 配置 Nginx 转发,拦截无效请求
Python 后端搞定后,接下来是 Nginx 做前置网关,拦截无效请求,减轻后端压力。我用 Nginx 转发 API 请求,配合 auth_request
模块来校验 JWT。
3.1 Nginx 配置
以下是我的 Nginx 配置,监听 80 端口,转发到 Flask 后端(跑在 127.0.0.1:5000):
server {
listen 80;
server_name api.yourdomain.com;
location / {
auth_request /auth;
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /auth {
internal;
proxy_pass http://127.0.0.1:5000/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Authorization $http_authorization;
}
}
解释:
auth_request /auth
:Nginx 在转发前先调用/auth
端点校验 Token。/auth
路由是内部路由,调用后端的/verify
接口,只传递Authorization
头。proxy_pass
:有效请求转发到后端 Flask。
3.2 后端验证接口
在 Flask 里加一个 /verify
接口,专门给 Nginx 校验用:
@app.route("/verify", methods=["GET"])
def verify():
token = request.headers.get("Authorization")
if not token:
return "", 401
try:
jwt.decode(token.replace("Bearer ", ""), SECRET_KEY, algorithms=["HS256"])
return "", 200
except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
return "", 401
踩坑经验:我一开始忘了在 Nginx 配置里加 proxy_set_header Authorization
,导致后端收不到 Token,Nginx 疯狂返回 401。检查日志(/var/log/nginx/error.log
)救了我一命。
如果您想深入研究Nginx优化,请查看Nginx日志分析利器 GoAccess(目标访问)部署安装教程 有关监视服务器性能的提示。
4 用 Lua 插件提速,优化鉴权逻辑
Nginx 自带的 auth_request
已经够用了,但如果追求极限性能,可以用 Lua 插件直接在 Nginx 层校验 JWT,减少后端请求。我用的是 OpenResty(Nginx + LuaJIT),效率飞起。
4.1 安装 OpenResty 和 Lua 依赖
先装 OpenResty(Ubuntu 环境为例):
sudo apt-get update
sudo apt-get install -y openresty
再装 Lua 的 JWT 库:
luarocks install lua-resty-jwt
踩坑经验:luarocks 安装可能报 SSL 错误,我切换到国内源(luarocks config variables.LUAROCKS_CONFIG https://luarocks.cn
)才搞定。
4.2 Lua 脚本校验 JWT
在 Nginx 配置文件里加 Lua 脚本,直接校验 JWT:
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
server {
listen 80;
server_name api.yourdomain.com;
location / {
access_by_lua_block {
local jwt = require "resty.jwt"
local auth_header = ngx.var.http_authorization
local secret = "your-secret-key-keep-it-safe"
if not auth_header then
ngx.status = 401
ngx.say('{"error": "Missing token"}')
return ngx.exit(401)
end
local token = string.gsub(auth_header, "Bearer ", "")
local jwt_obj = jwt:verify(secret, token)
if not jwt_obj.valid then
ngx.status = 401
ngx.say('{"error": "Invalid or expired token"}')
return ngx.exit(401)
end
ngx.req.set_header("X-User-ID", jwt_obj.payload.user_id)
ngx.exec("@backend")
}
location @backend {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-User-ID $http_x_user_id;
}
}
}
}
解释:
access_by_lua_block
:在 Nginx 访问阶段运行 Lua 脚本,校验 JWT。jwt:verify
:用lua-resty-jwt
验证 Token,失败返回 401。ngx.exec("@backend")
:验证通过后转发到后端,还把user_id
传过去。
踩坑经验:Lua 脚本对 Token 格式敏感,Bearer
前缀没去干净会导致验证失败。可以用 ngx.log
打印调试信息,日志在 /usr/local/openresty/nginx/logs/error.log
。
5 性能测试:Lua 真的快吗?
我用 wrk
测了下性能(100 并发,10 秒),对比 Nginx auth_request
和 Lua 方案:
- Nginx auth_request:平均延迟 12ms,QPS 约 8000。
- Lua 插件:平均延迟 8ms,QPS 约 12000。
Lua 方案快了 30% 左右,因为它直接在 Nginx 层处理,省去了后端请求的开销。高并发场景下,Lua 的优势更明显。
6 适用场景:啥时候用这套方案?
这套 Python + Nginx + Lua 的组合适合以下场景:
- 中小型 API 服务:需要快速实现安全鉴权,Python 后端简单好维护。
- 高并发场景:Nginx + Lua 能扛住高流量,适合对外 API。
- 预算有限:OpenResty 和 Python 都是开源方案,成本低。
局限性:
- Lua 脚本调试不友好,新手可能被日志整蒙。
- JWT 本身无状态,适合轻量鉴权,但复杂权限管理还得加数据库。
如果需要挡住机器人或爬虫,请参考 利用Cloudflare Turnstile+Nginx Lua 挡住爬虫
有关优化服务器性能的详细信息,请参见rclone进阶使用教程-常用命令详解 有助于高效同步日志或备份。
7 我的实战心得
- 安全第一:
SECRET_KEY
别硬编码,用环境变量或密钥管理工具(比如 AWS Secrets Manager)。我一开始硬写在代码里,差点翻车。 - 日志救命:Nginx 和 Lua 的错误日志是救星,遇到 401 或 500 直接去看
/var/log/nginx/error.log
或 OpenResty 的日志。 - 测试要充分:上线前用
curl
测 Token 失效、过期等边界情况,我第一次没测过期 Token,结果线上直接 500。
这套 Python + Nginx + Lua 的鉴权方案,简单、性能高、成本低,适合大部分中小型项目。Python 负责灵活的 Token 生成,Nginx 拦截无效请求,Lua 插件把性能拉满,整个流程像搭积木一样顺畅。你试过类似的方案没?有什么骚操作,留言分享一下吧!