实现签名验证插件

一)针对sign_auth插件目录下的handler.lua进行改造

---------------------handler.lua-----------------------

local utils = require("fago.common.utils")
local BasePlugin = require("fago.plugins.base_plugin")
local redis = require "resty.redis"  --引入redis模块

local function close_redis(red)  
    if not red then  
        return
    end  
    --释放连接(连接池实现)  
    local pool_max_idle_time = 10000 --毫秒  
    local pool_size = 100 --连接池大小  
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)  
    if not ok then  
        utils.error_log("set keepalive error : "..err)  
    end  
end

--检验请求的sign签名是否正确
--params:传入的参数值组成的table
--secret:项目secret,根据appid找到secret
local function signcheck(params,secret)
    --判断参数是否为空,为空报异常
    if utils.isTableEmpty(params) then
        local mess="params table is empty"
        utils.error_log(mess)
        return false,mess
    end
    
    --判断是否有签名参数
    local sign = params["sign"]
    if sign == nil then
        local mess="params sign is nil"
        utils.error_log(mess)
        return false,mess
    end
    
    --是否存在时间戳的参数
    local timestamp = params["time"]
    if timestamp == nil then
        local mess="params timestamp is nil"
        utils.error_log(mess)
        return false,mess
    end
    --时间戳有没有过期,10秒过期
    local now_mill = ngx.now() * 1000 --毫秒级
    if now_mill - timestamp > 10000 then
        local mess="params timestamp is 过期"
        utils.error_log(mess)
        return false,mess
    end
    
    local keys, tmp = {}, {}

    --提出所有的键名并按字符顺序排序
    for k, _ in pairs(params) do 
        if k ~= "sign" then --去除掉
            keys[#keys+1]= k
        end
    end
    table.sort(keys)
    --根据排序好的键名依次读取值并拼接字符串成key=value&key=value
    for _, k in pairs(keys) do
        if type(params[k]) == "string" or type(params[k]) == "number" then 
            tmp[#tmp+1] = k .. "=" .. tostring(params[k])
        end
    end
    --将salt添加到最后,计算正确的签名sign值并与传入的sign签名对比,
    local signchar = table.concat(tmp, "&") .."&"..secret
    local rightsign = ngx.md5(signchar);
    if sign ~= rightsign then
        --如果签名错误返回错误信息并记录日志,
        local mess="sign error: sign,"..sign .. " right sign:" ..rightsign.. " sign_char:" .. signchar
        utils.error_log(mess)
        return false,mess
    end
    return true
end

local SignAuthHandler = BasePlugin:extend()
SignAuthHandler.PRIORITY = 0

function SignAuthHandler:new()
    SignAuthHandler.super.new(self, "sign_auth-plugin")
    utils.debug_log("===========SignAuthHandler.new============");
end

function SignAuthHandler:access()
    SignAuthHandler.super.access(self)
    utils.debug_log("===========SignAuthHandler.access============");
    local params = {}

    local get_args = ngx.req.get_uri_args();
    
    local appid = get_args["appid"];
    
    if appid == nil then
        ngx.say("appid is empty,非法请求");
        return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
    end
    
    ngx.req.read_body()
    local post_args = ngx.req.get_post_args();

    utils.union(params,get_args)
    params = utils.union(params,post_args)
    
    local red = redis:new()  --创建一个对象,注意是用冒号调用的
    
    --设置超时(毫秒)  
    red:set_timeout(1000) 
    --建立连接  
    local host = "192.168.31.247"  
    local port = 6379
    local ok, err = red:connect(host, port)
    if not ok then  
        close_redis(red)
        utils.error_log("Cannot connect");
        return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)   
    end  
    
    --得到此appid对应的secret
    local resp, err = red:hget("apphash",appid)
    if not resp or (resp == ngx.null) then  
        close_redis(red)
        return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 获取值失败
    end 
    --resp存放着就是appid对应的secret       
    local checkResult,mess = signcheck(params,resp)

    if not checkResult then
        ngx.say(mess);
        return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
    end
end

return SignAuthHandler;

二)主入口fago.lua中access阶段进行改造

​三)在utils工具类加入对table的操作

启动nginx,利用之前编写的java代码,模拟请求

Last updated

Was this helpful?