1.初始化系统
下面配置的是阿里源
# 更换yum源
yum -y install wget vim
# 旧源备份
cd /etc/yum.repos.d
sudo mv CentOS-Base.repo CentOS-Base.repo.bak
# 修改为阿里源
sudo wget -O CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 安装epel repo源
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
# 其他
sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
# 刷新缓存
yum clean all
yum makecache
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
#关闭 SELINUX
sed -i s#^SELINUX=enforcing#SELINUX=disabled#g /etc/selinux/config
2. 安装OpenResty和第三方模块nginx-auth-ldap
# 安装依赖包
yum -y install gcc gcc-c++ make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel git
# 添加第三方模块nginx-auth-ldap,需要安装openldap-devel
yum install openldap-devel -y
# 下载nginx-auth-ldap
cd /usr/local/src/ && git clone https://github.com/kvspb/nginx-auth-ldap.git
# 下载nginx
cd /usr/local/src/ && wget https://openresty.org/download/openresty-1.19.3.1.tar.gz
# 解压nginx包
tar -zxvf openresty-1.19.3.1.tar.gz
# 编译nginx并添加第三方模块nginx-auth-ldap
cd openresty-1.19.3.1/ && ./configure --add-module=../nginx-auth-ldap
gmake && gmake install
# 创建软链到/usr/bin/
ln -s /usr/local/openresty/bin/openresty /usr/bin/
3. 安装Redis
yum -y install redis
3.1 配置redis
vim /etc/redis.conf
# 将这部注释掉,否则只有本机才能访问
#bind 127.0.0.1
# 保护模式修改为no
protected-mode no
# 修改密码为 Server2008
requirepass Server2008
# 启动redis
systemctl start redis
systemctl enable redis
4. 配置lua脚本和nginx.conf
4.1 token认证脚本
vi /usr/local/openresty/nginx/conf/auth_token.lua
-- 连接redis
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
local ok, err = red:connect("127.0.0.1", 6379)
local res, err = red:auth("Server2008")
-- 获取url的token
local args = ngx.req.get_uri_args();
local token = args["token"];
-- 获取redis的path
if token then
redis_path, err = red:get(token);
else
redis_path, err = red:get(ngx.var.cookie_token);
end
-- 获取redis的path错误
if not redis_path then
ngx.exec("@ERROR_TOKEN");
end
if redis_path == ngx.null then
ngx.exec("@ERROR_TOKEN");
end
-- 获取url的path
local url_path = ngx.re.sub(ngx.var.uri, "^(.*)/.*", "$1/", "jo");
-- 验证path
if string.find(url_path, redis_path, 0) then
ngx.exec("@PASS");
else
ngx.exec("@ERROR_TOKEN");
end
local ok, err = red:close();
4.2 获取token脚本
vi /usr/local/openresty/nginx/conf/get_token.lua
-- 生成随机数函数
function getRandom(n)
local t = {
"0","1","2","3","4","5","6","7","8","9",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
}
local s = ""
for i =1, n do
s = s .. t[math.random(#t)]
end;
return s
end;
local new_token = getRandom(48);
-- 连接redis
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
local ok, err = red:connect("127.0.0.1", 6379)
local res, err = red:auth("Server2008")
-- 获取连接的路径
local path = ngx.re.sub(ngx.var.uri, "^(.*)/get_token", "$1/", "jo");
-- 判断路径
local F,err=io.open("/data/product" .. path, "r")
if not F then
ngx.exit(403)
else
F:close()
-- 显示下载路径
ngx.say("下载路径为:", path)
-- 把token和path写到redis
local ok, err = red:set(new_token, path);
-- 设置token过期时间为一天
local ok, err = red:expire(new_token,"86400");
-- 关闭redis
local ok, err = red:close();
-- 显示内容token和path
ngx.say("有效期为1天,下载地址为:\nwget -r -np -x -nH -R index.html http://192.168.4.235", path, "?token=", new_token);
end
4.3 修改nginx.conf
vi /usr/local/openresty/nginx/conf/nginx.conf
user root;
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;
ldap_server zetyun {
url ldap://172.20.10.231:389/ou=zetyun,dc=zetyun,dc=com?sAMAccountName?sub?(objectClass=person);
binddn "CN=Administrator,CN=Users,DC=zetyun,DC=com";
binddn_passwd Zetyun@AD;
group_attribute member;
group_attribute_is_dn on;
satisfy any;
require group "cn=Download,ou=zetyun,dc=zetyun,dc=com";
}
#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;
# 外网域名访问和token认证
server {
listen 81;
server_name customer.download.aps.zetyun.cn;
charset utf-8;
#charset koi8-r;
#access_log logs/host.access.log main;
location @PASS {
default_type text/plain;
# 访问成功写入cookie
header_filter_by_lua_block {
local args = ngx.req.get_uri_args();
local token = args["token"];
if token then
ngx.header['Set-Cookie'] = 'token=' .. token;
end
}
root /data/product/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
location @ERROR_TOKEN {
default_type text/plain;
content_by_lua_block {
ngx.say("您的token错误或失效,请联系销售或交付人员!")
}
}
location / {
default_type text/plain;
access_by_lua_file /usr/local/openresty/nginx/conf/auth_token.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;
}
}
# 内网get_token和AD登录
server {
listen 80;
server_name 172.20.10.253;
charset utf-8;
location / {
auth_ldap "请输入AD账户密码";
auth_ldap_servers zetyun;
root /data/product/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
location ~ ^(.*/get_token) {
default_type text/plain;
content_by_lua_file /usr/local/openresty/nginx/conf/get_token.lua;
allow 192.168.0.0/16;
allow 10.0.0.0/30;
allow 172.20.0.0/17;
allow 172.21.1.0/24;
allow 172.25.1.0/24;
deny all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
参考
--[[
参考文档: https://www.nginx.com/resources/wiki/modules/lua/#nginx-api-for-lua
Nginx Lua模块指令:
Nginx共11个处理阶段,而相应的处理阶段是可以做插入式处理,即可插拔式架构;
另外指令可以在http、server、server if、location、location if几个范围进行配置:
指令: init_by_lua/init_by_lua_file
处理阶段:
loading-config
使用范围:
http
解释:
nginx Master进程加载配置时执行;
通常用于初始化全局配置/预加载Lua模块
指令: init_worker_by_lua/init_worker_by_lua_file
处理阶段:
starting-worker
使用范围:
http
解释:
每个NginxWorker进程启动时调用的计时器,
如果Master进程不允许则只会在init_by_lua之后调用;
通常用于定时拉取配置/数据,或者后端服务的健康检查
指令: set_by_lua/set_by_lua_file
处理阶段:
rewrite
使用范围:
server,server if,location,location if
解释:
设置nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快;
指令: rewrite_by_lua/rewrite_by_lua_file
处理阶段:
rewrite tail
使用范围:
http,server,location,location if
解释:
rewrite阶段处理,可以实现复杂的转发/重定向逻辑;
指令: access_by_lua/access_by_lua_file
处理阶段:
access tail
使用范围:
http,server,location,location if
解释:
请求访问阶段处理,用于访问控制
指令: content_by_lua/content_by_lua_file
处理阶段:
content
使用范围:
location,location if
解释:
内容处理器,接收请求处理并输出响应
指令: header_filter_by_lua/header_filter_by_lua_file
处理阶段:
output-header-filter
使用范围:
http,server,location,location if
解释:
设置header和cookie
指令:body_filter_by_lua/body_filter_by_lua_file
处理阶段:
output-body-filter
使用范围:
http,server,location,location if
解释:
对响应数据进行过滤,比如截断、替换。
指令:log_by_lua/log_by_lua_file
处理阶段:
log
使用范围:
http,server,location,location if
解释:
log阶段处理,比如记录访问量/统计平均响应时间
]]
ngx.var
-- nginx变量,如果要赋值如ngx.var.b = 2,此变量必须提前声明;
-- 另外对于nginx location中使用正则捕获的捕获组可以使用ngx.var[捕获组数字]获取;
ngx.var.request_uri
-- 这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI。
ngx.var.scheme -- ngx.var.server_addr
-- HTTP方法(如http,https)。按需使用
ngx.var.server_addruri
-- 服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
ngx.var.uri
-- 请求中的当前URI(不带请求参数,参数位于$args),可以不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。
ngx.var.server_name
-- 服务器名称
ngx.var.server_port
-- 请求到达服务器的端口号。
ngx.var.remote_addr
-- 获取远程的IP地址
ngx.var.remote_port
-- 获取远程的端口号
ngx.header
-- 输出响应头;
ngx.print
-- 输出响应内容体;
ngx.say
-- 通ngx.print,但是会最后输出一个换行符;
ngx.exit
-- 指定状态码退出。
ngx.redirect
-- 重定向;
ngx.status = 301
-- 设置响应的状态码;
ngx.resp.get_headers()
-- 获取设置的响应状态码;
ngx.send_headers()
-- 发送响应状态码, ,当调用ngx.say/ngx.print时自动发送响应状态码;
ngx.headers_sent = true
-- 判断是否发送了响应状态码。
ngx.req.get_headers
-- 获取请求头,默认只获取前100,如果想要获取所以可以调用ngx.req.get_headers(0);
-- 获取带中划线的请求头时请使用如headers.user_agent这种方式;
-- 如果一个请求头有多个值,则返回的是table;
ngx.req.get_uri_args
-- 获取url请求参数,其用法和get_headers类似;
ngx.req.get_post_args
-- 获取post请求内容体,其用法和get_headers类似,但是必须提前调用ngx.req.read_body()来读取body体
--(也可以选择在nginx配置文件使用lua_need_request_body on;开启读取body体,但是官方不推荐);
ngx.req.raw_header
-- 未解析的请求头字符串;
ngx.req.get_body_data
-- 为解析的请求body体内容字符串。
ngx.req.set_uri(uri, false)
-- 可以内部重写uri(可以带参数),等价于 rewrite ^ /lua_rewrite_3;
-- 通过配合if/else可以实现 rewrite ^ /lua_rewrite_3 break;
-- 这种功能;此处两者都是location内部url重写,不会重新发起新的
ngx.req.set_uri_args
-- 重写请求参数,可以是字符串(a=1&b=2)也可以是table;
ngx.escape_uri
ngx.unescape_uri
-- uri编码解码;
ngx.encode_args
ngx.decode_args
-- 参数编码解码;
ngx.encode_base64
ngx.decode_base64
-- BASE64编码解码;
ngx.re.match
-- nginx正则表达式匹配;
ngx.timer.at
-- 延时调用相应的回调方法;
-- ngx.timer.at(秒单位延时,回调函数,回调函数的参数列表);
-- 可以将延时设置为0即得到一个立即执行的任务,任务不会在当前请求中执行不会阻塞当前请求,而是在一个轻量级线程中执行。
-- 指令: init_by_lua
--[[
nginx.conf配置文件中的http部分添加如下代码,
#共享全局变量,在所有worker间共享
lua_shared_dict shared_data 1m;
init_by_lua_file /usr/example/lua/init.lua;
]]
--init.lua
--初始化耗时的模块
local redis = require 'resty.redis'
local cjson = require 'cjson'
--全局变量,不推荐
count = 1
--共享全局内存
local shared_data = ngx.shared.shared_data
shared_data:set("count", 1)
-- test.lua
count = count + 1
ngx.say("global variable : ", count)
local shared_data = ngx.shared.shared_data
ngx.say(", shared memory : ", shared_data:get("count"))
shared_data:incr("count", 1)
ngx.say("hello world")
-- 访问如http://192.168.1.2/lua 会发现全局变量一直不变,而共享内存一直递增
-- global variable : 2 , shared memory : 8 hello world
-- 另外注意一定在生产环境开启lua_code_cache,否则每个请求都会创建Lua VM实例。
-- 指令: init_worker_by_lua
--[[
用于启动一些定时任务,比如心跳检查,定时拉取服务器配置等等;
此处的任务是跟Worker进程数量有关系的,比如有2个Worker进程那么就会启动两个完全一样的定时任务。
nginx.conf配置文件中的http部分添加如下代码:
init_worker_by_lua_file /usr/example/lua/init_worker.lua;
另外根据实际情况设置如下指令
lua_max_pending_timers 1024; #最大等待任务数
lua_max_running_timers 256; #最大同时运行任务数
]]
-- init_worker.lua
local count = 0
local delayInSeconds = 3
local heartbeatCheck = nil
heartbeatCheck = function(args)
count = count + 1
ngx.log(ngx.ERR, "do check ", count)
local ok, err = ngx.timer.at(delayInSeconds, heartbeatCheck)
if not ok then
ngx.log(ngx.ERR, "failed to startup heartbeart worker...", err)
end
end
heartbeatCheck()
-- 指令: set_by_lua
--[[
设置nginx变量,我们用的set指令即使配合if指令也很难实现负责的赋值逻辑;
语法set_by_lua_file $var lua_file arg1 arg2...;
在lua代码中可以实现所有复杂的逻辑,但是要执行速度很快,不要阻塞;
example.conf配置文件:
location /lua_set_1 {
default_type "text/html";
set_by_lua_file $num /usr/example/lua/test_set_1.lua;
echo $num;
}
]]
-- test_set_1.lua
local uri_args = ngx.req.get_uri_args()
local i = uri_args["i"] or 0
local j = uri_args["j"] or 0
return i + j
-- 访问如http://192.168.1.2/lua_set_1?i=1&j=10进行测试。 如果我们用纯set指令是无法实现的。