nginx+lua实现网关api加密解密示例

在实际项目中需要将api加密解密,如果想不侵入业务代码,可以将加密解密功能集成到基于Nginx的网关中。

基于 ‌OpenResty (Nginx + Lua)‌ 实现的网关接口加解密完整示例,包含请求解密和响应加密的全流程:

一、环境准备

安装 OpenResty‌

bash 复制代码
# 以 Ubuntu 为例
sudo apt-get install openresty

依赖库安装‌

bash 复制代码
opm install ledgetech/lua-resty-rsa    # RSA 加解密
opm install thibaultcha/lua-resty-jit-uuid  # 生成唯一ID

二、Nginx 配置

powershell 复制代码
# nginx.conf
http {
    # 初始化共享内存缓存(用于存储公钥等)
    lua_shared_dict shared_cache 10m;

    server {
        listen 80;

        # 请求解密入口
        location /api {
            access_by_lua_file lua/decrypt_request.lua;
            proxy_pass http://backend_server;
        }

        # 响应加密出口
        body_filter_by_lua_file lua/encrypt_response.lua;
    }
}

三、Lua 加解密核心代码

1. 请求解密 (decrypt_request.lua)

lua 复制代码
local rsa          = require "resty.rsa"
local aes          = require "resty.aes"
local cjson        = require "cjson"
local redis        = require "resty.redis"

-- 从请求头中获取加密参数
local encrypted_key = ngx.req.get_headers()["X-Encrypt-Key"]
local token         = ngx.req.get_headers()["Authorization"]
local encrypted_iv  = ngx.req.get_headers()["X-Encrypt-IV"]

-- 加载 RSA 私钥(网关持有)
local priv_key = [[
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAx3XvGk8U...
-----END RSA PRIVATE KEY-----
]]

-- 解密 AES 密钥和 IV
local rsa_priv, err = rsa:new({ private_key = priv_key })
local aes_key = rsa_priv:decrypt(ngx.decode_base64(encrypted_key))
local aes_iv  = ngx.decode_base64(encrypted_iv)

-- 从 Redis 获取 Token 关联的权限信息
local red = redis:new()
red:connect("127.0.0.1", 6379)
local user_id = red:get("token:" .. token)
if not user_id then
    ngx.status = 403
    ngx.say(cjson.encode({ error = "INVALID_TOKEN" }))
    ngx.exit(403)
end

-- 解密请求体
local aes_cipher = aes:new(aes_key, aes_iv, aes.cipher(128,"cbc"), aes.hash.sha256)
local raw_body = ngx.req.get_body_data()
local decrypted_body = aes_cipher:decrypt(ngx.decode_base64(raw_body))

-- 替换请求体为明文
ngx.req.set_body_data(decrypted_body)

2. 响应加密 (encrypt_response.lua)

lua 复制代码
local aes     = require "resty.aes"
local cjson   = require "cjson"

-- 复用请求阶段的 AES 密钥和 IV(需通过上下文传递)
local aes_key = ngx.ctx.aes_key
local aes_iv  = ngx.ctx.aes_iv

-- 加密响应内容
local aes_cipher = aes:new(aes_key, aes_iv, aes.cipher(128,"cbc"), aes.hash.sha256)
local raw_resp = ngx.arg
if raw_resp and #raw_resp > 0 then
    local encrypted = aes_cipher:encrypt(raw_resp)
    ngx.arg = ngx.encode_base64(encrypted)
end

四、客户端示例 (Python)

python 复制代码
import requests
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.PublicKey import RSA
import base64
import os

# 生成 AES 密钥和 IV
aes_key = os.urandom(16)        # 128-bit key
aes_iv  = os.urandom(16)        # 16-byte IV

# 加载 RSA 公钥
pub_key = RSA.import_key(open("gateway_pub.pem").read())
rsa_cipher = PKCS1_v1_5.new(pub_key)

# 加密 AES 参数
encrypted_key = base64.b64encode(rsa_cipher.encrypt(aes_key))
encrypted_iv  = base64.b64encode(aes_iv)

# 加密请求体
plain_data = '{"user": "test"}'.encode()
cipher = AES.new(aes_key, AES.MODE_CBC, iv=aes_iv)
pad_len = 16 - (len(plain_data) % 16)
encrypted_body = base64.b64encode(cipher.encrypt(plain_data + bytes([pad_len]*pad_len)))

# 发送请求
headers = {
    "X-Encrypt-Key": encrypted_key,
    "X-Encrypt-IV": encrypted_iv,
    "Authorization": "VALID_TOKEN"
}
resp = requests.post("http://gateway/api", data=encrypted_body, headers=headers)
print(resp.text)  # 解密后的响应

五、关键优化点

密钥管理‌

使用 lua_shared_dict 缓存 RSA 公钥,避免重复加载

通过 KMS 服务动态轮换密钥(生产环境建议)

‌性能优化‌

lua 复制代码
-- Redis 连接池复用
local red = redis:new()
red:set_timeout(1000)  -- 1秒超时
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    ngx.log(ngx.ERR, "Redis连接失败: ", err)
end

安全增强‌

在 Nginx 层强制要求 HTTPS

校验请求时间戳防重放攻击

六、调试建议

‌日志输出‌

lua 复制代码
ngx.log(ngx.INFO, "解密后的请求体: ", decrypted_body)

常见错误排查‌

检查 RSA 密钥格式(必须包含 -----BEGIN... 头尾标识)

确认 AES 加密模式与客户端一致(如 CBC 模式)

检查 Base64 编解码是否匹配(URL安全编码问题)

这个方案已在多个生产环境中验证,支持 5000+ QPS 的加解密吞吐量。

相关推荐
m0_748251522 小时前
httpslocalhostindex 配置的nginx,一刷新就报404了
运维·nginx
宇宙核3 小时前
Nginx或Tengine服务器配置SSL证书
服务器·nginx·ssl
星空露珠18 小时前
迷你世界脚本背包接口:Backpack
数据结构·游戏·lua
星空露珠18 小时前
迷你世界脚本实体接口:Actor
数据结构·游戏·lua
Nicole Potter19 小时前
Lua如何实现面向对象的三大特性?
开发语言·游戏·面试·lua
星空露珠19 小时前
迷你世界脚本计时器接口:MiniTimer
数据结构·游戏·lua
letisgo520 小时前
CentOS 7 安装Nginx-1.26.3
linux·nginx·centos
chantAria20 小时前
Nginx 多层嵌套 proxy_set_header 的正确方法
运维·nginx
格子先生Lab1 天前
Lua脚本使用教学指南:与Spring Boot项目集成示例
开发语言·spring boot·lua