-
Notifications
You must be signed in to change notification settings - Fork 5
04 插件开发
jinhongyang edited this page Apr 4, 2019
·
1 revision
- 这几个阶段的存在,应该是 OpenResty 不同于其他多数 Web 平台编程的最明显特征了。由于 Nginx 把一个请求分成了很多阶段,这样第三方模块就可以根据自己行为,挂载到不同阶段进行处理达到目的。OpenResty 也应用了同样的特性。所不同的是,OpenResty 挂载的是我们编写的 Lua 代码。
-- 加载的插件
local plugins = {}
-- 定义执行流对象
local _M = {}
-- 初始化项目配置
function _M.init(options)
options = options or {}
singletons.config = env
plugins = loading_plugins(enable_plugins)
end
-- 初始化插件配置
function _M.init_worker()
for _, plugin in ipairs(plugins) do
plugin.handler:init_worker()
end
end
-- 请求转发、重定向等操作
function _M.rewrite()
for _, plugin in ipairs(plugins) do
plugin.handler:rewrite()
end
end
-- 请求转发、重定向等操作
function _M.access()
for _, plugin in ipairs(plugins) do
plugin.handler:access()
end
end
-- 响应头部过滤处理
function _M.header_filter()
local ctx = ngx.ctx
for _, plugin in ipairs(plugins) do
plugin.handler:header_filter(ctx)
end
end
-- 响应体过滤处理
function _M.body_filter()
local ctx = ngx.ctx
for _, plugin in ipairs(plugins) do
plugin.handler:body_filter(ctx)
end
end
-- 会话完成后本地异步完成日志处理
function _M.log()
local ctx = ngx.ctx
for _, plugin in ipairs(plugins) do
plugin.handler:log(ctx)
end
end
return _M
- 通过OpenResty的执行阶段,我们在代码中对每个阶段一一映射,这样我们就可以根据我们的需要,在不同的阶段直接完成大部分典型处理了。
- 进入插件目录plugins
- 根据插件名称建立目录(例如:plugins/jwt-auth)
- 插件目录中一般包含两个文件
handler.lua(jwt-auth)
- 插件主处理程序该模块声明需要继承plugins/base_plugin.lua
- 因每个模块的功能可能只在某个或某几个阶段中执行,继承base_plugin后在模块中仅需要声明该模块中需要执行的阶段即可,其余阶段会调用base_plugin中的空阶段
- init_worker 阶段一般用于初始化插件配置
- rewrite 阶段一般用于转发、重定向、缓存等作用
- access 阶段一般用于IP 准入、接口权限等情况处理
- header_filter 阶段一般用于响应头部的过滤处理
- body_filter 阶段一般用于响应体的过滤处理
- log 阶段一般用于会话完成后本地异步完成日志记录
local JwtAuthService = jwt_auth:new()
local JwtAuthHandler = plugin:extend()
function JwtAuthHandler:new()
JwtAuthHandler.super.new(self, "jwt-auth")
end
function JwtAuthHandler:init_worker()
if ngx.worker.id() == 0 then
local ok, err = ngx_timer_at(0, function(premature)
-- 初始化插件配置
JwtAuthService:init_config()
end)
if not ok then
ngx_log(ngx_ERR, "failed to create the timer: ", err)
return
end
end
end
function JwtAuthHandler:access(ctx)
if ctx.api.is_auth == 1 then
local cjson = require "cjson"
local headers = ngx.req.get_headers()
-- 获取项目私钥
local jwtsecret = JwtAuthService:get_config_by_backendname(ctx.backend_name)
if not jwtsecret then
return response:error(401, "Project Secret Undefined"):response()
end
-- 获取令牌信息
local authorization_header = headers["authorization"] or nil
if not authorization_header then
return response:error(401, "Header Token Undefined"):response()
end
-- 获取Header中Token信息
local jwttoken = ngx_re_match(authorization_header, "\\s*[Bb]earer\\s+(.+)")
if not jwttoken then
return response:error(401, "Header Token Format Error"):response()
end
-- 校验签名
local verifyinfo = jwt:verify(tostring(jwtsecret.secret_key), tostring(jwttoken[1]))
if not verifyinfo["verified"] then
return response:error(401, "Unauthorized"):response()
end
-- 负载数据不能为空否则认证失败
local payload = verifyinfo["payload"]
if not payload or not next(payload) then
return response:error(401, "Unauthorized"):response()
end
-- 把负载数据写入Header,方便业务层应用
for key, value in pairs(payload) do
local header_key = string_upper("JWT-" .. string_gsub(key, "_", "-"))
ngx.req.set_header(header_key, value)
end
end
end
return JwtAuthHandler
service.lua(jwt-auth)
- 插件服务模块,主要处理插件中在DB和缓存上的读写操作
local _M = {}
function _M:new()
local instance = {}
instance.db = pool:new()
instance.cachekey = "jwt-auth"
setmetatable(instance, {
__index = self
})
return instance
end
-- 初始化插件配置到缓存中
function _M:init_config()
--读取系统配置规则
local secrets = self.db:query("select project_name, secret_key, secret_alg from jwts")
if secrets and next(secrets) ~= nil then
for _, secret in pairs(secrets) do
local success, error = ngx_cache:set(self.cachekey, secret.project_name, secret)
ngx_log(ngx_DEBUG, string_format("CREATE JWT SECRET [%s] status:%s error:%s", secret.project_name,
success, error))
end
end
end
-- 根据项目标识更新JWT配置
function _M:update_config_by_backendname(backendname)
local status = false
if not backendname then
return status
end
local secrets = self.db:query("select project_name, secret_key, secret_alg from jwts where project_name = ? limit 1",
{ tostring(backendname) })
if secrets and next(secrets) then
local succ, err = ngx_cache:set(self.cachekey, backendname, secrets[1])
if succ then
status = true
end
ngx_log(ngx_DEBUG, string_format("UPDATE JWT SECRET [%s] status:%s error:%s", backendname, succ, err))
end
return status
end
-- 根据项目标识获取JWT配置
function _M:get_config_by_backendname(backendname)
if not backendname or type(backendname) ~= "string" or string_len(backendname) <= 0 then
return nil
end
local jwtconf = ngx_cache:get(self.cachekey, backendname)
if not jwtconf then
local succ = self:update_config_by_backendname(backendname)
if succ then
jwtconf = ngx_cache:get(self.cachekey, backendname)
end
end
return jwtconf
end
return _M