類似于這里提到的問題https://forum.openresty.us/d/6503-get-content-of-second-set-cookie-header
我有一個 NGINX 配置,它通過上游 auth_request 獲取存盤在 Set-Cookie 中的 cookie,我需要將這些 set-cookie 回傳給客戶端,但是每當我嘗試回傳這些 cookie 時,只有第一個 set-cookie 回傳給客戶端.
下面是一個示例配置來演示這個問題
location /auth/ {
proxy_pass http://auth/;
proxy_pass_request_body off;
proxy_redirect off;
}
location / {
auth_request /auth/loggedin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
proxy_set_header Cookie "$http_cookie; $auth_cookie";
proxy_pass http://someservice/;
}
在我上面的示例中,我希望可以在 Set-Cookie 標頭中回傳多個 cookie,a=12; PATH:"/", b=2; PATH:/"并且我想通過 add_header 將來自上游服務的任何 set-cookie 傳遞給客戶端瀏覽器。目前只有 cookiea正在發送給客戶端并且b總是丟失。
注意:我希望它是通用的,因此我無法從標題中獲取確切的 cookie 名稱。
感謝您提供任何幫助!
uj5u.com熱心網友回復:
不幸的是,按照您想要的方式進行操作是不可能的。使用多個Set-Cookie標頭設定 cookie是一種常見的方法,有關標頭的MDN 檔案Set-Cookie明確指出:
要發送多個 cookie,
Set-Cookie應該在同一個回應中發送多個標頭。
當從上游收到多個同名的標頭時,只有第一個可以使用$upstream_http_<header_name>變數訪問(有一些例外,例如Cache-Control一個,如果我沒記錯的話)。在 nginx 錯誤跟蹤器上有一張票(雖然我不認為這是一個錯誤)。Set-Cookieheader 確實是一種特殊情況,不能與許多其他標題相對折疊,請查看此答案和下面的評論以了解為什么會這樣。(當然,您仍然可以自由使用任何$upstream_cookie_<cookie_name>per-cookie 變數)。
但是,可以使用 OpenResty(在您的問題中提到)或lua-nginx-module. 壞訊息是,它將與auth_request指令不兼容,因為不可能將這些lua_...處理程式添加到 auth 位置(或任何其他可以使用的子請求位置,例如,來自 的add_before_body或add_after_body指令ngx_http_addition_module)。您沒有收到錯誤訊息,但不會在子請求上觸發這些處理程式。好訊息是,類似的功能auth_request可以使用ngx.location.captureAPI 呼叫來實作。
因此,如果使用nginx-lua-module適合您(如果不適合,也許解決方案會幫助其他人),可以通過以下方式完成:
location /auth/ {
internal;
proxy_pass http://auth/;
proxy_redirect off;
}
location / {
# -- this one is taken from the official lua-nginx-module documentation example
# -- see https://github.com/openresty/lua-nginx-module#access_by_lua
access_by_lua_block {
local res = ngx.location.capture("/auth/loggedin", {body = ""})
if res.status == ngx.HTTP_OK then
ngx.ctx.auth_cookies = res.header["Set-Cookie"]
return
end
if res.status == ngx.HTTP_FORBIDDEN then
ngx.exit(res.status)
end
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
}
# -- it doesn't matter where the 'proxy_pass' line actually would be, it doesn't change the workflow
# -- see https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png
proxy_pass http://someservice/;
header_filter_by_lua_block {
local function merge_cookies(a, b)
local c = a or b
-- if either "a" or "b" is empty, "c" already has the required result
if (a and b) == nil then return c end
-- neither "a" nor "b" are empty, result will be a table, "c" now equals to "a"
-- if "c" is a string, lets made it a table instead
if type(c) == "string" then c = {c} end
-- append "b" to "c"
if type(b) == "string" then table.insert(c, b) else
for _, v in ipairs(b) do table.insert(c, v) end
end
return c
end
ngx.header["Set-Cookie"] = merge_cookies(ngx.header["Set-Cookie"], ngx.ctx.auth_cookies)
}
}
此代碼不會檢查可能的 cookie 名稱交集,否則會復雜得多。但是我認為(沒有在實踐中檢查過)這并不重要,因為即使兩個Set-Cookie具有相同 cookie 名稱但不同值的請求將回傳給客戶端,也會使用最后一個。此代碼使Set-Cookie來自身份驗證服務器的Set-Cookie請求在來自主要上游的請求之后到達。做相反的事情,你需要改變
ngx.header["Set-Cookie"] = merge_cookies(ngx.header["Set-Cookie"], ngx.ctx.auth_cookies)
線到
ngx.header["Set-Cookie"] = merge_cookies(ngx.ctx.auth_cookies, ngx.header["Set-Cookie"])
特別感謝@wilsonzlin在使用表格時提供的非常有用的答案ngx.header["Set-Cookie"]。
更新
Though you didn't mention it in your question, looking at your example I saw you not only want to pass the Set-Cookie headers to the client but also to send those cookies to the someservice upstream. Again, you are trying to do it a wrong way. Using proxy_set_header Cookie "$http_cookie; $auth_cookie"; you are appending those cookies including their attributes like Path, Max-Age, etc. while the Cookie header should contain only the name=value pairs. Well, using the lua-nginx-module this is also posible. You'd need to change the above access_by_lua_block to the following one.
access_by_lua_block {
local res = ngx.location.capture("/auth/loggedin", {body = ""})
if res.status == ngx.HTTP_OK then
ngx.ctx.auth_cookies = res.header["Set-Cookie"]
if ngx.ctx.auth_cookies then
-- helper functions
-- strip all Set-Cookie attributes, e.g. "Name=value; Path=/; Max-Age=2592000" => "Name=value"
local function strip_attributes(cookie)
return string.match(cookie, "[^;] ")
end
-- iterator for use in "for in" loop, works both with strings and tables
local function iterate_cookies(cookies)
local i = 0
return function()
i = i 1
if type(cookies) == "string" then
if i == 1 then return strip_attributes(cookies) end
elseif type(cookies) == "table" then
if cookies[i] then return strip_attributes(cookies[i]) end
end
end
end
local cookies = ngx.req.get_headers()["Cookie"]
-- at the first loop iteration separator should be an empty string if client browser send no cookies or "; " otherwise
local separator = cookies and "; " or ""
-- if there are no cookies in original request, make "cookies" variable an empty string instead of nil to prevent errors
cookies = cookies or ""
for cookie in iterate_cookies(ngx.ctx.auth_cookies) do
cookies = cookies .. separator .. cookie
-- next separator definitely should be a "; "
separator = "; "
end
ngx.req.set_header("Cookie", cookies)
end
return
end
if res.status == ngx.HTTP_FORBIDDEN then
ngx.exit(res.status)
end
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
}
All the above examples are tested (using OpenResty 1.17.8.2) and confirmed to be workable.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/352475.html
標籤:nginx 饼干 nginx-反向代理 nginx配置
上一篇:如何使用安裝在Docker外部的Nginx代理Docker中的php-fpm程式
下一篇:動態定位塊變數到字串
