网关kong自定义的插件开发攻略

如果你对网关kong是一片空白,建议先看我写的安装部署方案,

https://blog.csdn.net/fj56355113/article/details/107613497

开发初期需要对网关插件的原理有一些了解:

1、OpenResty:OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。具体查阅网站:http://openresty.org/cn/

2、Lua:是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。不要说你不会,哥也就花了1~2天的时候上车了。

3、Nginx:nginx我不想多说了,我只想说kong其实是在nginx的基础上做了一些API的管理,然后开放了一些不错的插件;

4、constants.lua:kong安装完成后,会默认在/usr/local/share/lua/5.1/kong路径下把核心的插件初始化,该constants.lua是定义所有插件的主入口,我们新增一个插件都要在这个文件中声明变量;

插件具体位置在/usr/local/share/lua/5.1/kong/plugins

本次举例采用插件名为extend-auth为例,所以在plugins中创建extend-auth文件夹,以下为详细举例:

插件的主要构成部分有handler.lua、schema.lua,这是游戏规则,不要问为什么。

1、schema.lua是插件在使用时配定义参数的主要脚本文件

例如:konga中找到自己的插件,需要使用时,

以下每一个参数都是要定义出来,具体看代码:

schema.lua详细代码如下:

-- extend-auth.schema.lua
local typedefs = require "kong.db.schema.typedefs"

return {
  name = "extend-auth",
  fields = {
    { consumer = typedefs.no_consumer },
    { config = {
        type = "record",
        fields = {
          { token_source_enums = { type = "set", elements = { type = "string" } } },
          { check_ip_url = typedefs.url({ required = true }) },
          { base_code = { type = "string", required = true, default = "DATA_CENTER" } },
          { app_key = { type = "string", required = true } },
          { secret_key = { type = "string", required = true } },
          { ext_url_path_enums = { type = "set", elements = { type = "string" } } }
        }, 
      },
    },
  }
}

2、handler.lua是插件被使用后,请求拦截后进入的主要方法;

local BasePlugin = require "kong.plugins.base_plugin"
local jwt_decoder = require "kong.plugins.extend-auth.jwt_parser"
local authHttp = require "kong.plugins.extend-auth.auth_http"

-- 字符串拆分函数
function Split(szFullString, szSeparator)
  local nFindStartIndex = 1
  local nSplitIndex = 1
  local nSplitArray = {}
  while true do
    local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex)
    if not nFindLastIndex then
      nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString))
      break
    end
    nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1)
    nFindStartIndex = nFindLastIndex + string.len(szSeparator)
    nSplitIndex = nSplitIndex + 1
  end
  return nSplitArray
end

local CustomHandler = BasePlugin:extend()

CustomHandler.VERSION = "1.0.0"
CustomHandler.PRIORITY = 10

function CustomHandler:new()
  CustomHandler.super.new(self, "extend-auth")
end
function CustomHandler:access(config)
  -- 第一步:URL在配置范围内跳过校验(针对登录或者其它非授权页面)
  local urlPath = kong.request.get_path()
  if config.ext_url_path_enums ~= nil then
    for _, v in ipairs(config.ext_url_path_enums) do
      if v == urlPath then
        return
      end
    end
  end
  -- 第二步:获取Cookies中【tokenPre】令牌来源值
  local tokenPre = ngx.var.cookie_tokenPre
  -- 第三步:解析令牌来源,获取值如果是非内部令牌,非内部系统的范围值通过kong配置的枚举,跳过下面所有逻辑,由各个业务集成认证包去校验
  if tokenPre == nil then
    return kong.response.exit(401, "【tokenPre】不在设定范围内NULL", { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
  if tokenPre ~= "inside" and config.token_source_enums ~= nil then
    for _, v in ipairs(config.token_source_enums) do
      if v == tokenPre then
        return
      end
    end
    return kong.response.exit(401, "【tokenPre】不在设定范围内", { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
  -- 第四步:获取Cookies中【accessToken】访问令牌值
  local accessToken = ngx.var.cookie_accessToken
  if accessToken == nil then
    return kong.response.exit(401, "【accessToken】认证令牌失效", { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
  -- 第五步:解析访问令牌获取user_id,拿到user_id的值,该值的组成结构【INSIDE_B_819554】,如果是以【INSIDE_B_】开头则不进行URL校验逻辑
  local jwtAccessToken, err = jwt_decoder:new(accessToken)
  if err ~= nil then
    return kong.response.exit(401, "【accessToken】认证令牌失效,原因:" .. err, { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
  if jwtAccessToken.claims == nil or jwtAccessToken.claims.user_id == nil or 
     jwtAccessToken.claims.client_id == nil or jwtAccessToken.claims.expires_in == nil then
    return kong.response.exit(401, "【accessToken】认证令牌信息为空", { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
  local accessToken = jwtAccessToken.claims
  local clientId = accessToken.client_id
  local expiresIn = accessToken.expires_in
  local userArray = Split(accessToken.user_id, "_")
  if userArray ~= nil and userArray[1] ~= nil and userArray[2] ~= nil and userArray[3] ~= nil then
    if userArray[1] == "SEGI" and userArray[2] == "B" then
      -- 第六步:获取当前URL,问号以后的直接剔除
      local userId = userArray[3]
      -- 第七步:用user_id和URL去请求数据中心开放出来的API接口
      local checkIpUrl = config.check_ip_url
      local requestString = "{\"data\":{\"userId\":" .. userId .. ", \"url\":\"" .. urlPath .. "\", \"nOrgId\":0, \"channel\":1 }}"
      local respData, httpErr = authHttp.http_post_client(checkIpUrl, requestString, 5000)
      if httpErr ~= nil then
        return kong.response.exit(401, "【URL鉴权】校验异常" .. httpErr .. ",原因:" .. err, { ["Content-Type"] = "application/json;charset=UTF-8" } )
      else
        if respData ~= "0" then
          return kong.response.exit(401, "【URL鉴权】校验不通过respData:" .. respData, { ["Content-Type"] = "application/json;charset=UTF-8" } )
        end
      end
    end
  else
    return kong.response.exit(401, "【accessToken】认证令牌解析有误", { ["Content-Type"] = "application/json;charset=UTF-8" } )
  end
end

return CustomHandler

该实现的逻辑是根据我这边业务场景而写,里面具体内容可以不用关注,主要是给大家关注,kong的自定义插件怎么写。

local BasePlugin = require "kong.plugins.base_plugin"    # kong的基础类,所有自定义插件都是这样引入
local jwt_decoder = require "kong.plugins.extend-auth.jwt_parser"  # 为jwt解析类,可自己百度去找
local authHttp = require "kong.plugins.extend-auth.auth_http"         # 封装的HTTP请求第三方服务校验的客户端方法

注意几点:

1、每次插件写完后,需要重启kong,重启命令: kong restart -c /etc/kong/kong.conf --vv

2、如果插件中有语法错误,重启会失败,可以查阅日志找问题,日志位置在/usr/local/kong/logs/error.log

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值