har*_*arm 7 nginx proxy redirect
我有一个上游服务器处理我们网站的登录。成功登录后,我想将用户重定向到站点的安全部分。在登录失败时,我想将用户重定向到登录表单。
上游服务器200 OK
在登录成功和401 Unauthorized
登录失败时返回。
这是我的配置的相关部分:
{
error_page 401 = @error401
location @error401 {
return 302 /login.html # this page holds the login form
}
location = /login { # this is the POST target of the login form
proxy_pass http://localhost:8080;
proxy_intercept_errors on;
return 302 /secure/; # without this line, failures work. With it failed logins (401 upstream response) still get 302 redirected
}
}
Run Code Online (Sandbox Code Playgroud)
此设置在成功登录时有效。客户端使用 302 重定向。这在登录失败时不起作用。上游服务器返回 401,我预计它error_page
会启动。但我仍然得到 302。如果我删除该return 302 /secure/
行,则重定向到登录页面会起作用。所以似乎我可以拥有其中一个,但不能同时拥有。
奖金问题;我怀疑我处理该error_page
命名位置的方式是 The Way。我这样做是否正确?
编辑:原来return
在location
块中有 a使得 Nginx 根本不使用proxy_pass
。所以没有命中错误页面是有道理的。然而,如何做到这一点的问题仍然存在。
该问题的确切解决方案是使用 Nginx 的 Lua 功能。
在 Ubuntu 16.04 上,您可以安装支持 Lua 的 Nginx 版本:
$ apt install nginx-extra
Run Code Online (Sandbox Code Playgroud)
在其他系统上可能会有所不同。您也可以选择安装 OpenResty。
使用 Lua,您可以完全访问上游响应。请注意,您似乎可以通过$upstream_status
变量访问上游状态。并且在某种程度上您可以这样做,但是由于在 Nginx 中评估“if”语句的方式,您不能$upstream_status
在“if”语句条件中使用。
使用 Lua,您的配置将如下所示:
location = /login { # the POST target of your login form
rewrite_by_lua_block {
ngx.req.read_body()
local res = ngx.location.capture("/login_proxy", {method = ngx.HTTP_POST})
if res.status == 200 then
ngx.header.Set_Cookie = res.header["Set-Cookie"] # pass along the cookie set by the backend
return ngx.redirect("/shows/")
else
return ngx.redirect("/login.html")
end
}
}
location = /login_proxy {
internal;
proxy_pass http://localhost:8080/login;
}
Run Code Online (Sandbox Code Playgroud)
很直接。唯一的两个怪癖是读取请求正文以传递 POST 参数以及在对客户端的最终响应中设置 cookie。
经过社区的大力推动,我实际上最终做的是在客户端处理上游流响应。这让上游服务器保持不变,我的 Nginx 配置很简单:
location = /login {
proxy_pass http://localhost:8080;
}
Run Code Online (Sandbox Code Playgroud)
客户端初始化请求处理上游响应:
<body>
<form id='login-form' action="/login" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit">
</form>
<script type='text/javascript'>
const form = document.getElementById('login-form');
form.addEventListener('submit', (event) => {
const data = new FormData(form);
const postRepresentation = new URLSearchParams(); // My upstream auth server can't handle the "multipart/form-data" FormData generates.
postRepresentation.set('username', data.get('username'));
postRepresentation.set('password', data.get('password'));
event.preventDefault();
fetch('/login', {
method: 'POST',
body: postRepresentation,
})
.then((response) => {
if (response.status === 200) {
console.log('200');
} else if (response.status === 401) {
console.log('401');
} else {
console.log('we got an unexpected return');
console.log(response);
}
});
});
</script>
</body>
Run Code Online (Sandbox Code Playgroud)
上面的解决方案实现了我的目标,即明确分离关注点。身份验证服务器不知道调用者想要支持的用例。
虽然我完全同意@michael-hampton,即这个问题不应该由nginx 处理,但您是否尝试过error_page
进入位置块:
{
location @error401 {
return 302 /login.html # this page holds the login form
}
location = /login { # this is the POST target of the login form
proxy_pass http://localhost:8080;
proxy_intercept_errors on;
error_page 401 = @error401;
return 302 /secure/; # without this line, failures work. With it failed logins (401 upstream response) still get 302 redirected
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
18199 次 |
最近记录: |