添加额外的 HTTP 请求标头

HTTP 请求包含 User-Agent 或 Content-Type 等标头。除了浏览器附加的标头之外,Android 应用还可以通过 EXTRA_HEADERS intent extra 添加 Cookie 或引荐来源等额外的标头。出于安全考虑,Chrome 会根据 intent 的启动方式和位置过滤某些额外的标头。

由于客户端和服务器不归同一方所有,因此跨源请求需要额外的安全层。本指南介绍了如何通过 Chrome 自定义标签页启动此类请求,即从在浏览器标签页中打开网址的应用启动的 intent。在 Chrome 83 之前,开发者可以在启动自定义标签页时添加任何标头。从 83 版开始,Chrome 开始滤除除已列入许可名单的跨源标头之外的所有跨源标头,因为未列入许可名单的标头会带来安全风险。从 Chrome 86 开始,如果服务器和客户端使用数字资产链接相关联,则可以将未列入许可名单的标头附加到跨源请求。下表总结了此行为:

Chrome 版本 允许的 CORS 标头
Chrome 83 之前 已列入许可名单、未列入许可名单
Chrome 83 至 Chrome 85 approvelisted
从 Chrome 86 开始 已列入批准名单、未列入批准名单(设置数字资产关联后)

表 1:过滤未列入许可名单的 CORS 标头。

本文介绍了如何在服务器和客户端之间设置经过验证的连接,并使用该连接发送已列入许可名单和未列入许可名单的 HTTP 标头。如需了解相关代码,您可以跳至向自定义标签页 intent 添加额外标头

背景

已列入许可名单的 CORS 请求标头与未列入许可名单的 CORS 请求标头

跨源资源共享 (CORS) 允许来自一个源的 Web 应用请求其他源的资源。CORS 许可名单中的标头列表在 HTML 标准中维护。下表显示了已列入许可名单的标头示例:

标题 说明
accept-language 声明客户端理解的自然语言
content-language 描述面向当前受众群体的语言
content-type 指示资源的媒体类型

表 2:已列入许可名单的 CORS 标头示例。

已列入许可名单的标头被视为安全,因为它们不包含敏感的用户信息,并且不太可能导致服务器执行可能造成损害的操作。

以下表格显示了不在许可名单中的标头示例:

标题 说明
bearer-token 在服务器上对客户端进行身份验证
表示请求来源
饼干 包含由服务器设置的 Cookie

表 3:未列入许可名单的 CORS 标头示例。

HTML 标准不建议将未列入许可名单的标头附加到 CORS 请求,并且服务器假定跨源请求仅包含列入许可名单的标头。从跨源网域发送未列入许可名单的标头会允许恶意第三方应用伪造标头,滥用 Chrome(或其他浏览器)存储并附加到请求的用户 Cookie。这些 Cookie 可以验证恶意服务器交易,而否则无法验证。

将 CORS 批准名单中的标头附加到自定义标签页请求

自定义标签页是一种在自定义浏览器标签页中启动网页的特殊方式。您可以使用 CustomTabsIntent.Builder() 创建自定义标签页 intent。您还可以使用带有 Browser.EXTRA_HEADERS 标志Bundle 将标头附加到这些 intent:

CustomTabsIntent intent = new CustomTabsIntent.Builder(session).build();

Bundle headers = new Bundle();
headers.putString("bearer-token", "Some token");
headers.putString("redirect-url", "Some redirect url");   
intent.intent.putExtra(Browser.EXTRA_HEADERS, headers);

intent.launchUrl(Activity.this, Uri.parse("http://www.google.com"));

我们可以随时将已批准的标头附加到自定义标签页 CORS 请求。不过,Chrome 默认会滤除未列入许可名单的标头。虽然其他浏览器的行为可能有所不同,但开发者通常应预期未列入许可名单的标头会被屏蔽。

在自定义标签页中添加未列入许可名单的标头的支持方法是,先使用数字访问链接验证跨源连接。下一部分将介绍如何进行这些设置,以及如何启动包含所需标头的自定义标签页 intent。

向自定义标签页 intent 添加额外标头

如需允许通过自定义标签页 intent 传递未列入许可名单的标头,您必须在 Android 应用和 Web 应用之间设置数字资产链接,以验证作者是否拥有这两款应用。

请按照官方指南设置数字资产关联。对于关联关系,请使用“delegate_permission/common.use_as_origin”,这表示在关联经过验证后,这两个应用属于同一来源。

使用额外标头创建自定义标签页 intent

您可以通过多种方式创建自定义标签页 intent。您可以通过将库添加到 build 依赖项来使用 androidX 中提供的构建器:

MULTI_LINE_CODE_PLACEHOLDER_1

构建 intent 并添加额外的标头:

MULTI_LINE_CODE_PLACEHOLDER_2

自定义标签页连接用于在应用和 Chrome 标签页之间设置 CustomTabsSession。我们需要会话来验证应用和 Web 应用是否属于同一来源。 只有在数字资产关联设置正确的情况下,验证才会通过。

建议调用 CustomTabsClient.warmup()。它允许浏览器应用在后台预初始化,并加快网址打开速度。

MULTI_LINE_CODE_PLACEHOLDER_3

设置在验证后启动 intent 的回调

CustomTabsCallback 已传入会话。我们将其 onRelationshipValidationResult() 设置为在来源验证成功后启动之前创建的 CustomTabsIntent

MULTI_LINE_CODE_PLACEHOLDER_4

绑定自定义标签页服务连接

绑定服务会启动服务,并且最终会调用连接的 onCustomTabsServiceConnected()。别忘了适当地解除服务绑定。绑定和取消绑定通常在 onStart()onStop() activity 生命周期方法中完成。

// Bind the custom tabs service connection.
// Call this in onStart()
CustomTabsClient.bindCustomTabsService(this,
    CustomTabsClient.getPackageName(MainActivity.this, null), connection);

// …
// Unbind the custom tabs service.
// Call this in onStop().
unbindService(connection);

演示版应用代码

如需详细了解自定义标签页服务,请点击此处。如需查看可运行的示例应用,请参阅 android-browser-helper GitHub 代码库。

摘要

本指南演示了如何向自定义标签页 CORS 请求添加任意标头。已批准列表中的标头可以附加到每个自定义标签页 CORS 请求。在 CORS 请求中,未列入许可名单的标头通常被视为不安全,Chrome 会默认滤除这些标头。只有通过数字资产链接进行验证的同源客户端和服务器才能附加它们。