使用 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 服务对发出请求的用户进行身份验证,并仅允许来自已获授权的用户的请求通过。此外,它还可以修改请求标头,在其中包含有关经过身份验证的用户的信息。

此 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.txtapp.yaml,前者列出应用所使用的所有非默认 Python 库,后者告诉 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.png

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

使用 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 群组地址或 GSuite 域名)添加为成员。点击“添加成员”。输入您的电子邮件地址,然后选择为该地址分配 Cloud IAP/IAP-Secured Web 应用用户角色。您可以通过同样的方式输入更多地址或 GSuite 域名。

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

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

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

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

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

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

这是第 2 步,上一步结束时,您的 Cloud Shell 处于 iap-codelab/1-HelloWorld 文件夹中。切换到此步骤所对应的文件夹:

cd ~/iap-codelab/2-HelloUser

部署到 App Engine

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

gcloud app deploy

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

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

5b5fb03111258cec.png

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

检查应用文件

此文件夹包含的文件集与第 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.png

由于应用现在不受保护,用户可以发送似乎已通过 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 步,上一步结束时,您的 Cloud Shell 处于 iap-codelab/2-HelloUser 文件夹中。切换到此步骤所对应的文件夹:

cd ~/iap-codelab/3-HelloVerifiedUser

部署到 App Engine

将应用部署到适用于 Python 3.7 的 App Engine 标准环境:

gcloud app deploy

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

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

提醒一下,您在第 2 步中停用了应用内购买,因此应用不会提供任何应用内购买数据。您应该会看到如下所示的页面:

8ef2abcc23d96958.png

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

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

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

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

3a4d93c11f228852.png

请注意,验证方法所提供的电子邮件地址没有 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 实例。每次部署应用时,系统都会创建一个新版本,并且该版本会一直存在,直到被删除。退出实验以删除项目及其中的所有资源。