API网关-Kong-AES加密插件编写

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即可,(无需关注

实现:

  1. 首先了解一下Kong的插件结构,以及使用方式
    Kong 自定义插件

├── INSTALL.txt
├── README.md
├── kong
│   └── plugins
│       └── 
│           ├── handler.lua
│           └── schema.lua
└── -.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/
  1. 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
}
  1. 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
  1. 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插件

奉上github代码

初来乍到,有错误的地方欢迎指点,感谢~

你可能感兴趣的:(API网关-Kong-AES加密插件编写)