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>;
    }
}