如何通过OpenResty Lua-Nginx模块实现每次请求验证JWT令牌
问题:Nginx反向代理JWT验证仅首次有效,后续请求无需验证
我开发了一个React网站,使用带有效证书的HTTPS协议,需调用第三方管理的远程HTTP API。该第三方API通过URL中的GET参数?key=XXXX做访问验证,且存在CORS限制。为此搭建了Nginx反向代理,添加了允许CORS的头部,并增加JWT令牌验证功能。当前基础功能正常:未携带或携带无效Authorization Bearer令牌时,Nginx会阻止访问第三方API。但问题是,一旦提供一次有效的Authorization头部后,后续所有请求(包括不同设备、不同IP的请求)都会直接通过验证,无需再次检查令牌。请问如何实现每次请求都验证JWT令牌?
原配置
Nginx主配置
server { listen 443 ssl http2; ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; location / { default_type application/json; return 200 '{"message": "Endpoint is required"}'; } location ^~ /api/ { include jwt.conf; include headers.conf; set $args $args&key={KEY}; proxy_pass http://www.third-party.com/api/; } }
jwt.conf配置(使用openresty lua-nginx-module)
access_by_lua_block { local jwt = require "resty.jwt" local jwt_obj = jwt:verify("{SECRET}", token, claim_spec) local auth_header = ngx.var.http_Authorization if auth_header then _, _, token = string.find(auth_header, "Bearer%s+(.+)") end if token == nil then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.header.content_type = "application/json; charset=utf-8" ngx.say("{\"error\": \"missing JWT token or Authorization header\"}") ngx.exit(ngx.HTTP_UNAUTHORIZED) end if not jwt_obj["verified"] then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.log(ngx.WARN, jwt_obj.reason) ngx.header.content_type = "application/json; charset=utf-8" ngx.say("{\"error\": \"" .. jwt_obj.reason .. "\"}") ngx.exit(ngx.HTTP_UNAUTHORIZED) end }
headers.conf配置
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; proxy_buffers 32 16k; proxy_busy_buffers_size 64k; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; add_header 'Access-Control-Max-Age' 1728000; return 204; } # Ajouter les headers de contrôle d'accès CORS add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
问题原因
核心问题是jwt.conf中的Lua代码执行顺序错误:在从请求头提取JWT令牌之前,就调用了jwt:verify方法验证token变量(此时token尚未定义,值为nil)。后续提取到有效token后,并未重新执行验证逻辑,加上OpenResty的Lua变量作用域特性,导致首次有效验证后,后续请求错误地复用了之前的验证结果。
修复方案
调整jwt.conf中的代码顺序,确保先提取token,再执行验证逻辑,同时规范变量声明:
access_by_lua_block { local jwt = require "resty.jwt" local auth_header = ngx.var.http_Authorization local token = nil local claim_spec = {} -- 可根据需求添加JWT声明验证规则,比如exp过期时间 if auth_header then _, _, token = string.find(auth_header, "Bearer%s+(.+)") end -- 检查token是否存在 if token == nil then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.header.content_type = "application/json; charset=utf-8" ngx.say("{\"error\": \"missing JWT token or Authorization header\"}") ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 执行JWT验证 local jwt_obj = jwt:verify("{SECRET}", token, claim_spec) if not jwt_obj.verified then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.log(ngx.WARN, jwt_obj.reason) ngx.header.content_type = "application/json; charset=utf-8" ngx.say("{\"error\": \"" .. jwt_obj.reason .. "\"}") ngx.exit(ngx.HTTP_UNAUTHORIZED) end }
额外注意事项
- 禁用缓存:确保Nginx没有对/api/路径启用任何缓存,避免请求被缓存跳过验证。可在location /api/中添加:
proxy_cache off; expires off; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; - 声明验证:如果需要验证JWT的声明(比如过期时间
exp),请完善claim_spec变量,例如:local claim_spec = { exp = ngx.time() -- 验证令牌未过期 } - 变量作用域:确保所有变量都使用
local声明,避免全局变量导致的跨请求污染。
内容的提问来源于stack exchange,提问作者Loic H
相关产品推荐
相关产品推荐

