Nginx配合Lua
案例
今天实现一个非常简单的例子.
云服务器上部署的了一个很通用的应用程序(它没有保护策略),其端口是a,但是我想使用他,就要通过公网ip:端口去访问它。暴露在外面很不安全。
那么就可以通过nginx反向代理,就达到了不暴露端口的目的了。
但是,我又不想别人随便访问,要加一些限制。比如必须要有指定的请求头。
解决方案: 用nginx配置就行了呗。
但是,还是不安全呐,随便别人如果F12查看请求的话,还是可以发现我的请求头啊。他随便捏造一个也可以访问这个应用了呀。
终极方案: lua脚本 + nginx反向代理
- 生成的请求头的值value,存到Redis里面【有一定时限】
- 用lua脚本获取请求头,如果是空的,返回指定的json字符串
- 如果第二步通过了,把请求头的值去Redis查一查,如果有,就通过;如果Redis里面没有,返回指定的json字符串
环境:openresty【它就是升级版的nginx,里面要记得安装redis的模块】
我这里是宝塔面板的openresty,安装的自带有redis模块。(这个安装就不放了)
配置
通用配置:nginx.conf
user www www;
worker_processes auto;
error_log /www/wwwlogs/nginx_error.log crit;
pid /www/server/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;stream {log_format tcp_format '$time_local|$remote_addr|$protocol|$status|$bytes_sent|$bytes_received|$session_time|$upstream_addr|$upstream_bytes_sent|$upstream_bytes_received|$upstream_connect_time';access_log /www/wwwlogs/tcp-access.log tcp_format;error_log /www/wwwlogs/tcp-error.log;include /www/server/panel/vhost/nginx/tcp/*.conf;
}events{use epoll;worker_connections 51200;multi_accept on;}http{include mime.types;#include luawaf.conf;include proxy.conf;default_type application/octet-stream;server_names_hash_bucket_size 512;client_header_buffer_size 32k;large_client_header_buffers 4 32k;client_max_body_size 50m;sendfile on;tcp_nopush on;keepalive_timeout 60;tcp_nodelay on;fastcgi_connect_timeout 300;fastcgi_send_timeout 300;fastcgi_read_timeout 300;fastcgi_buffer_size 64k;fastcgi_buffers 4 64k;fastcgi_busy_buffers_size 128k;fastcgi_temp_file_write_size 256k;fastcgi_intercept_errors on;gzip on;gzip_min_length 1k;gzip_buffers 4 16k;gzip_http_version 1.1;gzip_comp_level 2;gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/json image/jpeg image/gif image/png font/ttf font/otf image/svg+xml application/xml+rss text/x-js;gzip_vary on;gzip_proxied expired no-cache no-store private auth;gzip_disable "MSIE [1-6]\.";limit_conn_zone $binary_remote_addr zone=perip:10m;limit_conn_zone $server_name zone=perserver:10m;server_tokens off;access_log off;server {................... 重点配置}
}include /www/server/panel/vhost/nginx/*.conf;
}
server的重点配置
server {listen 监听的端口;location / {# 使用 Lua 脚本进行请求认证 (可以将这里一块抽取出去)access_by_lua_block {local redis = require "resty.redis"local cjson = require "cjson" -- 用于处理 JSON 格式的响应-- 创建 Redis 客户端local red = redis:new()red:set_timeout(1000) -- 设置连接超时,单位是毫秒-- Redis 连接池设置local pool_size = 100 -- 连接池大小local keepalive_timeout = 60000 -- 连接池中连接的超时时间(单位:毫秒)local keepalive_pool_size = 100 -- Redis 连接池的大小local redis_host = "Redis的ip" -- 更新 Redis IP 地址local redis_port = 6379local redis_password = "密码" -- Redis 密码-- 获取请求头中的 judge-auth 值local auth_val = ngx.req.get_headers()["judge-auth"]-- 判断 judge-auth 是否为空if not auth_val or auth_val == "" thenlocal response = {code = 403,message = "请求头缺失!"}ngx.header.content_type = 'application/json; charset=utf-8'ngx.status = 403ngx.say(cjson.encode(response))return ngx.exit(ngx.HTTP_OK)end-- 连接到 Redis,使用连接池local ok, err = red:connect(redis_host, redis_port)if not ok thenngx.log(ngx.ERR, "failed to connect to Redis: ", err)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- Redis 密码验证local res, err = red:auth(redis_password) -- Redis 密码if not res thenngx.log(ngx.ERR, "failed to authenticate with Redis: ", err)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- 查询 Redis 中是否存在该 keylocal res, err = red:get(auth_val)if not res thenngx.log(ngx.ERR, "failed to query Redis: ", err)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- 如果 Redis 中没有该 key,返回 403 错误if res == ngx.null thenlocal response = {code = 403,message = "尚未认证!"}ngx.header.content_type = 'application/json; charset=utf-8'ngx.status = 403ngx.say(cjson.encode(response))return ngx.exit(ngx.HTTP_OK)end-- 认证成功,将连接放回连接池local pool_ok, pool_err = red:set_keepalive(keepalive_timeout, keepalive_pool_size)if not pool_ok thenngx.log(ngx.ERR, "failed to set keepalive for Redis: ", pool_err)return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end}proxy_pass http://127.0.0.1:应用的端口号;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}