Nginx系列-12 Nginx使用Lua脚本进行JWT校验

背景

本文介绍Nginx中Lua模块使用方式,并结合案例进行介绍。案例介绍通过lua脚本提取HTTP请求头中的token字段,经过JWT校验并提取id和name信息,设置到http请求头中发向后段服务器。

默认情况下,Nginx自身不携带lua模块,即不支持通过lua脚本进行功能扩展。需要在编译Nginx时手动引入lua模块,或者直接使用openresty,本文结合后者进行介绍。

1.openresty安装流程

1.1 安装包下载

shell 复制代码
wget https://openresty.org/download/openresty-1.25.3.1.tar.gz
tar -zxvf openresty-1.25.3.1.tar.gz
cd openresty-1.25.3.1/

1.2 配置

shell 复制代码
./configure --prefix=/usr/local/ewen/nginx --with-luajit --without-http_redis2_module --with-http_ssl_module --with-http_sub_module --with-http_stub_status_module --with-http_dav_module --with-http_mp4_module --with-http_v2_module 

由于案例不涉及使用Redis,因此可以在configure阶段通过--without-http_redis2_module以最小化安装。

执行结果如下:

shell 复制代码
platform: linux (linux)

...

Type the following commands to build and install:
    gmake
    gmake install

1.3 编译和安装

shell 复制代码
gmake
gmake install

1.4 案例测试

1.4.1 测试nginx正常工作

修改配置,添加一个location:

json 复制代码
location /test {
    return 200 "test success";
}

运行nginx后:

shell 复制代码
[root@124 sbin]# curl http://localhost:8765/test
test success

1.4.2 测试nginx的lua插件正常工作

修改配置,添加一个location:

json 复制代码
location /lua{
    content_by_lua 'ngx.say("<h1>HELLO,Lua</h1>")';
}

运行nginx后:

shell 复制代码
[root@124 sbin]# curl http://localhost:8765/lua
<h1>HELLO,Lua</h1>

2.lua介绍

参考: Lua使用方式介绍

3.http处理流程与lua模块

Nginx系列-12 HTTP消息处理流程文中介绍,Nginx处理HTTP消息的流程可以分为如下11个阶段:

Lua模块可以参与rewrite、access、content、log阶段,流程和对应指令如下所示:

当使用lua生成HTTP响应内容时,在content阶段处理对应content_by_lua指令,而进行请求校验时在access阶段处理,对应access_by_lua_block或者access_by_lua_file指令。

4.案例介绍

案例介绍通过lua实现校验请求是否合法:请求头中带有合法的token, 则通过校验,否则返回401响应。

案例使用jwt解析token,因此需要引入jwt依赖(lua-resty-jwt库),包括hmac.lua、evp.lua、jwt.lua、jwt-validators.lua; hmac.lua来源于lua-resty-jwt\vendor\resty,evp.lua、jwt.lua、jwt-validators.lua来源于lua-resty-jwt\lib\resty.

可以通过access_by_lua_block块的形式或者access_by_lua_file文件形式引入lua文件,本文选择后者。

4.1 lua文件介绍

ewen.lua文件内容如下:

lua 复制代码
local white_url_list = {'/open'};

-- 修改为自己的jwt密钥
public_key = "......";

function startsWith(str, prefix)
    return string.sub(str, 1, string.len(prefix)) == prefix;
end

local function exit_with_code_msg(code, msg)
    ngx.status = code;
    ngx.say(msg);
    ngx.exit(code);
end

local function get_jwt_claims(token, public_key)
    local jwt = require("resty.jwt");
    local jwt_obj, err = jwt:verify(public_key, token);
    if not jwt_obj then
        ngx.say("Failed to verify JWT: ", err);
        return nil;
    end
    return jwt_obj["payload"];
end

local function check_token_and_fill_head()
    local token = ngx.req.get_headers()["token"];
    if not token then
        exit_with_code_msg(ngx.HTTP_UNAUTHORIZED, "401 Unauthorized: Token not found or invalid");
    end

    local payload = get_jwt_claims(token, public_key)
    if not payload then
        exit_with_code_msg(ngx.HTTP_UNAUTHORIZED, "401 Unauthorized: Token not found or invalid");
    end
    ngx.req.set_header("id", tostring(payload["id"]));
    ngx.req.set_header("name", tostring(payload["name"]));
    ngx.req.set_header("role", tostring(payload["role"]));
end

local function need_check()
    for _, path in ipairs(white_url_list) do
        if startsWith(ngx.var.request_uri, path) then
            return true;
        end
    end
    return false;
end

if not need_check() then
    ngx.log(ngx.INFO, "JWT: " .. tostring(ngx.var.request_uri) .. "  check.");
    check_token_and_fill_head()
else
    ngx.log(ngx.INFO, "JWT: " .. tostring(ngx.var.request_uri) .. " not need to check.");
end

其中 : ngx.status属性表示HTTP响应状态码;ngx.say方法用于设置响应体内容;ngx.exit(code)用于设置状态码并直接返回给客户端(结束请求);ngx.req.set_header方法用于设置请求头;require("resty.jwt")表示引入jwt库,之后jwt:verify方法用于对token进行JWT校验和Claim信息提取。

4.2 配置lua文件

在nginx.conf文件的http块或者server块中添加:

shell 复制代码
access_by_lua_file ./lua/jwt.lua;

4.3 案例测试

分别使用带token和不带token进行测试:

shell 复制代码
[root@124 conf]# curl -X GET http://localhost:8765/lua
401 Unauthorized: Token not found or invalid

[root@124 conf]# curl -X GET -H "token:eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3MTk5NzkyMjEsInV1aWQiOiI1Y2M4OGYwZC1hNGM5LTQyNTItODdkMC1hNzZkNmQxNzEzZTEiLCJncmFudFR5cGUiOiJzYWMiLCJidXNpbmVzcyI6ImVjaGF0OnVlOjE5NjAwMDEwMDk4Iiwic2NvcGUiOiJlY2hhdDpldHMtY2FyZXRha2VyIGVjaGF0OmV0cy1lbXBsb3llZSBlY2hhdDplZXAiLCJsb2dpbkluZm8iOiJlY2hhdDp1ZToxOTYwMDAxMDA5OCIsInVzZXJJZCI6LTEsInZlcnNpb24iOiIxLjAuMCJ9.augjMcBV7BKXOb4_JjIcZK4RGuYDoVf73DksFVR8o49F1yQWZiRn07ZH_xmt2RnJmpwRtg-fUmIGn7tNv3Q7Dg" http://localhost:8765/lua
<h1>HELLO,Lua</h1>
相关推荐
m0_7482394722 分钟前
微信小程序-Docker+Nginx环境配置业务域名验证文件
nginx·docker·微信小程序
铅华尽23 分钟前
Nginx学习笔记
笔记·学习·nginx
纸飞机√※13 小时前
windows下部署安装 ELK,nginx,tomcat日志分析
windows·nginx·elk·tomcat
Fly不安全18 小时前
Web安全:缓存欺骗攻击;基于缓存、CDN的新型Web漏洞
nginx·web安全·缓存·web·cdn·缓存欺骗攻击
龙哥·三年风水20 小时前
openresty(nginx)+lua+kafka实现日志搜集系统
kafka·lua·openresty
Narutolxy2 天前
使用 Nginx 实现动态图片加水印:技术探索与实践指南20250122
运维·nginx
�时过境迁,物是人非2 天前
ECS中实现Nginx四层和七层负载均衡以及ALB/NLB实现负载均衡
运维·nginx·负载均衡
东软吴彦祖2 天前
包安装利用 LNMP 实现 phpMyAdmin 的负载均衡并利用Redis实现会话保持nginx
linux·redis·mysql·nginx·缓存·负载均衡
牙牙7052 天前
ansible一键安装nginx二进制版本
服务器·nginx·ansible
苹果醋32 天前
golang 编程规范 - Effective Go 中文
java·运维·spring boot·mysql·nginx