• 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
    
    
    
    --字符串按指定字符拆分公用方法
    function _M:lua_string_split(str, split_char)    
        local sub_str_tab = {}; 
        while (true) do        
            local pos = string.find(str, split_char);  
            if (not pos) then            
                local size_t = table.getn(sub_str_tab)
                table.insert(sub_str_tab,size_t+1,str);
                break;  
                end
        
            local sub_str = string.sub(str, 1, pos - 1);              
            local size_t = table.getn(sub_str_tab)
            table.insert(sub_str_tab,size_t+1,sub_str);
            local t = string.len(str);
            str = string.sub(str, pos + 1, t);   
        end    
        return sub_str_tab;
    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;
    }

    #user  nobody;
    worker_processes  1;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    
    #pid        logs/nginx.pid;
    
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
        #access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        server {
            listen       8083;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;        
            }
    
            #默认读取body
            #lua_need_request_body_on;
            location /lua{
                #default_type text/plain;
                default_type 'text/html';   
                #content_by_lua_file /data/luacode/demo.lua;
                #content_by_lua 'ngx.say("fsjfssfl")';
                # content_by_lua_block{
                #ngx.say("ok");
                #local data = ngx.req.get_body_data()
                #ngx.say("hello",data);
                #}
                lua_code_cache on;
                content_by_lua_file /data/wwwroot/gitwork/learning/luacode/purview.lua;
            }
    
            #error_page  404              /404.html;
    
            # redirect server error pages to the static page /50x.html
            #
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    
    }
    

     7.mysql数据设计

    /*
    Navicat MySQL Data Transfer
    Source Server         : 172.168.6.6
    Source Server Version : 50622
    Source Host           : 172.168.6.6:3306
    Source Database       : ttq_user_center
    Target Server Type    : MYSQL
    Target Server Version : 50622
    File Encoding         : 65001
    Date: 2016-06-21 13:43:41
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `ttq_appid_list`
    -- ----------------------------
    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对应关系表';
    
    -- ----------------------------
    -- Records of ttq_appid_list
    -- ----------------------------
    
    -- ----------------------------
    -- Table structure for `ttq_appid_white_list`
    -- ----------------------------
    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 DEFAULT CHARSET=utf8 COMMENT='项目权限表';
    
    -- ----------------------------
    -- Records of ttq_appid_white_list
    -- ----------------------------
    

     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

  • 相关阅读:
    Cyclic Nacklace HDU
    Oulipo HDU
    Period HDU
    Blue Jeans POJ
    剪花布条 HDU
    最长公共前缀 CSU
    Clock Pictures CSU
    Number Sequence HDU
    Arrange the Bulls POJ
    Traveling by Stagecoach POJ
  • 原文地址:https://www.cnblogs.com/interdrp/p/15852397.html
Copyright © 2020-2023  润新知