openresty+lua做接口调用权限限制
Posted 巴八灵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了openresty+lua做接口调用权限限制相关的知识,希望对你有一定的参考价值。
说明:openresty可以理解为一个服务器它将nginx的核心包含了过来,并结合lua脚本语言实现一些对性能要求高的功能,该篇文章介绍了使用openresty
1.purview.lua
--调用json公共组件 cjson = require("cjson") fun = require("ttq.fun") -- 引用公用方法文件 conf = require("ttq.ini") --引用配置文件 reds = require("ttq.redis_pool") --引用redis连接池 mysqld = require("ttq.mysql_pool") --引用mysql连接池 --参数校验 check_arg = fun:check_post_arg() --调用参数校验方法 arg_tables = {} --存储post的参数信息 if check_arg[\'status\'] ==0 then --参数校验通过,获取返回的参数,并将参数拼接 arg_tables= check_arg[\'arg_tables\'] get_info = string.format("%s:%s:%s",arg_tables[\'appid\'],arg_tables[\'ip\'],arg_tables[\'appkey\']) else ngx.say(fun:resJson(-1,check_arg[\'msg\'])) return; end --1.首先通过redis查找 --2.没有找到再找数据库 --3.根据appid查询项目是否授权 --4.项目获取权限成功,再查询ip是否被限制了 local res,err,value = reds:get_key(get_info) if not res then ngx.say(fun:resJson(-1,err)) return end if value == ngx.null then --redis数据未空,根据appid查询,查询信息是否一致 local sql_appid = string.format("select * from ttq_appid_list where appid= \'%s\' and appkey=\'%s\' limit 1 ",arg_tables[\'appid\'],arg_tables[\'appkey\']) local res,msg,result = mysqld:query(sql_appid) --连接失败报错 if not res then ngx.say(fun:resJson(-1,msg)) end --未查找数据报错 if table.maxn(result)== 0 then ngx.say(fun:resJson(-1,\'appid验证失败,被拦截\')) return end --项目权限获取成功,需要验证ip是否被允许 local sql = string.format("select * from ttq_appid_white_list where appid=\'%s\' and ip= \'%s\' limit 1 ",arg_tables[\'appid\'],arg_tables[\'ip\']) res,msg,result = mysqld:query(sql) if table.maxn(result)==0 then ngx.say(fun:resJson(-1,\'该项目,非法操作或没有授予权限,被拦截\')) return end --所有验证通过,最后写入redis缓存 ok, err = reds:set_key(get_info,1) ngx.say(fun:resJson(0,\'该项目鉴权成功,可以访问\')); return end --3.redis找到了信息鉴权成功 ngx.say(fun:resJson(0,"该项目鉴权成功,可以访问!"))
2.ini.lua
--配置相关方法 local _CONF = {} --返回redis配置文件 function _CONF.redis() local redis_config = {host=\'127.0.0.1\',pass=\'123456\',port=6379} --redis配置项 return redis_config end --返回mysql配置文件 function _CONF.mysql() local mysql_config = {host=\'127.0.0.1\',port=3306,database=\'test\',user=\'root\',password=\'123456\'} --mysql的配置项 return mysql_config end return _CONF
3.mysql_pool.lua
--连接mysql local mysql = require "resty.mysql" local mysql_pool = {} function mysql_pool:get_connect() if ngx.ctx[mysql_pool] then return true,\'返回mysql连接池成功\',ngx.ctx[mysql_pool] end local db, err_mysql = mysql:new() if not db then return false,"failed to instantiate mysql" end db:set_timeout(1000) -- 1 sec local ok, err_mysql, errno, sqlstate = db:connect{ host = conf.mysql()[\'host\'], port = conf.mysql()[\'port\'], database = conf.mysql()[\'database\'], user = conf.mysql()[\'user\'], password = conf.mysql()[\'password\'], max_packet_size = 1024 * 1024 } if not ok then --ngx.say(fun.resJson(-1,"mysql connect failed")) return false,"mysql conncet failed" end --存储mysql连接池并返回 ngx.ctx[mysql_pool] = db return true,\'mysql连接成功\',ngx.ctx[mysql_pool] end --关闭mysql连接池 function mysql_pool:close() if ngx.ctx[mysql_pool] then ngx.ctx[mysql_pool]:set_keepalive(60000, 1000) ngx.ctx[mysql_pool] = nil end end --执行sql查询 function mysql_pool:query(sql) --ngx.say(sql) local ret,msg,client = self:get_connect() --连接数据库失败,返回错误信息 if not ret then return false,msg end --连接成功后执行sql查询,执行失败返回错误信息 local res,errmsg,errno,sqlstate = client:query(sql) --self:close() if not res then return false,errmsg end --ngx.say(res[1][\'appid\']) --ngx.say(res[1][\'ip\']) --执行成功,返回信息 return true,"查询信息成功",res end return mysql_pool
4.redis_pool.lua
local redis = require("resty.redis") local redis_pool = {} --连接redis function redis_pool:get_connect() if ngx.ctx[redis_pool] then return true,"redis连接成功",ngx.ctx[redis_pool] end local red = redis:new() red:set_timeout(1000) -- 1 sec local ok, err = red:connect(conf.redis()[\'host\'],conf.redis()[\'port\']) if not ok then return false,"failed to connect redis" end --设置redis密码 local count, err = red:get_reused_times() if 0 == count then ok, err = red:auth(conf.redis()[\'pass\']) if not ok then return false,"redis failed to auth" end elseif err then return false,"redis failed to get reused times" end --选择redis数据库 ok, err = red:select(0) if not ok then return false,"redis connect failed " end --建立redis连接池 ngx.ctx[redis_pool] = red return true,\'redis连接成功\',ngx.ctx[redis_pool] end --关闭连接池 function redis_pool:close() if ngx.ctx[redis_pool] then ngx.ctx[redis_pool]:set_keepalive(60000, 300) ngx.ctx[redis_pool] = nil end end ---获取key的值 function redis_pool:get_key(str) local res,err,client = self:get_connect() if not res then return false,err end local keys = client:get(str) --self:close() return true,"获取key成功",keys end --设置key的值 function redis_pool:set_key(str,value) local res,err,client = self:get_connect() if not res then return false,err end client:set(str,value) --self:close() return true,"成功设置key" end return redis_pool
5.fun.lua
local _M = {} --返回json信息公用方法 function _M:resJson(status,mes) local arr_return = {} arr_return[\'status\'] = status arr_return[\'msg\'] = mes return cjson.encode(arr_return) end --检测post过来的参数合法性 function _M:check_post_arg() local rule_count = 3 --接收POST过来的数据 ngx.req.read_body() local arg = ngx.req.get_post_args() local arg_count = 0 --存储参数个数 local arg_table = {appid,ip,appkey} local get_info --参数拼接字符串,方便redis操作 --遍历post过来的参数 for k,v in pairs(arg) do arg_count = arg_count+1 arg_table[k] = v end --参数赋值 appid = arg_table[\'appid\'] ip = arg_table[\'ip\'] appkey = arg_table[\'appkey\'] --判断参数个数传递过来的参数要与规定的个数一致 if rule_count == arg_count then if string.len(appid) == 0 then return {status=-1,msg=\'参数传递错误,被拦截\'} end if string.len(ip) == 0 then return {status=-1,msg=\'参数传递错误,被拦截\'} end if string.len(appkey) == 0 then return {status=-1,msg=\'参数传递错误,被拦截\'} end ---参数正确返回参数信息 return {status=0,msg=\'参数校验成功\',arg_tables=arg_table} else return {status=-1,msg=\'参数传递错误,被拦截\'} end end return _M
6.配置nginx.conf文件
上面的lua文件都是放在/data/local/openresty/lualib/ttq/目录下
location /lua{
lua_code_cache on;
content_by_lua_file /data/local/openresty/lualib/ttq/purview.lua;
}
7.mysql数据设计
SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `ttq_appid_list`; CREATE TABLE `ttq_appid_list` ( `id` int(4) unsigned NOT NULL AUTO_INCREMENT, `appid` varchar(20) NOT NULL DEFAULT \'appid相当于项目名称\', `appkey` varchar(20) NOT NULL COMMENT \'appid密码\', `create_time` int(11) NOT NULL COMMENT \'生产appid时间\', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\'项目appid对应关系表\'; DROP TABLE IF EXISTS `ttq_appid_white_list`; CREATE TABLE `ttq_appid_white_list` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `appid` char(20) NOT NULL COMMENT \'项目标示或名称\', `ip` varchar(15) NOT NULL COMMENT \'项目允许访问对应的ip地址\', PRIMARY KEY (`id`) ) ENGINE=InnoDB
8.访问方法
<form action="http://192.168.3.128:8083/lua" method="post">
<input type="text" name="appid" value=\'ttq\'></input>
<input type="text" name="ip" value=\'192.168.3.2\'></input>
<input type="text" name="appkey" value=\'67872\'></input>
<input type="submit" value="鉴权check"></input>
</form>
9.压力测试
压力测试效果非常可观
qps可以达到2225
代码下载:
我的github代码地址:https://github.com/lisiqiong/learning/tree/master/luacode
以上是关于openresty+lua做接口调用权限限制的主要内容,如果未能解决你的问题,请参考以下文章
Openresty之resty.limit.conn模块介绍
Nginx视频教程 反向代理/https/openresty/lua实战
openresty+consul动态配置更新(服务变更发现)