openresty安全机制-白名单

1、openresty与nginx区别

        a、扩展性

                nginx主要通过预编译的模块来扩展功能,‌虽然其模块化架构设计良好,‌但对于定制化

        需求需要重新编译服务器。‌相比之下,‌OpenResty通过嵌入Lua脚本可以动态扩展功能,‌无

        需重新编译服务器,‌开发者可以灵活地调整和添加功能

        b、性能与资源使用

                nginx在处理静态内容和反向代理方面表现出色,‌资源消耗低,‌适合高并发的静态内容

        服务。‌OpenResty在高并发处理能力上继承了Nginx的优势,‌同时LuaJIT提供了高效的脚本

        执行能力,‌适合动态内容处理和复杂逻辑的实现

        c、开发灵活性

                nginx的配置文件采用纯文本格式,‌功能定义相对简单,‌适合标准化和固定需求的场景

        。‌OpenResty通过Lua脚本可以实现复杂的业务逻辑,‌适合需要灵活处理和快速迭代的开发

        场景

        d、应用场景

                nginx适合处理静态内容和反向代理,‌而OpenResty适合高性能Web应用、‌实时数据处

        理和分析、‌动态内容生成、‌API网关和微服务架构等场景

2、劣势

        a、社区与生态

                nginx有着庞大的用户群体和活跃的社区,‌提供丰富的文档和教程。‌虽然OpenResty的

        用户群体较小,‌但在开发者社区中备受推崇,‌尤其是在需要高度定制化的场景中

3、白名单配置

        a、安装rsa依赖

wget https://github.com/spacewander/lua-resty-rsa/archive/refs/tags/v1.1.0.tar.gz
tar -zxf lua-resty-rsa-1.1.0.tar.gz 
cd lua-resty-rsa-1.1.0/lib/resty
cp rsa.lua /usr/local/openresty/lualib/resty/

        b、修改nginx配置(以thinkphp6.0框架为例)

外网访问的配置文件为例
upstream api {
        server 192.168.0.191:8888;
}
server {
        listen        80;
        server_name www.testrsaredislua.com;
        error_log /data/nginx/www.testrsaredislua.com_error.log crit;
        access_log /data/nginx/www.testrsaredislua.com_acess_$logdate.log access-upstream;
        lua_code_cache off;
        autoindex off;
        server_tokens off;
        error_page   401 /401.html;
        location / {
        		proxy_set_header Host $host;
        		add_header 'Access-Control-Allow-Credentials' 'true';
        		add_header Access-Control-Allow-Origin "$http_origin";
        		add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Access-Token';
        		if ($request_method ~* OPTIONS) {
					return 200;
        		}
        		add_header Cache-Control no-cache;
        		add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                access_by_lua_file /data/wwwroot/rsa-redis.lua;
                proxy_pass http://api;
        }
        location = /401.html {
            root "/data/wwwroot/error";
        }
}
外网转换到内网配置文件为例
server {
        listen        8888;
        root   "/data/wwwroot/gpapi/public";
        error_log /data/nginx/www.testrsaredislua.com8888_error.log crit;
        access_log /data/nginx/www.testrsaredislua.com8888_acess_$logdate.log access-upstream;
        error_page   403 /403.html;
        error_page   404 /404.html;
        error_page      500 502 503 504 /50x.html;
        location / {
                if (!-e $request_filename) {
                    rewrite ^(.*)$ /index.php?s=/$1 last;
                }
                index index.php;
                autoindex off;
        }
        location = /403.html {
            root "/data/wwwroot/error";
        }
        location = /404.html {
            root "/data/wwwroot/error";
        }
        location = /50x.html {
            root "/data/wwwroot/error";
        }
        location ~ \.php$ {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
}

        c、在/data/wwwroot/目录下创建rsa-redis.lua文件

--引入Redis
local redis = require "resty.redis";
--引入rsa
local rsa = require "resty.rsa";
--Redis链接ip
local ip = "192.168.0.191"
--Redis链接端口
local port = 6379
--Redis连接密码
local pass = "123456"
--鉴权Redis(链接Redis)
local function connAuth()
	local red = redis:new();
	local connCount, err = red:connect(ip, port);
	if not connCount then
		ngx.say("failed to connect: ", err)
		close_redis(red)
		return
	end
	red:set_timeouts(2000);
	local ok, err = red:auth(pass)
	if not ok then
		ngx.say("failed to auth: ", err)
		close_redis(red)
		return
	end
	return red
end

--关闭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
        ngx.say("set keepalive error : ", err)
    end
end

--获取请求者IP
local function getIp()
    local clientIP = ngx.req.get_headers()["X-Real-IP"]
    if clientIP == nil then
        clientIP = ngx.req.get_headers()["x_forwarded_for"]
    end
    if clientIP == nil then
        clientIP = ngx.var.remote_addr
    end
    return clientIP
end
--获取用户访问IP
local clientIP = getIp();
--封禁token时间(秒)
local token_block_time= 120
--指定token访问频率计数最大值(次)
local token_max_count = 3

--如果数据为空的情况下
local function is_empty(value)
    if type(value) == "table" then
        return next(value) == nil
    elseif type(value) == "string" then
        return #value == 0
    elseif type(value) == "nil" then
        return true
    end
        return false
end

--过滤特殊字符串(只保留字母与数字,字母不区分大小写)
local function filter_special_chars(s)
    local ss = {}
    local k = 1
    while true do
        if k > #s then break end
        local c = string.byte(s,k)
        if not c then break end
        if c<192 then
            if (c>=48 and c<=57) or (c>= 65 and c<=90) or (c>=97 and c<=122) then
                table.insert(ss, string.char(c))
            end
            k = k + 1
        end
    end
    return table.concat(ss)
end

--如果头部信息没有指定的参数或是指定参数的值无法解析,加入IP黑名单
--如果同样的头部参数键在封禁token时间内连续访问指定token访问频率计数最大值次以上,加入IP黑名单
local function set_blacklist()
    local header = ngx.req.get_headers();
    local red = connAuth();
	local token, err = header["x-token"];
    --如果参数为空的情况下
	if not token then
        local res, err =  red:sismember('black-list', clientIP);
        if res ~= 1 then
            red:sadd('black-list', clientIP)
        end
        close_redis(red)
        return ngx.exit(401)
    else
        --如果参数值为空的情况下
        if is_empty(token) then
            local res, err =  red:sismember('black-list', clientIP);
            if res ~= 1 then
                red:sadd('black-list', clientIP)
            end
            close_redis(red)
            return ngx.exit(401)
        end
        --如果同一个token键在封禁token时间内连续访问指定token访问频率计数最大值次
        local outputToken = filter_special_chars(token)
        local redToken, err = red:get(outputToken);
        if redToken ~= ngx.null then
            if tonumber(redToken) >= tonumber(token_max_count) then
                local res, err =  red:sismember('black-list', clientIP);
                if res ~= 1 then
                    red:sadd('black-list', clientIP)
                end
                close_redis(red)
                return ngx.exit(401)
            else
                --累加访问次数
                red:incr(outputToken)
            end
        end
        --添加访问次数
        if redToken == ngx.null then
            red:set(outputToken,1)
        end
        --设置过期时间
        red:expire(outputToken,token_block_time)
        --如果参数值采用base64解析不开的情况下
        local encrypted, err = tostring(ngx.decode_base64(token))
        if not encrypted then
            local res, err =  red:sismember('black-list', clientIP);
            if res ~= 1 then
                red:sadd('black-list', clientIP)
            end
            close_redis(red)
            return ngx.exit(401)
        end
        --采用rsa技术解析token base64过后的值内容
        local priv_key = '-----BEGIN PRIVATE KEY-----\n' ..red:get('priv_key')..'\n-----END PRIVATE KEY-----';
        local priv, err = rsa:new({private_key = priv_key})
        local dst, err = rsa.decrypt(priv, encrypted)
        if not dst then
            local res, err =  red:sismember('black-list', clientIP);
            if res ~= 1 then
                red:sadd('black-list', clientIP)
            end
            close_redis(red)
            return ngx.exit(401)
        end
    end
end

--查看是否在黑名单里面
local function get_blacklist()
    local red = connAuth();
    local res, err =  red:sismember('black-list', clientIP);
    if res == 1 then
        close_redis(red);
        return ngx.exit(401);
    end
    close_redis(red);
end

get_blacklist();
set_blacklist();

4、总结

        a、OpenResty与Nginx的主要区别

                在于OpenResty通过嵌入Lua脚本提供了更高的灵活性和动态扩展性,‌适合需要复杂逻

        辑和快速迭代的开发场景,‌而Nginx则以其稳定性和对静态内容的优化处理著称。‌

        b、我们做登录系统时,往往无法隐藏用户登录token

                采用rsa加密方式后,可以隐藏用户token。但是在现实中,往往不是nginx压力过大,而

        是后端压力过大。我们采用openresty来过滤非法访问用户,这样就降低了后端压力。同样也

        变相防止了爬虫。

        c、openresty中lua可以连接redis或MySQL

                只要业务架构设计合理的情况下,有些读的操作可以直接让openresty中lua直接解决,

        无需走后端语言(java或是PHP等)。如果你能力可以的情况下,还可以直接让openresty调

        用lua,lua再调用go插件或者程序。都知道go语言是处理并发最强大的语言(非C语言等,

        只是说对java、php、pythen、node等来说)

        d、重点说明

                因本身是IT直男,在IT行业中是“一步一个脚印打出来的天下”,基本上常见的语言及中

        间件都实践过,并且有自己的一套实战理论。之所以在此提醒的原因,对于我个人而言:

        有耐心、有冲劲、有干劲、不怕吃苦、乐于实战研究且有一定的开发能力及设计能力。如果

        达不到最基本的标准,请不要使用openresty,他的社区没有nginx的强大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙哥·三年风水

感觉写得好,给创作人一点赏赐呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值