You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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
 }

额外注意事项

  1. 禁用缓存:确保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";
    
  2. 声明验证:如果需要验证JWT的声明(比如过期时间exp),请完善claim_spec变量,例如:
    local claim_spec = {
        exp = ngx.time()  -- 验证令牌未过期
    }
    
  3. 变量作用域:确保所有变量都使用local声明,避免全局变量导致的跨请求污染。

内容的提问来源于stack exchange,提问作者Loic H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 11:35:30