2

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()内容阶段执行?

我应该使用更好的方法吗?

4

1 回答 1

1

我最终调整了后端使用的 URL 模式,以便可以提前区分私有 URL 和公共 URL。前端逻辑就变得简单了:

  • 请求公共资源:代理到后端
  • 请求私有资源,无授权头:返回 401
  • 请求私有资源,带有授权标头:代理到后端

我不知道我最初的问题(首先代理,然后根据响应代码做出决定)在 OpenResty 中是否可行。但最终我认为新方法更清洁。

于 2020-11-30T14:47:57.393 回答