2018年10月29日
kong version: 0.14.1
需求:
最近开始使用了一个API网关软件 Kong,可实现登录认证、监控、权限控制等内容。
在使用的过程中约到了一个问题,同一个前端应用请求了api分别由两个服务提供,两个服务有公用的user_id,
但是又有不同的认证方式,感觉好麻烦,但是又必须搞
如图:
分析:
1)客户端携带由B的consumer信息加工而成的jwt,请求B
2)B 解密JWT产生consumer-id、customer-id(对应应用中的user id),放入请求头中
3)请求C应用时需要将customer-id进行aes加密,并放入header中,(需要Kong 插件处理
4)请求D应用时只需customer-id即可,(无需关注
实现:
- 首先了解一下Kong的插件结构,以及使用方式
Kong 自定义插件
<plugin-name>
├── INSTALL.txt
├── README.md
├── kong
│ └── plugins
│ └── <plugin-name>
│ ├── handler.lua
│ └── schema.lua
└── <plugin-name>-<version>.rockspec
使用docker-compose启动Kong
kong:
image: kong
environment:
// 配置插件地址及名称
KONG_LUA_PACKAGE_PATH: /kong-plugins/?.lua;;
KONG_CUSTOM_PLUGINS: aes256_encrypt
volumes:
// 挂载插件文件
- ./kong/:/kong-plugins/kong/
- schema.lua
配置启动插件时所需要的参数,并进行校验
return {
no_consumer = true,
fields = {
-- 加密的密码配置
secret = {type = "string", default = "", fun = check_secret},
-- 匿名配置
anonymous = {type = "string", default = "", func = check_user},
},
self_check = function(schema, plugin_t, dao, is_updating)
-- perform any custom verification
return true
end
}
- handler.lua
具体的处理过程
local singletons = require "kong.singletons"
local responses = require "kong.tools.responses"
local BasePlugin = require "kong.plugins.base_plugin"
local aes_cipher = require "kong.plugins.aes256_encrypt.aes_cipher"
-- header的读取
local ngx_set_header = ngx.req.set_header
-- header的设置
local ngx_get_header = ngx.req.get_headers
local Aes256Encrypt = BasePlugin:extend()
Aes256Encrypt.VERSION = "0.1.0"
-- PRIORITY 越大执行顺序月靠前
-- jwt 1005,放到jwt后面执行
Aes256Encrypt.PRIORITY = 1000
-- 请求时的处理过程
function Aes256Encrypt:access(config)
Aes256Encrypt.super.access(self)
-- 使用了 aes-256-cbc加密
local aes_type = 'aes-256-cbc'
local password = config.secret
local anonymous = config.anonymous
local authorization = ngx_get_header()['authorization']
local consumer_customer_id = ngx_get_header()['x-consumer-custom-id']
-- 请求时没有认证信息,但是有匿名设置
-- 将得到 consumer_customer_id,供下一步使用
if authorization == nil then
if anonymous ~= nil then
local result, err = singletons.db.consumers:select { id = anonymous }
if result ~= nil then
consumer_customer_id = result.custom_id
ngx_set_header('x-consumer-custom-id', result.custom_id)
ngx_set_header('x-consumer-username', result.username)
ngx_set_header('x-consumer-id', result.id)
ngx_set_header('x-anonymous-consumer', true)
else
return responses.send(403, "missing authorization error anonymous")
end
else
return responses.send(403, "missing authorization none anonymous")
end
end
-- 得到 consumer_customer_id
-- 继续加密处理
if consumer_customer_id ~= nil then
local encrypted_str = aes_cipher.encrypt(aes_type, password, consumer_customer_id)
ngx_set_header('authorization', 'Bearer'..' '..encrypted_str)
end
-- Implement any custom logic here
end
-- container name aes256_kong_1
-- padding PKCS5Padding
- cipher.lua
调用了一个openssl库-luaossl,自己实现有点难,
这个库找了好久,可能用lua的人太少了吧,我就是头一次用🤣🤣🤣
突然发现nodejs一大堆的库还是挺好用的
local rand = require('openssl.rand')
local cipher = require('openssl.cipher')
function binary_2_hex(str)
return (str:gsub('.', function (c)
return string.format('%02X', string.byte(c))
end))
end
function hex_2_binary(str)
return (str:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end))
end
function encrypt(type, pass, text)
local iv = rand.bytes(16)
return binary_2_hex(iv..cipher.new(type):encrypt(pass, iv):final(text))
end
function decrypt(type, pass, encrypted)
local iv = hex_2_binary(encrypted:sub(0 + 1, 31 + 1))
local string = hex_2_binary(encrypted:sub(32 + 1))
return cipher.new(type):decrypt(pass, iv):final(string)
end
export_module = {}
export_module.encrypt = encrypt
export_module.decrypt = decrypt
return export_module
启动测试
使用docker镜像启动,安装方便
启动kong以及对应的UI
docker-compose up -d
启动一个简单的node服务查看加密结果
node server.js
localcalhost:1337 进行配置和测试
需要配置路由、添加使用jwt、aes256_encrypt插件
初来乍到,有错误的地方欢迎指点,感谢~