热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Kong插件开发向导

Kong插件开发向导转载李亚飞大佬的文章:https:www.lyafei.com简介前面洋洋洒洒写了那么多文章,Kong搭建、Konga搭建、Kong插件开发工具包、Lua算法实


转载 李亚飞 大佬的文章:https://www.lyafei.com/



简介

前面洋洋洒洒写了那么多文章,Kong搭建、Konga搭建、Kong插件开发工具包、Lua算法实现等等,就为了这篇Kong插件开发铺垫,在进一步讨论之前,有必要再简要阐述下 Kong 是如何构建的,特别是它如何与 Nginx 集成,以及它与 Lua 脚本之间的关系。

使用 lua-nginx-module 模块可以在 Nginx 中启用 Lua 脚本功能,Kong 与 OpenResty 一起发布,OpenResty 中已经包含了 lua-nginx-module 模块,OpenResty 不是 Nginx 的分支,而是一组扩展 Nginx 功能的模块。

因此,Kong 是一个 Lua 应用程序,旨在加载和执行 Lua 模块(我们通常称之为”插件”),并且 Kong 还为此提供了整套开发环境,包括 SDK、数据库抽象、数据迁移等等。

插件由 Lua 模块组成,用户可以使用插件开发包(又称PDK),通过调用请求响应或者流交互实现各种功能,PDK 是一组 Lua 方法,插件可以使用它来促进 Kong 核心模块(或其它组件)与插件本身交互。

有关 PDK 的详情,请详见我的另一篇文章

https://www.lyafei.com/archives/278/

文件结构


简介

插件其实是一组 Lua 模块,本章中描述的每个文件都可以视为一个单独的模块,如果它们的命名遵循某个约定,Kong 就会检测并加载该插件模块

kong.plugins..

用户定义的插件模块需要通过 package.path 变量访问到,用户可以更改 lua_package_path 配置调整这个值,然而,安装插件的首选方法是通过 LuaRocks,它与 Kong 天然集成,有关 LuaRocks 安装插件的详情,请参考后面的章节。

为了让 Kong 意识到哪些插件需要安装,用户必须将它们添加到配置文件中 plugins 属性中,格式是以逗号分隔的列表,例如:

plugins = bundled,my-custom-plugin # your plugin name here

或者,用户不想加载任何预捆绑的插件:

plugins = my-custom-plugin # your plugin name here

现在,Kong 会试图从以下列命名空间中加载 Lua 模块

kong.plugins.my-custom-plugin.

其中一些模块是必需的(例如:handler.lua),有些是可选的,以允许插件实现一些额外的功能(例如:api.lua 可以扩展 Admin API 端点)

基础插件模块

最基础的插件,必需包含两个模块

lua-plugin
├── handler.lua
└── schema.lua


  • handler.lua:插件的核心,它是需要实现的接口,其中每个方法会在请求/连接的生命周期中运行

  • schema.lua:插件可能需要保留一些用户输入的配置,此模板定义一些规则保存配置的模式,以便用户只能输入有效的配置项


高级插件模块

有些插件与 Kong 之间有更深入的集成,比如在数据库中存数据,在 Admin API 中公开端点等等,每个插件都可以通过向插件添加新模块来完成,插件的结构大致如下

lua-plugin
├── api.lua
├── daos.lua
├── handler.lua
├── migrations
│ ├── init.lua
│ └── base_complete_plugin.lua
└── schema.lua

简要说明如下:




































模块名是否必须描述
api.lua定义 Admin API 中也用的端点列表,与插件自定义的实体进行交互
daos.lua定义数据库访问对象列表
handler.lua一个需要实现的接口,其中每个方法会在请求/连接的生命周期中运行
migrations/*.lua数据源迁移,只有当用户的插件有自定义实体时才需要
schema.lua保存插件的配置项,一边用户只能输入有效的配置值

Key-Auth 插件实现了整套完整的插件接口,可以查看源码了解细节

实现自定义逻辑


简介

Kong 的插件允许用户在整个生命周期的几个切点加入自定义逻辑,为此,必须实现 base_plugin.lua 接口中的一些方法,这些方法在 kong.plugins..handler 模块中实现。

模块

kong.plugins..handler

可用的上下文

插件接口允许用户覆盖 handler.lua 文件中的以下任何方法,在 Kong 的执行生命周期的各个切点实现自定义逻辑:

HTTP Module:为 HTTP / HTTPS 请求编写的插件














































方法名段信息描述
:init_worker()init_worker每个 Nginx worker 进程启动时执行
:certificate()ssl_certificate在 SSL 握手提供证书时执行
:rewrite()rewrite从客户端接收到请求,进入 rewrite 段执行,注意,在这个阶段没有识别服务,也没有消费者介入,只有配置成全局插件才会执行此处理程序
:access()access从客户端接收到请求到被代理到 upstream service 之前执行
:header_filter()header_filter从 upstream service 接收到所有响应头时执行
:body_filter()body_filter针对从 upstream service 接收到的响应体块执行,由于响应以流的形式返回给客户端,超过缓冲区大小的按块进行传输,因此,如果响应体很大,会多次调用这个方法
:log()log最后一个响应字节发送到客户端时执行

Stream Module:为 TCP 流连接编写的插件


























方法名段信息描述
:init_worker()init_worker每个 Nginx worker 进程启动时执行
:preread()preread每个连接执行一次
:log()log每个连接中断执行一次

除了 :init_worker() 方法,每个方法都会携带一个参数,这个参数由 Kong 给出,即插件的配置,这个参数的类型是 Lua table,包含了用户定义的值,格式根据用户定义的插件 schema 格式

handler.lua 格式

handler.lua 文件需要返回一个 table,里面包含了用户希望执行的方法,为了方便起见,这里给大家看一下我自定义的 sign-aes256 加解密插件的示例,代码如下:

local kOng= kong
local pcall = pcall
local get_header = kong.request.get_header
local set_header = kong.service.request.set_header
local set_raw_body = kong.service.request.set_raw_body
local ngx_decode_args = ngx.decode_args
local encode_args = ngx.encode_args
local str_find = string.find
local multipart = require "multipart"
local cjson = require "cjson"
local aes = require("resty.aes_ecb")
local CONTENT_TYPE = "content-type"
local CONTENT_LENGTH = "content-length"
local JSON, MULTI, ENCODED = "json", "multi_part", "form_encoded"
local RequestSignAes256Handler = {}
local function decode_args(body)
if body then
return ngx_decode_args(body)
end
return {}
end
local function parse_json(body)
if body then
local status, res = pcall(cjson.decode, body)
if status then
return res
end
end
end
local function get_content_type(content_type)
if content_type == nil then
return
end
if str_find(content_type:lower(), "application/json", nil, true) then
return JSON
elseif str_find(content_type:lower(), "multipart/form-data", nil, true) then
return MULTI
elseif str_find(content_type:lower(), "application/x-www-form-urlencoded", nil, true) then
return ENCODED
end
end
-- 请求时的处理过程
function RequestSignAes256Handler:access(conf)
local content_type_value = get_header(CONTENT_TYPE)
local content_type = get_content_type(content_type_value)
local params, contenttype
local body = kong.request.get_raw_body()
if content_type == ENCODED then
parameters = decode_args(body)
params = parameters["params"]
elseif content_type == MULTI then
parameters = multipart(body and body or "", content_type_value)
params = parameters:get("params").value
elseif content_type == JSON then
parameters = parse_json(body)
params = parameters["params"]
end
if params == nil then
return kong.response.exit(200, { code = 20000, data = "", msg = "Missing required parameters" })
end
local ecb = aes:new()
local aes_baseparams = ecb:decrypt(conf.key,dec(params) )
if content_type == ENCODED then
parameters["params"] = aes_baseparams
body = encode_args(parameters)
elseif content_type == MULTI then
parameters:delete("params")
parameters:set_simple("params", aes_baseparams)
body = parameters:tostring()
elseif content_type == JSON then
parameters["params"] = aes_baseparams
body = cjson.encode(parameters)
end
set_raw_body(body)
set_header(CONTENT_LENGTH, #body)
end
-- PRIORITY 越大执行顺序越靠前
RequestSignAes256Handler.PRIORITY = 800
RequestSignAes256Handler.VERSION = "1.0.0"
return RequestSignAes256Handler

插件配置


简介

大多数情况下,插件的配置可以满足用户的需求,插件的配置存储在数据库中,当插件运行时,Kong 在数据库中检索出它们,并将其传递给 handler.lua 方法
配置在 Kong 中由 Lua table 组成,我们称之为 schema,用户通过 Admin API 启用插件时,以键值对的形式输入参数,Kong 提供了验证用户插件配置的方法,当用户向 Admin API 发送请求启用或更新给定 Service、Route 或 Consumer 上的插件时,Kong 会根据用户定义的 schema 来验证插件配置,举例,用户执行如下请求:

curl -X POST http://kong:8001/services//plugins -d "name=my-custom-plugin" -d "config.foo=bar"

如果配置对象的所有属性都验证有效,API 会返回 201 Created,插件将和配置一起存储在数据库中:

{
foo = "bar"
}

如果配置验证不通过,API 会返回 400 Bad Request 和错误信息

模块

kong.plugins..schema

schema.lua 格式

这个模块返回一个 Lua table,其中包含了用户可以配置插件哪些属性,可用的属性包含:


























属性名数据类型描述
namestring插件名称,比如 key-auth
fieldstable字段定义数组
entity_checksfunction校验条件数组

所有插件都默认继承的属性:





























































属性名数据类型描述
idstring自动生成的插件 Id
namestring插件名称,比如 key-auth
created_atnumber插件配置时间
routetable绑定的路由
servicetable绑定的服务
consumertable绑定的消费者
run_onstring插件运行在服务网格上的哪个节点
protocolstable插件运行的协议
tableboolean插件是否生效
tagstable插件的标签

大多数情况下,用户可以使用默认值,或者让用户在启用插件时指定值,以下是一份我自定义插件中写的简单 schema.lua 文件:

local typedefs = require "kong.db.schema.typedefs"
return {
name = "request-sign-aes256",
fields = {
{ cOnsumer= typedefs.no_consumer },
{ protocols = typedefs.protocols_http },
{ cOnfig= {
type = "record",
fields = {
{ key = { type = "string", default = "12345678912345678912345678912345" }, },
},
},
},
},
}

这里罗列了一些常用的属性规则:










































































































规则描述
type属性的类型
required属性是否是必须的
default属性的默认值
elementsarray 或 set 格式的元素类型
keys map格式的 key 元素类型
valuesmap 格式的 value 元素类型
fieldsrecord 格式的元素类型
between校验输入是否在约定的范围之内
eq校验输入是否等于约定值
ne校验输入是否不等于约定值
gt校验输入是否大于约定值
len_eq校验输入字符串长度是否等于约定值
len_min校验输入字符串长度是否大于约定值
len_max校验输入字符串长度是否小于约定值
match校验输入字符串是否匹配约定正则表达式
not_match校验输入字符串是否不匹配约定正则表达式
match_all校验输入字符串是否全部匹配约定正则表达式列表
match_none校验输入字符串是否全部不匹配约定正则表达式列表
match_any校验输入字符串是否匹配约定正则表达式列表中的一个
starts_with校验输入字符串是否以约定值开头
one_of校验输入字符串是否是约定值列表中的一个
contains校验输入字符串列表是否包含约定值
is_regex校验输入字符串是否是合法的正则表达式
custom_validator校验输入是否是标准的 Lua 方法

我的自定义插件 schema.lua 文件比较简单,想要了解上面的一些属性规则具体使用,可以参考 Kong 的自带插件 key-auth 等。

启动我的自定义插件,在插件,Other tab下,有很多我的自定义插件,如下:

在这里插入图片描述

选择在本文示例的 Request Sign Aes256 插件,添加

在这里插入图片描述

在这里插入图片描述

大功告成,所有的请求内容都需要进行 aes256 加密才可,由我的插件解密成明文,再发给原来的服务。


推荐阅读
  • 每种编程语言都有其独特的完成任务的方式,这也说明了为什么有这么多语言可供选择。在JimHall的《不同的编程语言如何完成相同的事情》文章中,他演示了13种不同的语言如何使用不同的语 ... [详细]
  • 本文探讨了Go语言(Golang)的学习价值及其在Web开发领域的应用潜力,包括其独特的语言特性和为什么它是现代软件开发的理想选择。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 随着网络安全威胁的不断演变,电子邮件系统成为攻击者频繁利用的目标。本文详细探讨了电子邮件系统中的常见漏洞及其潜在风险,并提供了专业的防护建议。 ... [详细]
  • 本文介绍了多个关于JavaScript的书籍资源、实用工具和编程实例,涵盖从入门到进阶的各个阶段,帮助读者全面提升JavaScript编程能力。 ... [详细]
  • 分享一个简化版的Silverlight链接图项目:Link Map Simplified
    本文介绍了一个使用Silverlight开发的可视化工具,主要用于展示和操作复杂的实体关系图(Graph)。该工具在犯罪调查系统中得到了广泛应用,帮助用户直观地获取和理解相关信息。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 本文探讨了Java编程的核心要素,特别是其面向对象的特性,并详细介绍了Java虚拟机、类装载器体系结构、Java类文件和Java API等关键技术。这些技术使得Java成为一种功能强大且易于使用的编程语言。 ... [详细]
  • 精选Unity开源项目:UniRx实现响应式编程
    本文介绍了Unity中的响应式编程框架——UniRx,探讨了其在解决异步编程难题中的应用及优势。 ... [详细]
  • 本文详细介绍了如何通过修改Lua源码或使用动态链接库(DLL)的方式实现Lua与C++之间的高级交互,包括如何编译Lua源码、添加自定义API以及在C++中加载和调用Lua脚本。 ... [详细]
  • OpenWrt 是一款高度可定制的嵌入式 Linux 发行版,广泛应用于无线路由器等领域,拥有超过百个预装软件包。本文详细探讨如何在 OpenWrt 上通过 Luci 构建自定义模块,以扩展其功能。 ... [详细]
  • 导读上一篇讲了zsh的常用字符串操作,这篇开始讲更为琐碎的转义字符和格式化输出相关内容。包括转义字符、引号、print、printf的使用等等。其中很多内容没有必要记忆,作为手册参 ... [详细]
  • 本文详细解析 Skynet 的启动流程,包括配置文件的读取、环境变量的设置、主要线程的启动(如 timer、socket、monitor 和 worker 线程),以及消息队列的实现机制。 ... [详细]
author-avatar
hanjing0118
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有