当前位置:   article > 正文

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

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

背景

本文介绍Nginx中Lua模块使用方式,并结合案例进行介绍。案例介绍通过lua脚本提取HTTP请求头中的token字段,经过JWT校验并提取id和name信息,设置到http请求头中发向后段服务器。
默认情况下,Nginx自身不携带lua模块,即不支持通过lua脚本进行功能扩展。需要在编译Nginx时手动引入lua模块,或者直接使用openresty,本文结合后者进行介绍。

1.openresty安装流程

1.1 安装包下载

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
  • 3

1.2 配置

./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 
  • 1

由于案例不涉及使用Redis,因此可以在configure阶段通过–without-http_redis2_module以最小化安装。
执行结果如下:

platform: linux (linux)

...

Type the following commands to build and install:
    gmake
    gmake install
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.3 编译和安装

gmake
gmake install
  • 1
  • 2

1.4 案例测试

1.4.1 测试nginx正常工作

修改配置,添加一个location:

location /test {
    return 200 "test success";
}
  • 1
  • 2
  • 3

运行nginx后:

[root@124 sbin]# curl http://localhost:8765/test
test success
  • 1
  • 2

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

修改配置,添加一个location:

location /lua{
    content_by_lua 'ngx.say("<h1>HELLO,Lua</h1>")';
}
  • 1
  • 2
  • 3

运行nginx后:

[root@124 sbin]# curl http://localhost:8765/lua
<h1>HELLO,Lua</h1>
  • 1
  • 2

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文件内容如下:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

其中: 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块中添加:

access_by_lua_file ./lua/jwt.lua;
  • 1

4.3 案例测试

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

[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
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/921595
推荐阅读
相关标签
  

闽ICP备14008679号