p>一,环境与源码包
$ cat /etc/issue Ubuntu 20.04.2 LTS n l $ uname -a Linux lenky-HP 5.10.0-1057-oem #61-Ubuntu SMP Thu Jan 13 15:06:11 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux $ ls nginx-1.20.2.tar.gz openresty-1.19.9.1.tar.gz
二,目标模型
client –> openresty –> nginx1/nginx2
说明:
1,nginx被用来作为后端服务,也就是会启动两个nginx+php,模拟web1站点和web2站点。
2,openresty用来作为请求分发服务器,将客户端不同的请求分发到web1或web2。
3,web2站点的资源访问需要做用户名/密码登录验证,但这个验证要对client透明,也就是client并不会去输用户名/密码。所以,这个验证过程需要由openresty来做,在openresty的本地保存有web2站点的账号/密码。
二,搭建nginx+php的站点
省略
三,搭建openresty
1,安装依赖包
$ sudo apt-get install openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev
2,编译安装
$ ./configure
$ make
$ sudo make install
默认安装在:$ ls -l /usr/local/openresty
3,修改nginx.conf,添加对lua的支持
$ sudo vim /usr/local/openresty/nginx/conf/nginx.conf $ sudo cat /usr/local/openresty/nginx/conf/nginx.conf |grep lua_package -B 1 -A 1 http { lua_package_path "/usr/local/openresty/lualib/?.lua;;"; lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; include /usr/local/openresty/nginx/lua/lua.conf;
4,创建lua配置和lua脚本
$ cd /usr/local/openresty/nginx $ sudo mkdir lua $ sudo chmod 777 lua $ cd lua/ $ vi lua.conf $ cat lua.conf server { listen 8080; server_name _; location /lua_hello { default_type 'text/html'; content_by_lua_file /usr/local/openresty/nginx/lua/hello.lua; } } $ vi hello.lua $ cat hello.lua ngx.say("hello world");
5,测试配置
$ sudo /usr/local/openresty/nginx/sbin/nginx -t nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful
6,启动openresty和测试业务效果
$ sudo /usr/local/openresty/nginx/sbin/nginx $ ps aux | grep nginx root 317226 0.0 0.0 11796 1324 ? Ss 11:24 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx nobody 317227 0.0 0.0 12456 4008 ? S 11:24 0:00 nginx: worker process $ wget -q -O - 127.0.0.1:8080/lua_hello hello world
四,配置openresty的代理
web2下有个php文件,客户端直接访问web2是受限的:
$ wget --no-check-certificate -q -O - https://192.168.122.237/web2/system/user_create_page.php <script language="javascript"> window.location = "denied.html" </script>
要到登录https://192.168.122.237/web2/login.php先进行登录,才能访问。
现在要实现客户端访问openresty,然后由openresty去自动登录web2并且将对应的资源请求下来发给客户端。
0,直接访问openresty的web2是不通
$ wget -O - http://127.0.0.1:8080/web2/login.php --2022-04-03 14:27:41-- http://127.0.0.1:8080/web2/login.php 正在连接 127.0.0.1:8080... 已连接。 已发出 HTTP 请求,正在等待回应... 404 Not Found 2022-04-03 14:27:41 错误 404:Not Found。
1,给openresty加上web2的upstream配置
在nginx.conf的http里添加如下:
upstream web2 {
server 192.168.122.237:443;
}
然后在lua.conf的server里添加如下:
location /web2 {
proxy_pass https://web2;
}
2,重新加载配置,测试
$ sudo /usr/local/openresty/nginx/sbin/nginx -s reload $ wget -O - http://127.0.0.1:8080/web2/login.php </html> - 100%[======================================================================>] 4.80K --.-KB/s 用时 0s 2022-04-03 14:32:15 (324 MB/s) - 已写入至标准输出 [4914/4914]
也就是将https://192.168.122.237:443/web2/login.php通过openresty返回到客户端了。
3,访问受限资源
$ wget -q -O - http://127.0.0.1:8080/web2/system/user_create_page.php <script language="javascript"> window.location = "denied.html" </script>
同样是受限。
接下来要实现上面这个请求能够正常访问,即由openresty去web2自动登录认证,然后将user_create_page.php资源请求下来发给客户端。
五,用lua脚本实现的openresty自动代理认证
1,先修改lua.conf,对location进行一下拆分,把认证接口单独为一个location,然后把其他请求丢到lua里去处理,完整的代码如下:
server { listen 8080; server_name _; location /lua_hello { default_type 'text/html'; content_by_lua_file /usr/local/openresty/nginx/lua/hello.lua; } location /web2/rest/auth.php { proxy_pass https://web2; } location /web2 { content_by_lua_file /usr/local/openresty/nginx/lua/web2.lua; } location /web2_internal/ { internal; proxy_pass https://web2/; } }
认证接口/web2/rest/auth.php单独出来,直接proxy_pass到web2。
其他web2请求(仍然假设到web2的请求都是在/web2下)交给lua/web2.lua处理。
添加一个/web2_internal/的内部代理,末尾要添加/,方便能转到web2站点的正确路径。可以在web2上查看access_log进行确认。
2,重点是lua/web2.lua文件,内容如下:
local cjson = require("cjson") local res = ngx.location.capture("/web2/rest/auth.php", { method = ngx.HTTP_POST, args = {}, body = '{"username": "admin","password":"a1bc6ce70630870bf20166791197c57eb174eb4e16e36693612b3e499cc115b4","login": true}' }) --ngx.say(res.status) --ngx.say(res.body) --[[ 200 {"success":true,"result":{"token":"co4wgw8gsgoc","url":"system/dashboard_page.php"}} ]] local body_json = cjson.decode(res.body) --ngx.say(body_json.success) if body_json.success ~= true then ngx.exit(403) end --[[ ngx.say("res.header:") for k,v in pairs(res.header) do ngx.say(tostring(k)..":"..tostring(v)) end ]] --[[ X-Powered-By:PHP/5.5.15 Set-Cookie:PHPSESSID=72p27dfkgcob0q2ftveo4ggjo6; path=/; secure; HttpOnly Expires:Thu, 19 Nov 1981 08:52:00 GMT Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma:no-cache X-PHP-Response-Code:200 Content-Type:application/json Strict-Transport-Security:max-age=31536000; includeSubDomains Content-Length:85 ]] local token = "" for k,v in pairs(res.header) do local k_str = tostring(k) local v_str = tostring(v) if k_str == "Set-Cookie" and string.find(v_str, "PHPSESSID=") ~= nil then local i = string.find(v_str, "PHPSESSID=") local j = string.find(v_str, ";", i) if j == nil then j = -1 end token = string.sub(v_str, i, j) end end --ngx.say(token) if token == "" then ngx.exit(403) end local req_cookie = ngx.req.get_headers()["cookie"] if req_cookie == nil then ngx.req.set_header("cookie", token) elseif string.find(req_cookie, "PHPSESSID=") == nil then ngx.req.set_header("cookie", token..req_cookie) else -- do not modify the old sessionid end --ngx.say(ngx.var.request_uri); ngx.exec("/web2_internal"..ngx.var.request_uri)
这个逻辑要根据web2的认证接口的请求数据和响应数据进行反复的调。整个的基本逻辑就是先用子请求ngx.location.capture(“/web2/rest/auth.php”来进行自动认证,认证通过后,将对应的session id(也就是cookie)添加到主请求的请求头里,再用ngx.exec跳转location即可。另外,账号/密码可以从redis或文件里读取,这里测试就是直接写在代码里。
六,进一步优化
客户端的每个请求都发起认证子请求,性能上是不合适的。因此,可以把cookie写到响应头里发给客户端,后续的请求先判断是否已存在这个token,如果存在就可以不用发认证子请求了。
在开始进一步修改之前,客户端获得的响应头如下:
—response begin—
HTTP/1.1 200 OK
Server: openresty/1.19.9.1
Date: Sun, 03 Apr 2022 04:05:08 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Powered-By: PHP/5.5.15
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
可以看到没有cookie的设置,改一下web2.lua,把认证token写到客户端。修改代码如下:
local token = "" local set_cookie = "" for k,v in pairs(res.header) do local k_str = tostring(k) local v_str = tostring(v) if k_str == "Set-Cookie" and string.find(v_str, "PHPSESSID=") ~= nil then local i = string.find(v_str, "PHPSESSID=") local j = string.find(v_str, ";", i) if j == nil then j = -1 end token = string.sub(v_str, i, j) set_cookie = v_str end end ngx.header["Set-Cookie"] = set_cookie
添加了三行set_cookie的代码,在客户端再请求,就能看到Set-Cookie的响应头了。
---response begin--- HTTP/1.1 200 OK Server: openresty/1.19.9.1 Date: Sun, 03 Apr 2022 04:14:00 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive Set-Cookie: PHPSESSID=fpf287b6n3mkmguol7d29pgfn6; path=/; secure; HttpOnly Strict-Transport-Security: max-age=31536000; includeSubDomains X-Powered-By: PHP/5.5.15 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache
跑通基本逻辑,其他的根据需求再改改,重构的完整代码如下:
-- file: web2.lua local function auth() res = ngx.location.capture("/web2/rest/auth.php", { method = ngx.HTTP_POST, args = {}, body = '{"username": "admin","password":"a1bc6ce70630870bf20166791197c57eb174eb4e16e36693612b3e499cc115b4","login": true}' }) if res == nil then ngx.exit(401) end --ngx.log(ngx.ERR, res.status) --ngx.log(ngx.ERR, res.body) --for k,v in pairs(res.header) do -- ngx.log(ngx.ERR, tostring(k)..":"..tostring(v)) --end return res; end local function get_cookie_from_auth(header) cookie_session_id = "" cookie_value = "" for k,v in pairs(header) do local k_str = tostring(k) local v_str = tostring(v) if k_str == "Set-Cookie" and string.find(v_str, "PHPSESSID=") ~= nil then local i = string.find(v_str, "PHPSESSID=") local j = string.find(v_str, ";", i) if j == nil then j = -1 end cookie_session_id = string.sub(v_str, i, j) cookie_value = v_str break end end --ngx.log(ngx.ERR, cookie_session_id) --ngx.log(ngx.ERR, cookie_value) return cookie_session_id, cookie_value end local function auth_is_pass() local req_cookie = ngx.req.get_headers()["cookie"] --ngx.log(ngx.ERR, req_cookie) if req_cookie == nil then return false elseif string.find(req_cookie, "PHPSESSID=") == nil then return false else return true end end local function add_session_id_to_req(cookie_session_id) local req_cookie = ngx.req.get_headers()["cookie"] --ngx.log(ngx.ERR, req_cookie) if req_cookie == nil then ngx.req.set_header("cookie", cookie_session_id) elseif string.find(req_cookie, "PHPSESSID=") == nil then ngx.req.set_header("cookie", cookie_session_id..req_cookie) else -- do not modify the old sessionid end end -- main if auth_is_pass() == false then local res = auth() local cjson = require("cjson") local body_json = cjson.decode(res.body) --ngx.log(ngx.ERR, body_json.success) if body_json.success ~= true then ngx.exit(402) end cookie_session_id, cookie_value = get_cookie_from_auth(res.header) if cookie_session_id == "" then ngx.exit(403) else add_session_id_to_req(cookie_session_id) end ngx.header["Set-Cookie"] = cookie_value end -- redirect ngx.exec("/web2_internal"..ngx.var.request_uri)
GG~
参考:
https://blog.csdn.net/cuichunchi/article/details/89682234
https://blog.csdn.net/reblue520/article/details/100165598
https://www.nginx.com/resources/wiki/modules/lua/#ngx-location-capture
网友评论已有0条评论, 我也要评论