共享存储空间和私有聚合实现快速入门

本文档是使用 Shared Storage API 和 Private Aggregation API 的快速入门指南。您需要了解这两个 API,因为 Shared Storage API 用于存储值,而 Private Aggregation API 用于创建可汇总的报告。

目标受众群体:广告技术平台和效果衡量服务提供商。

Shared Storage API

为了防止跨网站跟踪,浏览器纷纷开始分隔所有形式的存储空间,包括本地存储空间、Cookie 等。但在某些用例中,需要使用未分区的存储空间。Shared Storage API 可在不同顶级网站之间提供无限写入权限,同时提供可保护隐私的读取权限。

共享存储空间仅限于上下文来源(sharedStorage 的调用方)。

共享存储空间对每个来源都有容量限制,每个条目的字符数上限也不同。如果达到该限制,系统便不会再存储其他输入。“共享存储空间”说明中列出了数据存储限制。

调用共享存储空间

广告技术平台可以使用 JavaScript 或响应标头写入共享存储空间。从共享存储空间读取仅在名为 worklet 的隔离 JavaScript 环境中进行。

  • 使用 JavaScript 广告技术平台可以在 JavaScript 工作区之外执行特定的 Shared Storage 函数,例如设置、附加和删除值。不过,读取共享存储空间和执行私有汇总等功能必须通过 JavaScript 工作函数来完成。可在 JavaScript 工作函数之外使用的方法可在拟议的 API Surface - 工作函数之外中找到。

    如需了解在操作期间在 worklet 中使用的这些方法,请参阅提议的 API Surface - 在 worklet 中

  • 使用响应标头

    与 JavaScript 类似,只有在 Shared Storage 中设置、附加和删除值等特定函数才能使用响应标头执行。如需在响应标头中使用共享存储空间,必须在请求标头中添加 Shared-Storage-Writable: ?1

    如需从客户端发起请求,请根据您选择的方法运行以下代码:

    • 使用 fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • 使用 iframeimg 标记

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • 将 IDL 属性与 iframeimg 标记搭配使用

      let iframe = document.getElementById("my-iframe");
      iframe.sharedStorageWritable = true;
      iframe.src = "https://a.example/path/for/updates";
      

如需了解详情,请参阅共享存储空间:响应标头

写入共享存储空间

如需写入 Shared Storage,请从 JavaScript 工作区内或外部调用 sharedStorage.set()。如果从 worklet 外部进行调用,系统会将数据写入发出调用的浏览上下文的源。如果从 worklet 内部调用,系统会将数据写入加载了 worklet 的浏览上下文的来源。设置的密钥的到期日期为上次更新后的 30 天。

ignoreIfPresent 字段为可选字段。如果存在且设置为 true,则如果键已存在,系统不会对其进行更新。密钥到期时间会延长至 set() 调用后的 30 天,即使密钥未更新也是如此。

如果在同一页面加载期间使用同一键多次访问共享存储空间,则该键的值会被覆盖。如果键需要保留之前的值,最好使用 sharedStorage.append()

  • 使用 JavaScript

    在 worklet 之外:

    window.sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: false });
    // Shared Storage: {'myKey': 'myValue2'}
    

    同样,在 worklet 内:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • 使用响应标头

    您还可以使用响应标头写入共享存储空间。为此,请在响应标头中使用 Shared-Storage-Write 以及以下命令:

    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present
    
    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present=?0
    

    多个项可以用英文逗号分隔,并且可以组合使用 setappenddeleteclear

    Shared-Storage-Write :
    set;key="hello";value="world";ignore_if_present, set;key="good";value="bye"
    

附加值

您可以使用附加方法将值附加到现有键。如果键不存在,调用 append() 会创建键并设置值。这可以使用 JavaScript 或响应标头来实现。

  • 使用 JavaScript

    如需更新现有键的值,请在 worklet 内或外部使用 sharedStorage.append()

    window.sharedStorage.append('myKey', 'myValue1');
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.append('myKey', 'myValue2');
    // Shared Storage: {'myKey': 'myValue1myValue2'}
    window.sharedStorage.append('anotherKey', 'hello');
    // Shared Storage: {'myKey': 'myValue1myValue2', 'anotherKey': 'hello'}
    

    如需在 worklet 中附加内容,请执行以下操作:

    sharedStorage.append('myKey', 'myValue1');
    
  • 使用响应标头

    与在共享存储空间中设置值类似,您可以在响应标头中使用 Shared-Storage-Write 传入键值对。

    Shared-Storage-Write : append;key="myKey";value="myValue2"
    

批量更新值

您可以从 JavaScript 工作流内或外部调用 sharedStorage.batchUpdate(),并传入用于指定所选操作的有序方法数组。每个方法构造函数接受与其对应的各个 set、append、delete 和 clear 方法相同的参数。

您可以使用 JavaScript 或响应标头设置锁定:

  • 使用 JavaScript

    可与 batchUpdate() 搭配使用的可用 JavaScript 方法包括:

    • SharedStorageSetMethod():将键值对写入共享存储空间。
    • SharedStorageAppendMethod():将值附加到 Shared Storage 中的现有键。
    • SharedStorageDeleteMethod():从共享存储空间中删除键值对。
    • SharedStorageClearMethod():清除共享存储空间中的所有键。
    sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', 'valueOne'),
    new SharedStorageAppendMethod('keyTwo', 'valueTwo'),
    new SharedStorageDeleteMethod('keyThree'),
    new SharedStorageClearMethod()
    ]);
    
  • 使用响应标头

    Shared-Storage-Write : batchUpdate;methods="set;key=keyOne;value=valueOne, append;key=keyTwo;value=valueTwo,delete;key=keyThree,clear"
    

从共享存储空间读取

您只能在 worklet 中从共享存储空间读取数据。

await sharedStorage.get('mykey');

用于加载 Worklet 模块的浏览上下文的来源决定了系统会读取谁的共享存储空间。

从共享存储空间中删除

您可以使用 JavaScript 从工作区内外或通过使用响应标头与 delete() 搭配使用来从共享存储空间中执行删除操作。如需一次删除所有按键,请使用任一接口中的 clear()

  • 使用 JavaScript

    如需从 Worklet 外部删除共享存储空间中的内容,请执行以下操作:

    window.sharedStorage.delete('myKey');
    

    如需从 Worklet 内删除共享存储空间中的内容,请执行以下操作:

    sharedStorage.delete('myKey');
    

    如需从工作流程之外一次性删除所有键,请执行以下操作:

    window.sharedStorage.clear();
    

    如需从 worklet 中一次性删除所有键,请执行以下操作:

    sharedStorage.clear();
    
  • 使用响应标头

    如需使用响应标头删除值,您还可以在响应标头中使用 Shared-Storage-Write 传递要删除的键。

    delete;key="myKey"
    

    如需使用响应标头删除所有键,请执行以下操作:

    clear;
    

从共享存储空间读取 Protected Audience 兴趣群体

您可以通过共享存储空间 Worklet 读取 Protected Audience 的兴趣群体。interestGroups() 方法会返回一个 StorageInterestGroup 对象数组,其中包括 AuctionInterestGroupGenerateBidInterestGroup 属性。

以下示例展示了如何读取浏览情境兴趣群组以及可以对检索到的兴趣群组执行的一些可能操作。可使用的两种操作是查找兴趣群体数量和查找出价次数最多的兴趣群体。

async function analyzeInterestGroups() {
  const interestGroups = await interestGroups();
  numIGs = interestGroups.length;
  maxBidCountIG = interestGroups.reduce((max, cur) => { return cur.bidCount > max.bidCount ? cur : max; }, interestGroups[0]);
  console.log("The IG that bid the most has name " + maxBidCountIG.name);
}

默认情况下,要读取的兴趣群体的来源取决于 worklet 模块加载时所处浏览环境的来源。如需详细了解默认 worklet 源以及如何更改它,请参阅 Shared Storage API 演示中的“执行 Shared Storage 和 Private Aggregation”部分

选项

所有 Shared Storage 修饰符方法都支持将可选的选项对象作为最后一个参数。

withLock

withLock 选项是可选的。如果指定了此选项,则该方法会先使用 Web Locks API 为定义的资源获取锁,然后再继续操作。请求锁定时,会传递锁定名称。该名称代表一个资源,其使用情况会在源代码中的多个标签页、工作器或代码之间协调。

withLock 选项可与以下共享存储空间修饰符方法搭配使用:

  • set
  • append
  • 删除
  • 清除
  • 批量更新

您可以使用 JavaScript 或响应标头设置锁定:

  • 使用 JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • 使用响应标头

    Shared-Storage-Write : set;key="myKey";value="myValue";with_lock="myResource"
    

共享存储空间锁会按数据来源进行分区。这些锁与使用 LockManager request() 方法获取的任何锁(无论是在 window 还是 worker 上下文中)无关。不过,它们与在 SharedStorageWorklet 上下文中使用 request() 获取的锁共享相同的范围。

虽然 request() 方法支持各种配置选项,但在共享存储空间中获取的锁定始终遵循以下默认设置:

  • mode: "exclusive":无法同时持有同名的其他锁。
  • steal: false:系统不会释放同名现有锁,以便满足其他请求。
  • ifAvailable: false:请求会无限期等待,直到锁可用。
何时使用 withLock

在可能同时运行多个 worklet 的情况下(例如,网页上有多个 worklet,或不同标签页中有多个 worklet),并且每个 worklet 都查看相同的数据时,锁会很有用。在这种情况下,最好使用锁封装相关的工作流程代码,以确保一次只有一个工作流程处理报告。

锁的另一个用途是,如果 worklet 中需要一起读取多个键,并且应同步它们的状态。在这种情况下,应使用锁封装 get 调用,并确保在写入这些键时获取相同的锁。

锁的顺序

由于 Web 锁的特性,修饰符方法可能不会按照您定义的顺序执行。如果第一个操作需要锁定且延迟,则第二个操作可能会在第一个操作完成之前开始。

例如:

// This line might pause until the lock is available.
sharedStorage.set('keyOne', 'valueOne', { withLock: 'resource-lock' });

// This line will run right away, even if the first one is still waiting.
sharedStorage.set('keyOne', 'valueTwo');
修改多个键的示例

此示例使用锁来确保 worklet 中的读取和删除操作同时发生,以防止来自 worklet 外部的干扰。

以下 modify-multiple-keys.js 示例使用 modify-lockkeyOnekeyTwo 设置新值,然后从 Worklet 执行 modify-multiple-keys 操作:

// modify-multiple-keys.js
sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', calculateValueFor('keyOne')),
    new SharedStorageSetMethod('keyTwo', calculateValueFor('keyTwo'))
], { withLock: 'modify-lock' });

const modifyWorklet = await sharedStorage.createWorklet('modify-multiple-keys-worklet.js');
await modifyWorklet.run('modify-multiple-keys');

然后,在 modify-multiple-keys-worklet.js 中,您可以使用 navigator.locks.request() 请求锁定,以便根据需要读取和修改密钥

// modify-multiple-keys-worklet.js
class ModifyMultipleKeysOperation {
  async run(data) {
    await navigator.locks.request('modify-lock', async (lock) => {
      const value1 = await sharedStorage.get('keyOne');
      const value2 = await sharedStorage.get('keyTwo');

      // Do something with `value1` and `value2` here.

      await sharedStorage.delete('keyOne');
      await sharedStorage.delete('keyTwo');
    });
  }
}
register('modify-multiple-keys', ModifyMultipleKeysOperation);

上下文切换

共享存储空间数据会写入发起调用的浏览上下文的来源(例如 https://example.adtech.com)。

使用 <script> 标记加载第三方代码时,代码会在嵌入程序的浏览上下文中执行。因此,当第三方代码调用 sharedStorage.set() 时,系统会将数据写入嵌入者的共享存储空间。当您在 iframe 中加载第三方代码时,该代码会收到一个新的浏览上下文,其来源是 iframe 的来源。因此,从 iframe 发出的 sharedStorage.set() 调用会将数据存储到 iframe 源的共享存储空间中。

第一方环境

如果第一方网页嵌入了调用 sharedStorage.set()sharedStorage.delete() 的第三方 JavaScript 代码,则键值对会存储在第一方环境中。

存储在嵌入了第三方 JavaScript 的第一方网页中的数据。

第三方环境

您可以通过创建 iframe 并在 iframe 内的 JavaScript 代码中调用 set()delete(),将键值对存储在广告技术平台或第三方上下文中。

存储在广告技术平台或第三方环境中的数据。

Private Aggregation API

如需衡量存储在 Shared Storage 中可汇总的数据,您可以使用 Private Aggregation API。

如需创建报告,请在 worklet 内使用存储分区和值调用 contributeToHistogram()。该存储分区由一个无符号 128 位整数表示,该整数必须作为 BigInt 传递给函数。该值为正整数。

为保护隐私,报告的载荷(包含存储分区和值)会在传输过程中加密,并且只能使用汇总服务进行解密和汇总。

浏览器还会限制网站对输出查询的贡献。具体而言,贡献预算会限制在给定时间范围内,针对给定浏览器在所有分桶中的单个网站的所有报告的总和。如果超出当前预算,系统将不会生成报告。

privateAggregation.contributeToHistogram({
  bucket: BigInt(myBucket),
  value: parseInt(myBucketValue)
});

执行 Shared Storage 和 Private Aggregation

您必须创建一个工作函数才能访问共享存储空间中的数据。为此,请使用 worklet 的网址调用 createWorklet()。默认情况下,将 createWorklet() 与共享存储空间搭配使用时,数据分区来源将是发起浏览上下文的来源,而不是 Worklet 脚本本身的来源。

如需更改默认行为,请在调用 createWorklet 时设置 dataOrigin 属性。

  • dataOrigin: "context-origin":(默认)数据存储在发起浏览上下文来源的共享存储空间中。
  • dataOrigin: "script-origin":数据存储在工作区脚本源的共享存储空间中。请注意,您必须选择启用此模式,才能使用此模式。
sharedStorage.createWorklet(scriptUrl, {dataOrigin: "script-origin"});

如需选择启用,在使用 "script-origin" 时,脚本端点必须使用标头 Shared-Storage-Cross-Origin-Worklet-Allowed 进行响应。请注意,应为跨源请求启用 CORS

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

使用跨源 iframe

需要 iframe 才能调用共享存储空间 Worklet。

在广告的 iframe 中,通过调用 addModule() 加载 worklet 模块。如需运行在 sharedStorageWorklet.js worklet 文件中注册的方法,请在同一广告 iframe JavaScript 中调用 sharedStorage.run()

const sharedStorageWorklet = await window.sharedStorage.createWorklet(
  'https://any-origin.example/modules/sharedStorageWorklet.js'
);
await sharedStorageWorklet.run('shared-storage-report', {
  data: { campaignId: '1234' },
});

在 worklet 脚本中,您需要创建一个包含异步 run 方法的类,并将其 register 以便在广告的 iframe 中运行。在 sharedStorageWorklet.js 中:

class SharedStorageReportOperation {
  async run(data) {
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}
register('shared-storage-report', SharedStorageReportOperation);

使用跨源请求

借助 Shared Storage API 和 Private Aggregation API,您无需跨源 iframe 即可创建跨源 worklet。

第一方网页还可以调用对跨源 JavaScript 端点的 createWorklet() 调用。创建 worklet 时,您需要将 worklet 的数据分区源设置为脚本源。

async function crossOriginCall() {
  const privateAggregationWorklet = await sharedStorage.createWorklet(
    'https://cross-origin.example/js/worklet.js',
    { dataOrigin: 'script-origin' }
  );
  await privateAggregationWorklet.run('pa-worklet');
}
crossOriginCall();

跨源 JavaScript 端点必须使用 Shared-Storage-Cross-Origin-Worklet-Allowed 标头进行响应,并注意为请求启用了 CORS。

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

使用 createWorklet() 创建的工作流程将具有 selectURLrun()addModule() 不支持此操作。

class CrossOriginWorklet {
  async run(data){
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}

后续步骤

以下页面介绍了 Shared Storage API 和 Private Aggregation API 的重要方面。

熟悉这些 API 后,您就可以开始收集报告了。这些报告会以 JSON 格式在请求正文中作为 POST 请求发送到以下端点。

  • 调试报告 - context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • 报告 - context-origin/.well-known/private-aggregation/report-shared-storage

收集报告后,您可以使用本地测试工具进行测试,也可以设置适用于汇总服务的可信执行环境来获取汇总报告。

分享反馈

您可以在 GitHub 上分享对 API 和文档的反馈。