You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

在Nginx服务器上解码JWT Token并记录用户信息的实现方案咨询

当然可以实现这个需求!既然你已经接受基于Lua的方案,那我们可以借助Nginx的ngx_lua模块(OpenResty自带这个模块,用起来很方便)来解码JWT Token,提取用户信息后写入日志。下面是具体的步骤和配置示例:

前置准备

首先你需要确保Nginx环境支持Lua:

  • 最省心的方式是使用OpenResty(Nginx的增强发行版,默认集成了ngx_lua和常用Lua库)。
  • 如果是自行编译Nginx,编译时需添加--add-module=path/to/ngx_lua参数(路径替换为你的ngx_lua模块位置)。
实现思路

核心逻辑分为三步:

  1. 从请求的Authorization头中提取JWT Token(去掉Bearer 前缀)。
  2. 解码JWT的Payload部分(JWT由Header/Payload/Signature三部分组成,用户信息通常存在Payload里)。
  3. 将提取到的用户信息存入Nginx变量,最后在自定义日志格式中引用这些变量。
具体配置示例

1. 定义自定义日志格式

先在Nginx主配置或Server块中定义包含JWT用户信息的日志格式:

log_format jwt_combined '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       '"$http_referer" "$http_user_agent" '
                       '"jwt_sub:$jwt_sub" "jwt_username:$jwt_username"';

这里的$jwt_sub$jwt_username是我们后续要通过Lua脚本设置的变量,对应JWT Payload里的用户ID和用户名(根据你的实际JWT字段调整)。

2. 编写Lua处理逻辑并配置反向代理

在Server块中加入Lua脚本处理JWT,并配置反向代理规则:

server {
    listen 80;
    server_name your-domain.com;

    # 提前声明要使用的Nginx变量(可选,但能避免潜在的变量未定义警告)
    set $jwt_sub "";
    set $jwt_username "";

    # 处理JWT解码与信息提取
    access_by_lua_block {
        -- 获取Authorization请求头
        local auth_header = ngx.req.get_headers()["Authorization"]
        if auth_header then
            -- 提取Bearer后的Token部分
            local jwt_token = string.match(auth_header, "^Bearer%s+(.+)")
            if jwt_token then
                -- 分割JWT的三个组成部分
                local parts = {}
                for part in string.gmatch(jwt_token, "[^.]+") do
                    table.insert(parts, part)
                end

                if #parts == 3 then
                    -- 处理Payload的Base64URL解码(JWT用的是Base64URL编码,需转成标准Base64)
                    local payload = parts[2]
                    payload = payload:gsub("-", "+"):gsub("_", "/")
                    -- 补全Base64的padding字符
                    local padding = #payload % 4
                    if padding > 0 then
                        payload = payload .. string.rep("=", 4 - padding)
                    end

                    -- 解码为JSON字符串并解析成Lua表
                    local decoded_payload = ngx.decode_base64(payload)
                    if decoded_payload then
                        local cjson = require("cjson")
                        local payload_data = cjson.decode(decoded_payload)
                        -- 将需要的用户信息存入Nginx变量
                        ngx.var.jwt_sub = payload_data.sub or ""
                        ngx.var.jwt_username = payload_data.username or ""
                    end
                end
            end
        end
    }

    # 反向代理到你的微服务集群
    location / {
        proxy_pass http://your-microservice-upstream;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 使用自定义日志格式记录日志
    access_log /var/log/nginx/jwt_access.log jwt_combined;
}
优化方案:使用lua-resty-jwt库简化代码

如果不想自己处理Base64URL解码和JSON解析的细节,可以用官方维护的lua-resty-jwt库,它封装了更完善的JWT处理逻辑:

  1. 安装库(OpenResty环境下可通过opm安装):
opm install openresty/lua-resty-jwt
  1. 修改Lua脚本部分:
access_by_lua_block {
    local jwt = require("resty.jwt")
    local auth_header = ngx.req.get_headers()["Authorization"]
    
    if auth_header then
        local jwt_token = string.match(auth_header, "^Bearer%s+(.+)")
        if jwt_token then
            -- 仅解析JWT不验证签名(如果需要验证,传入你的签名密钥作为第二个参数)
            local jwt_obj = jwt:load_jwt(jwt_token)
            if jwt_obj.valid then
                ngx.var.jwt_sub = jwt_obj.payload.sub or ""
                ngx.var.jwt_username = jwt_obj.payload.username or ""
            end
        end
    end
}
注意事项
  • 上述基础方案不验证JWT签名,如果需要确保Token的合法性,可以在lua-resty-jwtverify方法中传入你的签名密钥(比如jwt:verify(your_secret_key, jwt_token))。
  • 日志中的字段要和你的JWT Payload实际字段对应,比如有的JWT用user_id代替sub,记得调整变量赋值逻辑。
  • 脚本中已处理了多种异常情况(比如无Authorization头、JWT格式错误),不会因解码失败阻断请求,保证反向代理的正常转发。

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

火山引擎 最新活动