我lua-resty-openidc
用来实现位于我的后端系统前面的 Web UI。
后端提供 REST API,受Authorization
包含 JWT 的标头保护。前端管理一个会话,如果需要登录,则将 Web 用户发送到身份提供者。当 Web 用户有会话时,前端必须查找 JWT,将其添加到 Authorization 标头中,并将请求代理到后端– 相当标准的东西。
不幸的是,我的后端并没有清楚地区分公共资源和私有资源。例如,它可能具有带有 URL 的资源:
/api/public/0
/api/public/1
/api/private/2
/api/private/3
它允许/api/public/{0,1}
在没有授权标头的情况下进行请求,但/api/private/{2,3}
需要授权。前端必须以某种方式处理这个问题。(注意:上面的网址是简化的,真实的不遵循模式,不容易一一列举。)
核心问题是前端无法从请求 URI 中判断是否应该触发登录。它必须是响应式的,将请求代理到后端并检查响应代码。401 代码应该强制客户端登录,但任何其他响应都应该按原样返回。
因此,我不能将我的身份验证逻辑放在一个access_by_lua
块中,因为它们在请求发送到上游(后端)之前在访问阶段运行。
我尝试使用块将我的逻辑移动到内容阶段body_filter_by_lua
:
location /api {
set $session_storage shm;
proxy_set_header X-Forwarded-Host $http_host;
proxy_pass https://backend;
body_filter_by_lua_block {
if ngx.status == ngx.HTTP_UNAUTHORIZED then
ngx.log(ngx.INFO, 'Upstream returned a 401! Triggering auth flow')
local opts = {
discovery = 'https://login-server/.well-known/openid-configuration',
scope = 'openid',
}
local res, err = openidc.authenticate(opts)
if err or not res then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.header.content_type = 'text/html';
ngx.log(ngx.ERR, err)
ngx.say("Forbidden")
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
end
}
}
…但这会因错误而失败(如下所示)。似乎我在请求处理生命周期中设置标头和 cookie 为时已晚:
*194 [lua] body_filter_by_lua:5: Upstream returned a 401! Triggering auth flow while sending to client, client: 10.255.1.2, server: , request: "GET /api/private/2 HTTP/1.1", upstream: "https://backend/api/private/2", host: "frontend.example.org"
*194 [lua] openidc.lua:1363: authenticate(): Error starting session: Attempt to set session cookie after sending out response headers. while sending to client, client: 10.255.1.2, server: , request: "GET /api/private/2 HTTP/1.1", upstream: "https://backend/api/private/2", host: "frontend.example.org"
*194 attempt to set ngx.status after sending out response headers while sending to client, client: 10.255.1.2, server: , request: "GET /api/private/2 HTTP/1.1", upstream: "https://backend/api/private/2", host: "frontend.example.org"
是否可以在Nginx 请求处理openidc.authenticate()
的内容阶段执行?
我应该使用更好的方法吗?