使用 Identity-Aware Proxy 进行用户身份验证

1. 简介

对 Web 应用的用户进行身份验证通常是必要的,并且通常需要在应用中进行特殊编程。对于 Google Cloud Platform 应用,您可以将这些职责交给 Identity-Aware Proxy 服务。如果您只需要限制选定用户具有访问权限,则无需对应用进行任何更改。如果应用需要知道用户的身份(例如,为了在服务器端保留用户的偏好),只需编写极少的应用代码,Identity-Aware Proxy 即可提供该功能。

什么是 Identity-Aware Proxy?

Identity-Aware Proxy (IAP) 是一项 Google Cloud Platform 服务,可用于拦截发送到您应用的 Web 请求、对使用 Google Identity Service 发出请求的用户进行身份验证,并且仅当请求来自您授权的用户时,才允许通过。此外,它还可以修改请求标头,在其中包含有关经过身份验证的用户的信息。

此 Codelab 将指导您创建自己的应用、限制对应用的访问,以及通过 IAP 获取用户身份。

构建内容

在此 Codelab 中,您将使用 Google App Engine 构建一个极简的 Web 应用,然后探索各种使用 Identity-Aware Proxy 来限制对该应用的访问并为其提供用户身份信息的方法。您的应用将:

  • 显示欢迎页面
  • 访问 IAP 提供的用户身份信息
  • 使用加密验证来防范冒用用户身份信息的行为

学习内容

  • 如何使用 Python 3.7 编写和部署简单的 App Engine 应用
  • 如何启用和停用 IAP 以限制对应用的访问权限
  • 如何将 IAP 中的用户身份信息传递给应用
  • 如何以加密方式验证 IAP 中的信息以防仿冒

所需条件

  • 新型网络浏览器,例如 Chrome
  • 具备 Python 编程语言的基础知识

此 Codelab 将重点介绍 Google App Engine 和 IAP。对于不相关的概念,我们仅会略作介绍,但是会提供相应代码块供您复制和粘贴。

2. 准备工作

您将在 Cloud Shell 命令行环境中工作。首先打开该环境并将示例代码提取到该环境中。

启动控制台和 Cloud Shell

在实验页面的左上角,点击“打开 Google 控制台”按钮。您需要使用该按钮下方显示的用户名和密码登录。

对于为您创建并打开的项目,此 Codelab 中的所有命令都将在 Cloud Shell 中执行。点击控制台页面标题右侧的“激活 Cloud Shell”图标,打开 Cloud Shell。您可以在页面的下半部分输入并运行命令。这些命令可以在您自己的 PC 上运行,但您需要先安装并配置所需的开发软件。Cloud Shell 已经拥有您需要的所有软件工具。

下载代码

点击 Cloud Shell 中的命令行区域,以便您可以输入命令。从 GitHub 获取代码,然后切换到代码文件夹:

git clone https://github.com/googlecodelabs/user-authentication-with-iap.git
cd user-authentication-with-iap

此文件夹包含用于此 Codelab 每个步骤的子文件夹。您需要切换到正确的文件夹来执行相应的步骤。

3. 第 1 步 - 部署应用并使用 IAP 保护应用

这是用 Python 3.7 编写的 App Engine 标准应用程序,它只显示“Hello, World”欢迎页面。我们将对它进行部署和测试,然后使用 IAP 来限制对它的访问权限。

查看应用代码

从主项目文件夹更改为包含此步骤代码的 1-HelloWorld 子文件夹。

cd 1-HelloWorld

应用代码位于 main.py 文件中。它使用 Flask Web 框架使用模板的内容响应 Web 请求。该模板文件位于 templates/index.html 中,对于此步骤仅包含纯 HTML。第二个模板文件包含 templates/privacy.html 中的框架示例隐私权政策。

此外还有两个文件:requirements.txt 列出了应用使用的所有非默认 Python 库,app.yaml 用于告知 Google Cloud Platform 这是一个 Python 3.7 App Engine 应用。

您可以使用 cat 命令在 shell 中列出每个文件,如下所示:

cat main.py

或者,您可以点击 Cloud Shell 窗口右上角的铅笔图标打开 Cloud Shell 代码编辑器,然后以此方式检查代码。

在这一步您无需更改任何文件。

部署到 App Engine

现在将应用部署到 Python 3.7 版 App Engine 标准环境

gcloud app deploy

系统可能会要求您选择要部署到的区域。选择您附近显示“支持标准模式”的任意一家。当系统询问您是否要继续时,请输入 Y 表示“是”。

部署应该会在几分钟后完成,届时您会看到一条消息,提示您可以使用 gcloud app browse 查看应用。输入该命令。如果新标签页没有打开,请点击显示的链接,在新标签页中打开,或根据需要将其复制到手动打开的新标签页。由于这是此应用首次运行,因此要在云实例启动后才会显示,此过程需要几秒钟时间。您应该会看到下面的窗口。

1c1c0b166c6023e

从任何连接到互联网的计算机打开该网址都可以看到该网页。访问权限尚未受到限制。

使用 IAP 限制访问

在 Cloud 控制台窗口中,依次点击页面左上角的菜单图标、“安全”和“Identity-Aware Proxy”。

由于这是您首次为此项目启用身份验证选项,您将看到一条消息,提示您必须先配置 OAuth 权限请求页面,然后才能使用 IAP。

点击“配置同意屏幕”按钮。系统随即会打开一个新标签页,供您配置同意屏幕。

在必填空白字段中填写适当的值:

应用名称

IAP 示例

支持人员电子邮件地址

您的电子邮件地址。系统可能已为您填好。

已获授权的域名

应用程序网址的主机名部分,例如iap-example-999999.appspot.com。您可以在之前打开的 Hello World 网页的地址栏中看到该代码。请勿包含该网址的开头 https:// 或结尾/填写此值后,必须按 Enter 键。

应用首页链接

您用来查看应用的网址

应用隐私权政策链接

应用中的隐私权页面链接,与首页链接相同,末尾添加了 /privacy

点击保存。系统会提示您创建凭据。您无需为此 Codelab 创建凭据,因此只需关闭此浏览器标签页即可。

返回 Identity-Aware Proxy 页面并刷新。您现在应该会看到您可以保护的资源列表。在 App Engine 应用行中,点击“IAP”列中的切换按钮以开启 IAP。

您将看到将受 IAP 保护的域名。点击“开启”。

现在,打开浏览器标签页并导航到您的应用的网址。您会看到“使用 Google 账号登录”屏幕,要求您登录才能访问该应用。

使用 Google 或 G Suite 账号登录。您会看到拒绝您访问的屏幕。

您已成功使用 IAP 保护您的应用,但尚未指示 IAP 允许哪些账号通过。

返回控制台的 Identity-Aware Proxy 页面,选中 App Engine 应用旁边的复选框,然后查看页面右侧的边栏。

您需要将每个应获得访问权限的电子邮件地址(或者 Google 群组地址或 G Suite 域名)都添加为成员。点击“添加成员”。输入您的电子邮件地址,然后选择要分配给该地址的 Cloud IAP/IAP-Secured Web App User 角色。您可以采用相同的方式输入更多地址或 G Suite 网域。

点击“保存”。窗口底部会显示“政策已更新”消息。

返回您的应用并重新加载页面。您现在应该可以看到自己的 Web 应用,因为您已使用授权的用户登录。不过,您可能仍会看到“您没有访问权限”提示,因为 IAP 可能不会重新检查您的授权。在这种情况下,请执行以下步骤:

  • 打开网络浏览器,转到首页地址,并在网址末尾添加 /_gcp_iap/clear_login_cookie,例如 https://iap-example-999999.appspot.com/_gcp_iap/clear_login_cookie
  • 您将看到一个新的“使用 Google 账号登录”屏幕,并且您的账号已显示在其中。请勿点击该账号,而应点击“使用其他账号”,然后重新输入您的凭据。
  • 这会让 IAP 重新检查您的访问权限,您现在应该能看到应用的首页。

如果您有权访问其他浏览器,或能够在浏览器中使用无痕模式,并且拥有其他有效的 GMail 或 G Suite 账号,则可以使用该浏览器转到您的应用页面,然后使用另一个账号登录。由于该账号尚未获得授权,因此浏览器会显示“您没有访问权限”页面,而不是您的应用。

4. 第 2 步 - 访问用户身份信息

一旦应用受到 IAP 保护,它就可以使用 IAP 在所传递的 Web 请求标头中提供的身份信息。在此步骤中,该应用将获取已登录用户的电子邮件地址,以及 Google Identity 服务分配给该用户的永久性唯一身份用户 ID。该数据将在欢迎页面中显示给用户。

这是第 2 步,最后一步是在 iap-codelab/1-HelloWorld 文件夹中打开 Cloud Shell。切换到此步骤的文件夹:

cd ~/iap-codelab/2-HelloUser

部署至 App Engine

由于部署需要几分钟时间,因此,请先将应用部署到 Python 3.7 版 App Engine 标准环境:

gcloud app deploy

当系统询问您是否要继续时,请输入 Y 表示“是”。部署应该会在几分钟内完成。等待期间,您可以按照如下所述检查应用文件。

部署准备就绪后,您会看到一条消息,提示您可以使用 gcloud app browse 查看应用。输入该命令。如果浏览器中没有打开新标签页,请复制所显示的链接,按常规的方法在新标签页中将其打开。您应该会看到如下所示的页面:

5b5fb03111258cec

新版应用替换之前的版本可能需要几分钟的时间,请耐心等候。如果需要,请刷新页面,直到看到与上图类似的页面。

检查应用文件

此文件夹包含与第 1 步相同的一组文件,但其中两个文件已更改:main.pytemplates/index.html。程序已改为检索 IAP 在请求标头中提供的用户信息,并且模板现在会显示这些数据。

main.py 中有两行用于获取 IAP 提供的身份数据:

user_email = request.headers.get('X-Goog-Authenticated-User-Email')
user_id = request.headers.get('X-Goog-Authenticated-User-ID')

X-Goog-Authenticated-User- 标头由 IAP 提供,名称不区分大小写,因此可以根据需要采用全部小写或全部大写。render_template 语句现在包含这些值,因此应用可以显示这些信息:

page = render_template('index.html', email=user_email, id=user_id)

index.html 模板可通过用双大括号括住名称来显示这些值:

Hello, {{ email }}! Your persistent ID is {{ id }}.

如您所见,提供的数据的前缀为 accounts.google.com:,表明信息的来源。如果需要,您的应用可以移除冒号前的所有内容(包括冒号)以获取原始值。

关闭 IAP

如果 IAP 被停用或以某种方式被绕过(例如,被同一个云项目中运行的其他应用绕过),此应用会发生什么情况?我们关闭 IAP 来看看。

在 Cloud 控制台窗口中,依次点击页面左上角的菜单图标、“安全”和“Identity-Aware Proxy”。点击 App Engine 应用旁边的 IAP 切换开关以关闭 IAP。

系统会显示警告,指出这样会允许所有用户访问该应用。

刷新应用网页。您应该会看到相同的页面,但没有任何用户信息:

17c850de95fea839

由于应用现在不受保护,用户可以发送似乎已通过 IAP 检查的 Web 请求。例如,您可以通过 Cloud Shell 运行以下 curl 命令(将 <your-url-here> 替换为应用的正确网址):

curl -X GET <your-url-here> -H "X-Goog-Authenticated-User-Email: totally fake email"

网页将显示在命令行上,如下所示:

<!doctype html>
<html>
<head>
  <title>IAP Hello User</title>
</head>
<body>
  <h1>Hello World</h1>

  <p>
    Hello, totally fake email! Your persistent ID is None.
  </p>

  <p>
    This is step 2 of the <em>User Authentication with IAP</em>
    codelab.
 </p>

</body>
</html>

应用无法知道 IAP 已被停用或绕过。对于可能存在风险的情况,第 3 步会显示解决方案。

5. 第 3 步 - 使用加密验证

如果存在 IAP 被关闭或绕过的风险,应用可以检查并确保收到的身份信息有效。这将使用 IAP 添加的第三个 Web 请求标头,名为 X-Goog-IAP-JWT-Assertion。标头的值是一个以加密方式签名的对象,其中还包含用户身份数据。您的应用可以验证数字签名,并使用此对象中提供的数据来确定签名由 IAP 提供且未经篡改。

数字签名验证流程需要执行几个额外的步骤,例如检索最新的一组 Google 公钥。您可以根据有人可能能够关闭或绕过 IAP 的风险以及应用的敏感性,来决定您的应用是否需要这些额外的步骤。

这是第 3 步,也是最后一步,也就是在 iap-codelab/2-HelloUser 文件夹中打开 Cloud Shell。切换到此步骤的文件夹:

cd ~/iap-codelab/3-HelloVerifiedUser

部署到 App Engine

将应用部署到 Python 3.7 版 App Engine 标准环境:

gcloud app deploy

当系统询问您是否要继续时,请输入 Y 表示“是”。部署应该会在几分钟内完成。等待期间,您可以按照如下所述检查应用文件。

部署准备就绪后,您会看到一条消息,提示您可以使用 gcloud app browse 查看应用。输入该命令。如果浏览器中没有打开新标签页,请复制所显示的链接,按常规的方法在新标签页中将其打开。

回想一下,您在第 2 步中停用了 IAP,因此没有向应用提供任何 IAP 数据。您应该会看到如下所示的页面:

8ef2abcc23d96958.png

与之前一样,您可能需要等待几分钟,直到最新版本生效后才能看到页面的新版本。

由于 IAP 已停用,因此未提供用户信息。现在重新开启 IAP。

在 Cloud 控制台窗口中,依次点击页面左上角的菜单图标、“安全”和“Identity-Aware Proxy”。点击 App Engine 应用旁边的 IAP 切换开关以再次启用 IAP。

刷新页面。页面应如下所示:

3a4d93c11f228852

请注意,经过验证的方法提供的电子邮件地址没有 accounts.google.com: 前缀。

如果 IAP 被关闭或绕过,则已验证数据将会缺失或无效,因为该数据不可能具有有效的签名,除非它是由 Google 私钥持有者创建的。

检查应用文件

此文件夹包含与第 2 步相同的一组文件,但更改了两个文件和一个新文件。新文件为 auth.py,它提供了一种 user() 方法来检索和验证经过加密签名的身份信息。更改后的文件是 main.pytemplates/index.html,它们现在会使用该方法的结果。系统还会显示第 2 步中未验证的标头以便进行比较。

新功能主要在 user() 函数中:

def user():
    assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
    if assertion is None:
        return None, None

    info = jwt.decode(
        assertion,
        keys(),
        algorithms=['ES256'],
        audience=audience()
    )

    return info['email'], info['sub']

assertion 是指定请求标头中提供的经过加密签名的数据。代码使用一个库来验证和解码这些数据。验证功能使用 Google 提供的公钥来检查所签名的数据,并了解数据是为哪个受众群体准备的(本质上是受保护的 Google Cloud 项目)。辅助函数 keys()audience() 会收集并返回这些值。

该签名对象具有我们需要的两部分数据:经过验证的电子邮件地址和唯一 ID 值(在 sub 中提供,适用于订阅者的标准字段)。

第 3 步到这里就完成了。

6. 总结

您部署了一个 App Engine Web 应用。在第 1 步中,您将应用的访问权限限制为只有您选择的用户才能访问。在第 2 步中,您检索并显示 IAP 允许访问您应用的用户的身份,并了解了在停用或绕过 IAP 的情况下,这些信息可能被欺骗。在第 3 步中,您验证了用户身份的加密签名断言(不可仿冒)。

7. 清理

您在此 Codelab 中仅使用的 Google Cloud Platform 资源是 App Engine 实例。您每次部署应用时,系统都会创建一个新版本,该版本将继续存在,直到将其删除。退出实验即可删除项目及其中的所有资源。