为了账号安全,请及时绑定邮箱和手机立即绑定

OAuth2代理认证流程详解——第二部分

如果你还没有阅读本文的第一篇,可以在这里看一下:
https://medium.com/devops-dev/securing-microservice-apis-with-oauth2-proxy-a-complete-project-71fabc79147d

这篇文章里有

  • 更多关于OAuth2流程的内容。
  • CSRF令牌(Token)。
  • CORS及其预检请求。
  • OAuth2代理Cookie刷新,以及其他功能。
我们当前的设置和认证步骤

OAuth2 代理认证过程 (OAuth2 代理认证)
  1. 用户请求访问: 用户尝试访问需要身份验证的受保护资源(todo-api),我们的nginx服务器(反向代理)将用户重定向到OAuth2代理。
  2. OAuth2代理检查_oauth2_proxyCookie: OAuth2代理检查用户是否已经完成身份验证,通过查看用户是否具有有效的 _oauth2_proxy cookie。
  3. 没有_oauth2_proxyCookie: 如果 _oauth2_proxy cookie 不存在或无效,OAuth2代理将用户重定向到配置的身份验证提供商(Google)。
  4. 用户身份验证: 用户登录到身份验证提供商并授予应用访问其信息的权限。
  5. 身份验证提供者发送授权: 成功验证后,身份验证提供者(Google)将授权令牌或访问代码发送回OAuth2代理。
  6. OAuth2代理授予访问权限: OAuth2代理使用授权令牌验证用户身份,并通过设置有效的 _oauth2_proxy cookie来授予访问权限。
  7. 用户访问受保护资源(todo-api): 使用有效的 _oauth2_proxy cookie,用户将被重定向回原始应用,并可以访问该受保护资源。
  8. 访问被允许: 如果 _oauth2_proxy cookie有效,OAuth2代理允许用户访问受保护的资源而无需重新进行身份验证。
我们这样分四个步骤怎么样?

在使用带有基于cookie认证的OAuth2代理时,我们有常规的认证流程。

让我们通过拆解它为四个步骤来讨论它在我们项目中的具体应用。

步骤 01:OAuth2 代理检查_oauth2_proxy cookie:

OAuth2 代理检查 _oauth2_proxy cookie:

OAuth2 代理检查 _oauth2_proxy cookie 是否存在

当用户访问安全的资源时,会通过OAuth2代理进行身份验证。

正如你可能还记得的,在之前的 story中,这是通过nginx.conf中的auth_request指令完成的。

OAuth2 代理在允许访问之前会检查 cookie 是否存在且有效。如果检查失败,用户将被引导至设定的认证提供商,我们这里使用的是 Google OAuth2

步骤 02:OAuth2 代理认证流程启动

启动 OAuth2 代理认证

在我们的设置中,我们通过调用 /oauth2/start 端点开始认证。这是 OAuth2 代理的标准启动端点。

这样一来,浏览器会设置一个CSRF cookie,之后用户会被引导至配置的认证提供者,我们这里用的是Google。

用户可以通过提供方的登录页面进行登录,然后会被重定向回应用程序。如果你已经按照了上一篇文章的内容,你应该已经体验过这样的情况了。

CSRF 防护令牌?

OAuth2 代理使用 CSRF(跨站请求伪造)Cookie 作为安全措施,防止 CSRF 攻击。当用户访问 /oauth2/start 端点时开始 OAuth2 认证流程,OAuth2 代理会生成一个 CSRF 令牌,并将其设置为用户的浏览器 Cookie。

当用户被重定向到身份验证提供商(例如 Google)进行登录时,CSRF 令牌会包含在请求里。验证提供商验证了用户凭证后,它会将用户重定向回 OAuth2 代理处,并附带 CSRF 令牌返回。

OAuth2 代理会验证从认证提供商收到的 CSRF 令牌是否与用户浏览器中设置的令牌一致。这确保请求确实是该用户发起的身份验证请求,有助于防止恶意网站让用户在未同意的情况下在其他网站上执行操作的 CSRF 攻击。

您可以在这里了解更多关于CSRF防护的内容 这里

步骤 03:设置好 OAuth2 代理 cookie 并将用户重定向

设置 _oauth2_proxy 的 cookie 并重定向回应用页面

登录之后,您会被重定向到在 oauth2_proxy 变量中设置的重定向 URL。

如你所知,在之前的文章,我们在创建 Google OAuth2 同意屏幕和凭证的过程中,授权了该重定向网址(URI)。

OAuth2 代理接着验证身份验证是否成功。如果验证成功,它将在用户的浏览器中设置一个有效 _oauth2_proxy cookie 并清除 _oauth2_proxy_csrf cookie。

步骤四:发送一个包含有效 _oauth2_proxy cookie 的请求

发送带有有效 oauth2_proxy cookie 的请求

最后,用户将能够使用有效的 _oauth2_proxy cookie(或称令牌)访问受保护的 API 端点。此 cookie 可以包含在请求头里,以访问任何由 oauth2_proxy 鉴权的受保护微服务。

当使用有效的 cookie 重复身份验证过程时,调用 nginx 配置中的 auth_request 指令(例如:/oauth2/auth),OAuth2代理 会向客户端发送 202 Accepted(已接受),并允许访问受保护的微服务。

OAuth2 代理服务器配置选项

现在你应该对我们项目中的 OAuth2 代理认证流程有了更好的理解。让我们谈谈 docker-compose.yml 文件中的 OAuth2 代理配置。

      oauth2-proxy:  
        container_name: oauth2-proxy  
        image: quay.io/oauth2-proxy/oauth2-proxy:latest  
        environment:  
          - OAUTH2_PROXY_CLIENT_ID=<your-client-id>  
          - OAUTH2_PROXY_CLIENT_SECRET=<your-client-secret>  
          - OAUTH2_PROXY_COOKIE_SECRET=<your-cookie-secret>  
          - OAUTH2_PROXY_EMAIL_DOMAINS=*  
          - OAUTH2_PROXY_REVERSE_PROXY=true  
          - OAUTH2_PROXY_REDIRECT_URL=http://localhost/oauth2/callback  
          - OAUTH2_PROXY_COOKIE_SECURE=false  
          - OAUTH2_PROXY_UPSTREAM=http://todo-api:8000  
          - OAUTH2_PROXY_HTTP_ADDRESS=http://0.0.0.0:4180  
          - OAUTH2_PROXY_SET_AUTHORIZATION_HEADER=true  
          - OAUTH2_PROXY_SET_XAUTHREQUEST=true  
          - OAUTH2_PROXY_WHITELIST_DOMAINS=.localhost:3000  
        command:  
          # 设置HTTP地址和上游服务,使oauth2-proxy能够正确代理请求
          - --http-address=0.0.0.0:4180  
          # 设置上游服务,使oauth2-proxy能够正确代理请求
          - --upstream=http://todo-api:8000  
          - --skip-provider-button=true  
          - --skip-auth-preflight=true  
        networks:  
          - 网络配置: microservices
  1. 正如我在前一个故事中提到的,**OAUTH2_PROXY_CLIENT_ID****OAUTH2_PROXY_CLIENT_SECRET** 环境变量用于将身份验证提供程序连接到 OAuth2 代理。
  2. **OAUTH2_PROXY_COOKIE_SECRET** 是用于加密和签名我们认证流程中所用 cookie 的密钥。它应该是唯一的,并且对外保密。如果您还记得前一个故事中的内容,您应该记得我们使用 Linux 终端或 Windows PowerShell 生成了一个 cookie 密钥。
  3. **OAUTH2_PROXY_EMAIL_DOMAINS** 指定允许的认证电子邮件域。使用 ***** 代表允许任何电子邮件域。
  4. **OAUTH2_PROXY_REVERSE_PROXY** 表示 OAuth2 代理运行在反向代理后面(在本例中是 nginx),并且应该使用反向代理提供的头信息以获取客户端信息。
  5. **OAUTH2_PROXY_REDIRECT_URL** 是 OAuth 提供者在认证后重定向用户到的 URL。(第 03 步
  6. 当然!这里是一个修订版本:
  7. **OAUTH2_PROXY_COOKIE_SECURE** 决定了 cookie 是否仅通过 HTTPS 发送。在我们的项目中,我们将此设置为 false,因为我们使用的是本地网络,并且希望避免通过实施自签名证书来增加复杂性。
  8. **OAUTH2_PROXY_UPSTREAM** 指定在认证后代理请求的上游服务器。这就像告诉 OAuth2 代理,“在你检查用户是否已认证后,将他们的请求发送到该服务器。”
  9. **OAUTH2_PROXY_HTTP_ADDRESS** OAuth2 代理应该监听的 HTTP 地址。
  10. **OAUTH2_PROXY_SET_AUTHORIZATION_HEADER** 如果你将此设置为 true,OAuth2 代理将在上游请求中设置 Authorization 头部,其中包含用户的访问令牌。
  11. **OAUTH2_PROXY_SET_XAUTHREQUEST** 如果你将此设置为 true,OAuth2 代理将在上游请求中添加 X-Auth-Request-Email 头部,其中包含用户的电子邮件地址。
  12. **OAUTH2_PROXY_WHITELIST_DOMAINS** 指定 OAuth2 代理允许的域名。这些域名列表之外的请求会被拒绝。(这是因为你的重定向请求如果相关域名不在列表中也会被拒绝。)
    示例:如果我们在列表中没有指定 .localhost:3000,当我们调用 http:localhost/oauth2/auth/start?**rd=http%3A%2F%2Flocalhost%3A3000** 时,OAuth2 代理将在认证后拒绝重定向到 http://localhost:3000
  13. **--skip-provider-button** 这个选项告诉 OAuth2 代理不要显示提供者选择页面。如果您只配置了一个 OAuth 提供者,此页面将被跳过,用户将直接被重定向到登录页面。
  14. **--skip-auth-preflight** 这个选项跳过认证预检检查。通常,OAuth2 代理会在处理请求之前检查用户是否已经认证。此检查有助于避免不必要的重定向。但是,如果您将此选项设置为 true,将会跳过此检查,这可能会稍微减少延迟,但可能导致更多的认证重定向。
CORS (跨源资源共享) 和 预检 请求
什么是前置请求?

预检请求(Preflight请求)是一种CORS请求,用于检查服务器是否理解CORS协议,并确认服务器是否了解特定的HTTP方法和头部。

这其实是一个**OPTIONS**请求,使用这些HTTP请求头:Access-Control-Request-MethodOrigin,以及可选的Access-Control-Request-Headers

什么是CORS?

CORS(跨源资源共享,简称CORS) 是一种让网站服务器通知浏览器允许来自其他网站的一些特定请求的方式。

通常,浏览器出于安全原因会阻止网站访问不同域名的资源,但 CORS 允许服务器绕过这种限制。它通过发送特殊的 HTTP 标头来实现这一点,告诉浏览器可以进行这些跨域请求。

跨域请求的基本请求流程包括两个步骤:主要请求和预检请求(Preflight请求)。

  • 主要请求: 从前端JavaScript代码向位于不同域名的服务器发送主要GET或POST请求。此请求包含相关的CORS头,如Origin,用以表明请求的来源。
  • 预检请求: 在发送实际请求之前,浏览器可能会发送一个预检请求(使用OPTIONS方法)到服务器,以检查是否允许根据其设置的CORS策略进行此主要请求。

如果预检请求成功,并且服务器通过CORS头表明主请求被允许,浏览器将发送该请求。

否则,浏览器会阻塞主请求,前端代码就无法访问服务器资源了。

重要

请注意

某些请求不会触发 CORS 预检请求(preflight请求)。这类请求被称为简单请求。

这就是原因,有些请求能够在不设置 **--skip-auth-preflight=true** 的情况下成功到达后端服务器。由于这些请求根本没有发起预检请求(preflight请求),因此能够顺利进行而没有任何问题。

其他请求咋样了?

如果你运行 docker compose up -d 但没有设置 --skip-auth-preflight=true,你会看到一些 简单请求 成功处理了,而有些则没有。

要成为一个简单的请求,它必须满足以下列出的所有条件这里。

比如说,如果你尝试编辑任务,请求可能会因预检请求失败而引发CORS错误。

尽管它在浏览器中存在,如果你仔细检查预飞行请求,你会注意到_oauth2_cookie缺失了,尽管我们在fetch请求中包含了这些凭证?

为什么呢?

CORS 预检请求不能包含凭证。

由于 preflight 请求由浏览器处理 ,所以它们默认情况下不会携带凭证。因此,我们需要设置 **--skip-auth-preflight=true** 以让 OAuth2 代理能够处理这些请求。否则,OAuth2 代理会尝试对 preflight 请求进行身份验证,导致请求失败。

既然我们处理的是来自不同来源的请求,使用 CORS ,这一步是 必须的。在这种情况下,我们必须这么做,因为我们是从 http://localhost:3000/ 请求资源,而资源的来源则是 http://localhost/

你可能在想,为什么即使我们在同一个网络上,并且都使用了“localhost”,它仍然失败了。那是因为 CORS 限制了资源共享,即使源地址名称相同,但如果端口不同,CORS 就不会允许资源共享。这就是 CORS 的标准做法。

你可以在这里了解更多关于预检请求(preflight 请求)和需要凭证的请求的信息。

Cookie的有效期和刷新

你现在对跨域资源共享(CORS)、预检请求(OPTIONS 请求)以及通过OAuth2代理的OAuth 2认证过程有更好的理解了。

如果你还记得上一个故事,你应该记得我们在 Next.js 中间件中是如何处理登录及登出的,以及通过调用 /oauth2/start 端点启动认证过程。

你可能想知道如果 cookie 失效或者过期会发生啥。

由于我们目前的设置中还没有设置任何关于 cookie 过期的时间,所以我们无法明确地看出发生了什么事。

我们可以通过在 Docker Compose 配置文件中设置 **--cookie-expire=15s** 来使 cookie 过期,如下所示。

    oauth2-proxy:  
        container_name: oauth2-proxy  
        image: quay.io/oauth2-proxy/oauth2-proxy:latest  
        environment:  
          - OAUTH2_PROXY_CLIENT_ID=<your-client-id>  
          - OAUTH2_PROXY_CLIENT_SECRET=<your-client-secret>  
          - OAUTH2_PROXY_COOKIE_SECRET=<your-cookie-secret>  
          - OAUTH2_PROXY_EMAIL_DOMAINS=*  
          - OAUTH2_PROXY_REVERSE_PROXY=true  
          - OAUTH2_PROXY_REDIRECT_URL=http://localhost/oauth2/callback  
          - OAUTH2_PROXY_COOKIE_SECURE=false  
          - OAUTH2_PROXY_UPSTREAM=http://todo-api:8000  
          - OAUTH2_PROXY_HTTP_ADDRESS=http://0.0.0.0:4180  
          - OAUTH2_PROXY_SET_AUTHORIZATION_HEADER=true  
          - OAUTH2_PROXY_SET_XAUTHREQUEST=true  
          - OAUTH2_PROXY_WHITELIST_DOMAINS=.localhost:3000  
        command:  
          - --http-address=0.0.0.0:4180  
          - --upstream=http://todo-api:8000  
          - --skip-provider-button=true  
          - --skip-auth-preflight=true  
          - --cookie-expire=15s   
        networks:  
          - microservices

现在退出当前会话,然后再试一次登录,同时打开网络标签。

登录后,尝试与待办事项进行互动,并同时留意请求的状态。

15秒后,你会看到你的请求出现了CORS(跨源资源共享)错误。这是因为我们设置了cookie将在15秒后过期。

现在,如果你检查浏览器中的 cookies,你会发现 **oauth2_proxy** 这个 cookie 不见了,被 **_oauth2_proxy_csrf** 这个 cookie 替换了。

这是因为当**_oauth2_proxy** cookie过期时,OAuth2 代理服务器会删除我们浏览器中的**_oauth2_proxy** cookie并设置**_oauth2_proxy_csrf** cookie,将我们引导回身份提供商以重新验证。

你会在上面的图中看到。即使请求包含 **_oauth2_proxy** cookie凭证,因为它是无效的cookie,OAuth2代理会移除它并设置 **_oauth2_proxy_csrf** cookie来将我们重定向到身份验证提供者进行登录操作。

如果你现在刷新页面,你会被引导到登录页面,因为浏览器中没有 cookie。我们已经在 Next.js 中间件中解决了这个问题。

在当前的实现中,即使用户正在积极地使用我们的待办事项前端应用,也可能由于 cookie 过期失效而被自动登出。这在诸如在线考试系统等应用场景中可能会成为一个问题。

我们可以用刷新 cookie 来搞定这种情况。

我们可以通过使用 cookie 刷新机制来解决这个问题。比如说,设置 --cookie-expire=15s,我们可以在 docker-compose.yml 配置中设置 --cookie-refresh=10s


    oauth2-proxy:  
        container_name: oauth2-proxy  
        image: quay.io/oauth2-proxy/oauth2-proxy:latest  
        environment:  
          - OAUTH2_PROXY_CLIENT_ID=<your-client-id>  
          - OAUTH2_PROXY_CLIENT_SECRET=<your-client-secret>  
          - OAUTH2_PROXY_COOKIE_SECRET=<your-cookie-secret>  
          - OAUTH2_PROXY_EMAIL_DOMAINS=*  
          - OAUTH2_PROXY_REVERSE_PROXY=true  
          - OAUTH2_PROXY_REDIRECT_URL=http://localhost/oauth2/callback  
          - OAUTH2_PROXY_COOKIE_SECURE=false  
          - OAUTH2_PROXY_UPSTREAM=http://todo-api:8000  
          - OAUTH2_PROXY_HTTP_ADDRESS=http://0.0.0.0:4180  
          - OAUTH2_PROXY_SET_AUTHORIZATION_HEADER=true  
          - OAUTH2_PROXY_SET_XAUTHREQUEST=true  
          - OAUTH2_PROXY_WHITELIST_DOMAINS=.localhost:3000  
        command:  
          - --http-address=0.0.0.0:4180  
          - --upstream=http://todo-api:8000  
          - --skip-provider-button=true  
          - --skip-auth-preflight=true  
          - --cookie-expire=15s   
          - --cookie-refresh=10s  
        networks:  
          - microservices

这种设置将在用户主动向OAuth2保护的API发起请求时,每10秒刷新一次OAuth2 cookie。

然而,如果用户在5秒刷新间隔内没有操作,或者在任何情况下超过15秒没有操作,OAuth2 cookie将过期,并且应用程序会提醒用户重新登录。

通过刷新 cookie,我们可以减少这样的问题,并提供更流畅的用户体验。

给大家的一句话

这花了我一点时间,因为个人原因。我主要关注OAuth2代理的关键点,暂时跳过了FastAPI和Todo前端的细节。我将在以后的帖子中介绍对Next.js Todo前端和FastAPI微服务的改进。

谢谢啦。 ☕

谢谢!如果你看到这里,做得不错。

如果你喜欢这个项目,别忘了给它点个 👏 哦。你的支持让我更有动力和热情去创作更多这样的项目。谢谢大家!

编码愉快!!! 🐧

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消