Nginx - 解码 URL 查询参数并将其作为请求转发 header
Nginx - Decode URL query parameter and forward it as request header
我需要以查询参数(es.http://example.com?BASIC_AUTH=dXNlcjpwYXNz
)的形式向 nginx 发送一些基本的身份验证凭据(es.user:pass
),并能够以更常用的方式转发它们 Authorization: Basic dXNlcjpwYXNz
header 形式到代理后面的目标服务器。
我已经能够使用正则表达式检索编码的身份验证字符串的值。问题是,该值通常可能包含一些需要在 URL 中成为 percent-encoded 的字符。 Es。 user:pass!
-> ?BASIC_AUTH=dXNlcjpwYXNzIQ==
变为 ?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D
因此,当我将请求转发到目标服务器时,我最终指定 Authorization: Basic dXNlcjpwYXNzIQ%3D%3D
目标服务器将拒绝哪个请求,给出 401 Unauthorized。
如何强制 nginx 在设置授权前解码 auth 字符串 header?预先感谢您的帮助。
注意:由于某些 application-specific 限制,我无法首先在授权 header 中发送身份验证字符串。
"Pure" nginx解决方案
遗憾的是nginx没有提供丰富的字符串操作集。我认为没有办法通过一些字符串进行全局搜索和替换(如果我们可以将所有 %2B
替换为 +
,%2F
替换为 [=,这可能是一个解决方案15=] 和 %3D
与 =
)。然而,在某些情况下,nginx 会执行某些字符串的 urldecoding - 当此字符串成为将转发到上游代理服务器的 URI 的一部分时。
所以我们可以将 BASIC_AUTH
请求参数的值添加到 URI 并向我们自己发出代理请求:
# Main server block
server {
listen 80 default_server;
...
location / {
if ($arg_basic_auth) {
# "basic_auth" request argument is present,
# append "/decode_basic_auth/<BASE64_token>" to the URI
# and go to the next location block
rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth last;
}
# No "basic_auth" request argument present,
# can do a proxy call from here without setting authorization headers
...
}
location /decode_basic_auth/ {
# This will be an internal location only
internal;
# Remove "basic_auth" request argument from the list of arguments
if ($args ~* (.*)(^|&)basic_auth=[^&]*(|$)&?(.*)) {
set $args ;
}
# Some hostname for processing proxy subrequests
proxy_set_header Host internal.basic.auth.localhost;
# Do a subrequest to ourselfs, preserving other request arguments
proxy_pass http://127.0.0.1$uri$is_args$args;
}
}
# Additional server block for proxy subrequests processing
server {
listen 80;
server_name internal.basic.auth.localhost;
# Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
proxy_set_header Authorization "Basic ";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>$is_args$args;
}
# Do not serve other requests
location / {
return 444;
}
}
也许这不是一个非常优雅的解决方案,但它已经过测试并且有效。
OpenResty / ngx_http_lua_module
这可以很容易地用openresty or ngx_http_lua_module using ngx.escape_uri函数解决:
server {
listen 80 default_server;
...
location / {
set $auth $arg_basic_auth;
if ($args ~* (.*)(^|&)basic_auth=[^&]*(|$)&?(.*)) {
set $args ;
}
rewrite_by_lua_block {
ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
}
proxy_set_header Authorization "Basic $auth";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>;
}
}
我需要以查询参数(es.http://example.com?BASIC_AUTH=dXNlcjpwYXNz
)的形式向 nginx 发送一些基本的身份验证凭据(es.user:pass
),并能够以更常用的方式转发它们 Authorization: Basic dXNlcjpwYXNz
header 形式到代理后面的目标服务器。
我已经能够使用正则表达式检索编码的身份验证字符串的值。问题是,该值通常可能包含一些需要在 URL 中成为 percent-encoded 的字符。 Es。 user:pass!
-> ?BASIC_AUTH=dXNlcjpwYXNzIQ==
变为 ?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D
因此,当我将请求转发到目标服务器时,我最终指定 Authorization: Basic dXNlcjpwYXNzIQ%3D%3D
目标服务器将拒绝哪个请求,给出 401 Unauthorized。
如何强制 nginx 在设置授权前解码 auth 字符串 header?预先感谢您的帮助。
注意:由于某些 application-specific 限制,我无法首先在授权 header 中发送身份验证字符串。
"Pure" nginx解决方案
遗憾的是nginx没有提供丰富的字符串操作集。我认为没有办法通过一些字符串进行全局搜索和替换(如果我们可以将所有 %2B
替换为 +
,%2F
替换为 [=,这可能是一个解决方案15=] 和 %3D
与 =
)。然而,在某些情况下,nginx 会执行某些字符串的 urldecoding - 当此字符串成为将转发到上游代理服务器的 URI 的一部分时。
所以我们可以将 BASIC_AUTH
请求参数的值添加到 URI 并向我们自己发出代理请求:
# Main server block
server {
listen 80 default_server;
...
location / {
if ($arg_basic_auth) {
# "basic_auth" request argument is present,
# append "/decode_basic_auth/<BASE64_token>" to the URI
# and go to the next location block
rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth last;
}
# No "basic_auth" request argument present,
# can do a proxy call from here without setting authorization headers
...
}
location /decode_basic_auth/ {
# This will be an internal location only
internal;
# Remove "basic_auth" request argument from the list of arguments
if ($args ~* (.*)(^|&)basic_auth=[^&]*(|$)&?(.*)) {
set $args ;
}
# Some hostname for processing proxy subrequests
proxy_set_header Host internal.basic.auth.localhost;
# Do a subrequest to ourselfs, preserving other request arguments
proxy_pass http://127.0.0.1$uri$is_args$args;
}
}
# Additional server block for proxy subrequests processing
server {
listen 80;
server_name internal.basic.auth.localhost;
# Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
proxy_set_header Authorization "Basic ";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>$is_args$args;
}
# Do not serve other requests
location / {
return 444;
}
}
也许这不是一个非常优雅的解决方案,但它已经过测试并且有效。
OpenResty / ngx_http_lua_module
这可以很容易地用openresty or ngx_http_lua_module using ngx.escape_uri函数解决:
server {
listen 80 default_server;
...
location / {
set $auth $arg_basic_auth;
if ($args ~* (.*)(^|&)basic_auth=[^&]*(|$)&?(.*)) {
set $args ;
}
rewrite_by_lua_block {
ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
}
proxy_set_header Authorization "Basic $auth";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>;
}
}