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>
相关推荐
亦舒.1 小时前
宝塔面板Nginx手动配置负载均衡实战指南
运维·nginx·负载均衡
deming_su3 小时前
轻松上手:使用Nginx实现高效负载均衡
运维·nginx·负载均衡
Jack_hrx5 小时前
docker部署nginx
linux·nginx·docker·centos
紫璨月7 小时前
nginx反向代理的bug
运维·nginx·bug
快下雨了L8 小时前
Lua现学现卖
开发语言·lua
就叫飞六吧10 天前
基于keepalived、vip实现高可用nginx (centos)
python·nginx·centos
WIN赢10 天前
PostMan使用
测试工具·lua·postman
小生云木10 天前
Linux离线编译安装nginx
linux·运维·nginx
Cat God 00710 天前
项目上线(若依前后分离版)
java·nginx
多多*10 天前
计算机网络期末 网络基础概述
运维·服务器·网络·数据库·计算机网络·oracle·lua