跳转至

访客标识 Visitor ID

Visitor ID 用于在未登录或无法可靠识别用户的场景中,为浏览器生成稳定的匿名标识。 观测云 Browser RUM SDK 可以把该值注入命中规则的 XHR 和 Fetch 请求头,供 API 网关按访客限流;同时将 当前值写入 RUM 事件的 context.visitor_id,用于关联分析。

Visitor ID 只能作为限流分桶和可观测性关联键,不能用于鉴权、租户隔离、计费或审计。

版本要求:RUM SDK 3.3.15 及以上版本。

开启 Visitor ID

datafluxRum.init() 中配置 visitorId

datafluxRum.init({
  applicationId: "<APPLICATION_ID>",
  site: "<PUBLIC_OPENWAY_URL>",
  clientToken: "<CLIENT_TOKEN>",
  visitorId: {
    enabled: true,
    header: "x-rum-visitor-id",
    match: [
      "https://api.example.com/",
      /^https:\/\/gateway\.example\.com\//,
      function (url) {
        return url.includes("/security/")
      }
    ],
    ttlHours: 24
  }
})
参数 类型 是否必须 默认值 说明
enabled Boolean false 是否开启 Visitor ID。未显式设为 true 时不会生成标识或注入请求头。
header String 开启时必须 要注入的自定义请求头名称。必须为合法的小写可写请求头,不能使用鉴权、CSRF、签名、链路追踪请求头或 SDK 资源关联头 x-rum-resource-id
match Array 开启时必须 非空 URL 匹配列表,支持 String、RegExp 和 Function。任意一项命中即注入请求头。
ttlHours Number 24 Visitor ID 有效时间,单位为小时,必须大于 0。到期后在下一次使用时轮转,不会滑动续期。

请求匹配规则

match 中不同类型的规则按以下方式匹配:

  • String:匹配完整 URL 的前缀,且必须属于同一 Origin(协议、域名、端口)。例如 https://api.example.com 不会命中 https://api.example.com.evil.test/
  • RegExp:针对完整 URL 执行正则匹配;
  • Function:接收完整 URL 并返回 Boolean。

匹配函数抛出的异常会被 SDK 隔离,该规则按未命中处理,不会中断业务请求。SDK 自身的 数据上报请求不会被注入 Visitor ID Header。

浏览器 SDK 只处理初始化后发起的 XHR 和 Fetch。页面导航、图片、表单提交、 sendBeacon、WebSocket 和 Worker 内请求不在此能力范围内。

标识生命周期

同一浏览器在 Visitor ID 有效期内会复用同一个标识,不会为每个请求生成新值。SDK 优先将标识保存到 localStorage;不可用时依次降级到 sessionStorage 和当前页面内存:

存储层级 复用范围
localStorage 同一源下的后续页面访问及标签页
sessionStorage 当前标签页会话
页面内存 当前页面生命周期

以下情况会生成新值:

  • 当前 Visitor ID 超过 ttlHours
  • 业务主动调用 resetVisitorId()
  • 浏览器存储被清理或不可访问,且原有内存状态已结束。

存在多级有效存储记录时,SDK 会选择最新记录;轮转后不会因刷新恢复旧的降级记录。

本地存储成功时生成持久标识;降级到会话存储或内存时生成会话标识。业务不应解析或 依赖标识格式。

请求头与 RUM 数据

请求头只注入命中 match 的 XHR 和 Fetch;开启功能后,当前 Visitor ID 会作为 context.visitor_id 写入 RUM 事件。Visitor ID 与 RUM Session、用户标识和 Trace 相互独立:

  • sessionSampleRatetracingSampleRate 不决定是否注入 Visitor ID Header;
  • Visitor ID 不会创建或强制采样 RUM Session;
  • 未采样 Session 不会上报 RUM 数据,但命中的业务请求仍可携带 Visitor ID Header;
  • resetVisitorId() 不会修改 RUM Session、用户标识或 Trace。

如果业务已经设置了与 visitorId.header 同名的请求头,SDK 会使用当前 Visitor ID 覆盖该值,并在控制台输出警告。该请求头应只由 SDK 管理;未命中 match 的请求 保留原有请求头,包括 injectTraceHeader 返回的同名头。

由同源 iframe 创建、交给当前页面 fetch() 发送的 Request,会保留原有请求方法、 请求体和业务请求头。同一个 XHR 的 send() 因参数错误同步失败后再次发送时, 不会重复追加 Visitor ID;已经注入该请求的标识不会因重试期间的轮转而改变。

全局、View 和事件自定义上下文中的 visitor_id 不会覆盖 SDK 管理的值。 如需在发送前删除或调整上下文,仍可使用 beforeSend

初始化前或等待远程配置期间缓冲的自定义事件,在 SDK 启动后会补齐 Visitor ID; 已具有 SDK 标识快照的事件继续使用该快照,不覆盖事件发生时的其他上下文。

如果请求发出后、Resource 完成前发生 Visitor ID 轮转,该 Resource 的 context.visitor_id 仍保留请求头实际使用的旧值;轮转后的新请求和其他后续 RUM 事件使用新值。

主动轮转

用户确认退出登录或切换账户后,可以主动轮转 Visitor ID:

datafluxRum.resetVisitorId()

调用后,后续新发起的命中请求及后续 RUM 事件使用新值。不要在 401、未认证请求或其他可能 重复触发的失败分支中调用,否则会持续换桶并形成限流绕过通道。

CORS 与安全边界

自定义请求头会让跨域请求触发 CORS 预检。API 服务必须在 Access-Control-Allow-Headers 中允许 visitorId.header,并且 match 只应覆盖可信 API。

Visitor ID 由客户端生成,用户可以伪造、替换或删除。网关不能仅凭该值授予权限; 限流策略还应为缺失请求头的请求提供 IP、会话或其他业务维度的兜底规则,并保持较短的 处罚时间,降低攻击者冒用其他访客 ID 造成误封的影响。

Cloudflare 限流示例

以下 Cloudflare Advanced Rate Limiting 模板包含两条规则:有 Visitor ID Header 时 按访客标识计数,请求头缺失时按源 IP 兜底。它属于可选的网关部署示例,不是 SDK 运行时依赖;未使用 Cloudflare 的业务可以忽略。

部署前必须修改请求路径、请求头名称、阈值和处罚时间。规则默认关闭,应先创建并读回 检查表达式、计数维度和阈值,确认无误后再启用。

Cloudflare Advanced Rate Limiting 模板
{
  "name": "rum_visitor_id_rate_limit",
  "description": "Rate limit matched API requests by the RUM Visitor ID header, with an IP fallback when the header is missing.",
  "kind": "zone",
  "phase": "http_ratelimit",
  "rules": [
    {
      "description": "RUM Visitor ID API rate limit",
      "expression": "(starts_with(http.request.uri.path, \"/api/\") and len(http.request.headers[\"x-rum-visitor-id\"]) > 0)",
      "action": "block",
      "ratelimit": {
        "characteristics": [
          "cf.colo.id",
          "http.request.headers[\"x-rum-visitor-id\"]"
        ],
        "period": 60,
        "requests_per_period": 100,
        "mitigation_timeout": 600,
        "counting_expression": "(starts_with(http.request.uri.path, \"/api/\") and len(http.request.headers[\"x-rum-visitor-id\"]) > 0)",
        "requests_to_origin": true
      },
      "enabled": false
    },
    {
      "description": "Missing RUM Visitor ID IP fallback",
      "expression": "(starts_with(http.request.uri.path, \"/api/\") and len(http.request.headers[\"x-rum-visitor-id\"]) eq 0)",
      "action": "block",
      "ratelimit": {
        "characteristics": ["cf.colo.id", "ip.src"],
        "period": 60,
        "requests_per_period": 30,
        "mitigation_timeout": 600,
        "counting_expression": "(starts_with(http.request.uri.path, \"/api/\") and len(http.request.headers[\"x-rum-visitor-id\"]) eq 0)",
        "requests_to_origin": true
      },
      "enabled": false
    }
  ]
}

Cloudflare 规则格式可参考通过 API 创建限流规则限流参数说明

文档评价

文档内容是否对您有帮助?