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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM