OpenResty,Nginx实现接口验签与黑名单控制

发布于:2024-04-14 ⋅ 阅读:(73) ⋅ 点赞:(0)

介绍

nginx与openresty是两种优秀知名的7层负载均衡软件,nginx以其出色的性能和稳定性成为首选,而openresty则是在Nginx基础上构建的,支持嵌入Lua语言,大幅提升了开发效率。

安装OpenResty

功能实现

验签直接返回响应体

  • 打开nginx.conf文件编写脚本(使用hmac对原文生成hash值再取base64编码)
server {
        listen       80;
        server_name  localhost;
		#对外接口验签
		location /api {
		    default_type "application/json";
		    content_by_lua_block {
		        -- table是否包含指定元素方法
		        function arr_include(tab, value)
                    for k,v in pars(tab) do
                      if v == value then
                          return true
                      end
                    end
                    return false
                end

		        local headers = ngx.req.get_headers();
				local token = headers["token"];
				-- 无token
				if (token == nil) then
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "token为空"));
                    return;
				end
				-- 黑名单
                local guestIp = headers["X-Real-IP"] or headers["X-Forwarded-For"] or ngx.var.remote_addr;
                ngx.say(string.format("请求ip:%s", guestIp));
                local blacks = {"127.0.0.1", "10.190.75.139"};
                if (arr_include(blacks, guestIp)) then
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "黑名单禁止访问"));
                    return;
                end
				-- 验签-读取请求体
				ngx.req.read_body();
				local reqBody = ngx.req.get_body_data();
				ngx.say(string.format("请求体:%s", reqBody));
			    -- 开始验签
                local key = "A7409BB67B472E6CC7EF17C49784A6B8";
                local digest = ngx.encode_base64(ngx.hmac_sha1(key, reqBody));
                ngx.say(string.format("nginx签名值:%s", digest));
                if (digest == token) then
                    ngx.say(string.format("{\"success\":true,\"msg\":\"%s\"}", "校验通过"));
                else
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "验签失败"));
                end
			}
		}
    }
  • 启动nginx.exe

  • 客户端hmac哈希签名
    通过sha1哈希算法与密钥生成签名值
    在这里插入图片描述

  • 注释黑名单代码并且nginx -s reload后,调用接口测
    在这里插入图片描述
    在这里插入图片描述

  • 启用黑名单代码后,调用接口测试
    在这里插入图片描述
    黑名单功能也可以将黑名单放入redis,通过OpenResty编写lua脚本从redis获取黑名单ip来实现

验签通过后转发到上游服务

  • 介绍
    在上面的例子实现了验签后直接返回结果,但真实应用的时候一般是验签通过后转发到上游的业务应用,这时候我们的脚本得稍微进行改造,使用access_by_lua_block。
  • 代码实现
server {
        listen       80;
        server_name  localhost;
        location / {
		    default_type "text/html";
            content_by_lua 'ngx.say("<html><p>nginx start by lua<p><html>")';
        }
		#对外接口验签
		location /api {
		    default_type "application/json";
		    access_by_lua_block {
		        -- table是否包含指定元素方法
		        local function arr_include(tab, value)
                    for k,v in pairs(tab) do
                      if v == value then
                          return true
                      end
                    end
                    return false
                end

		        local headers = ngx.req.get_headers();
				local token = headers["token"];
				-- 无token
				if (token == nil) then
				    ngx.status = 403;
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "token为空"));
                    return ngx.exit(403);
				end
				-- 黑名单
                local guestIp = headers["X-Real-IP"] or headers["X-Forwarded-For"] or ngx.var.remote_addr;
                local blacks = {"10.190.75.139"};
                if (arr_include(blacks, guestIp)) then
                    ngx.status = 403;
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "黑名单禁止访问"));
                    return ngx.exit(403);
                end
				-- 验签-读取请求体
				ngx.req.read_body();
				local reqBody = ngx.req.get_body_data();
			    -- 开始验签
                local key = "A7409BB67B472E6CC7EF17C49784A6B8";
                local digest = ngx.encode_base64(ngx.hmac_sha1(key, reqBody));
                if (digest ~= token) then
                    ngx.status = 403;
                    ngx.say(string.format("{\"success\":false,\"msg\":\"%s\"}", "验签失败"));
                    return ngx.exit(403);
                end
			}
			proxy_pass http://localhost/backend;
		}

		location /backend {
		    default_type "application/json";
		    content_by_lua_block {
		        ngx.say(string.format("{\"success\":true,\"msg\":\"%s\"}", "校验通过"));
                return ngx.exit(200);
		    }
		}
    }
  • nginx -s reload后测试
    在这里插入图片描述
    在这里插入图片描述