Nginx-解码URL查询参数并将其作为请求标头转发

时间:2019-01-14 11:17:41

标签: nginx basic-authentication urlencode url-encoding

我需要以查询参数(例如user:pass)的形式向nginx发送一些基本的身份验证凭据(例如http://example.com?BASIC_AUTH=dXNlcjpwYXNz),并能够以更常见的{{1 }}标头形式指向代理后面的目标服务器。

我已经能够使用正则表达式检索编码的身份验证字符串的值。问题在于,该值通常可能包含一些需要在URL中进行百分比编码的字符。 Es。 Authorization: Basic dXNlcjpwYXNz-> user:pass!变成?BASIC_AUTH=dXNlcjpwYXNzIQ==

因此,当我将请求转发到目标服务器时,我最终指定了目标服务器将拒绝的?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D,给出了401 Unauthorized。

在设置Authorization标头之前,如何强制nginx解码auth字符串?预先感谢您的帮助。

注意:由于某些特定于应用程序的限制,我无法首先在Authorization标头中发送auth字符串。

1 个答案:

答案 0 :(得分:1)

“纯” nginx解决方案

不幸的是,nginx没有提供丰富的字符串操作集。我认为没有办法通过某些字符串进行全局搜索和替换(如果我们可以将所有%2B替换为+,将%2F替换为{{ 1}}和/%3D)。但是,在某些情况下,nginx会对某些字符串执行urldecoding-当此字符串成为URI的一部分时,它将被转发到上游代理服务器。

因此,我们可以向URI添加=请求参数的值,并向我们自己发出代理请求:

BASIC_AUTH

也许这不是一个非常优雅的解决方案,但是它已经过测试并且可以正常工作。

OpenResty / ngx_http_lua_module

使用openresty函数,可以用ngx_http_lua_modulengx.escape_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$1 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=[^&]*(\2|$)&?(.*)) {
            set $args $1$3$4;
        }
        # 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 $1";
        # Setup other HTTP headers here
        ...
        proxy_pass http://<upstream_server>$2$is_args$args;
    }

    # Do not serve other requests
    location / {
        return 444;
    }
}