Bitwarden 贡献文档
⮐ Bitwarden Contributing Documentation我的博客联系我
  • 关于
  • 入门
    • 概述
    • 工具
    • 服务器
      • 设置指南
      • 高级服务器设置
      • 数据库
        • MSSQL
        • 实体框架
      • 事件日志
      • Ingress 隧道
      • SCIM
      • 自托管指南
      • 系统管理门户
      • 单点登录 (SSO)
        • 本地 IdP
        • Okta
      • 故障排除
      • 用户机密
      • 公共 API
    • 网页客户端
      • 网页密码库
        • WebAuthn
      • 浏览器端
        • 生物识别解锁
        • Firefox 隐私模式
      • 桌面端
        • Mac App Store Dev
        • Microsoft Store
        • Native Messaging Test Runner
        • 更新测试
      • CLI
      • 故障排除
    • 移动端
      • Android
        • F-Droid
      • iOS
      • .NET MAUI (legacy)
        • Android
        • iOS
        • watchOS
    • SDK
      • 内部 SDK
      • Secrets Manager
        • Integrations
          • Kubernetes
    • 业务 App
      • 目录连接器
        • JumpCloud
        • OpenLDAP Docker 服务器
      • Key Connector
      • Splunk App
  • 贡献
    • 贡献
    • 代码样式
      • =Android & Kotlin
      • Angular & TypeScript
      • C#
      • =Rust
      • T-SQL
      • =Swift
      • Tailwind
    • 数据库迁移
      • 进化数据库设计
    • 提交签名
    • 拉取请求
      • =贡献审查程序
      • 分支
      • 代码审查
      • UI 审查 - Chromatic
    • 无障碍
    • 依赖管理
    • 功能标记
    • 模板存储库
    • 测试
      • =数据库集成测试
      • 负载测试
      • 单元测试
        • 命名约定
        • 测试结构
    • 修改用户机密
  • 架构
    • 架构
    • 架构决策记录 (ADR)
      • 0001 - Angular Reactive Forms
      • 0002 - Public API for modules
      • 0003 - Adopt Observable Data Services for Angular
      • 0004 - Refactor State Service
      • 0005 - Refactor Api Service
      • 0006 - Clients: Use Jest Mocks
      • 0007 - Manifest V3 sync Observables
      • 0008 - Server: Adopt CQRS
      • 0009 - Composition over inheritance
      • 0010 - Angular Modules
      • 0011 - Scalable Angular Clients folder structure
      • 0012 - Angular Filename convention
      • 0013 - Avoid layered folder structure for request/response models
      • 0014 - Adopt Typescript Strict flag
      • 0015 - Short Lived Browser Services
      • 0016 - Move Decryption and Encryption to Views
      • 0017 - Use Swift to build watchOS app
      • 0018 - Feature management
      • 0019 - Adoption of Web Push
      • 0020 - Observability with OpenTelemetry
      • 0021 - Logging to Standard Output
      • =0022 - Authorization
      • =0023 - Identifying Integrated Clients
    • 移动客户端架构
      • =Android
      • =iOS
        • =推送通知故障排除提示
      • =.NET MAUI (legacy)
        • =概述
        • watchOS
    • =SDK 架构
      • =数据模型
      • =依赖
      • Password Manager
        • Web
          • =互操作性
      • =Secrets Manager
      • =服务器绑定
      • =版本控制和破坏性更改
    • 网络客户端架构
      • 概述
      • 数据模型
      • 表示层
        • Angular
        • CLI
      • =依赖注入
      • 服务层
        • Vision
        • 实现
    • 服务器架构
    • 深度剖析
      • 身份验证
        • 双重身份验证
      • =授权
      • =浏览器自动填充
        • 收集页面详细信息
        • 生成并执行填充脚本
        • 表单提交检测
        • Shadow DOM
        • =内联自动填充菜单
      • Captcha
      • =只读数据库副本
      • 事件日志
      • =FIDO2 和通行密钥
        • =凭据
        • =操作
        • =命名惯例
        • =实现
          • =提供程序
            • =浏览器扩展
          • =依赖方
            • =用于解密的通行密钥
        • =术语表
      • 推送通知
        • 移动端推送通知
        • 其他客户端推送通知
      • =SSH 密钥和代理
        • =SSH 代理
      • =状态提供程序框架
        • =派生状态
    • =安全
      • =定义
      • =原则
        • =P01 - 锁定的密码库是安全的
        • =P02 - 半受损设备密码库的有限安全性
        • =P03 - 完全损坏的系统没有安全性
        • =P04 - 控制密码库数据的访问权限
        • =P05 - 将安全漏洞的影响降至最低
      • =要求
由 GitBook 提供支持
在本页
  • 概述
  • 服务器实现
  • 发送推送令牌到 Azure 通知中心
  • 使用推送令牌发送通知到设备
  • 客户端注册
  • 在移动客户端获取推送令牌
  1. 架构
  2. 深度剖析
  3. 推送通知

移动端推送通知

上一页推送通知下一页其他客户端推送通知

最后更新于1年前

对应的官方页面地址

概述

推送通知是一个有些复杂的领域,它们在服务器和客户端上的实现基于不同的维度而有所不同。

从服务器角度来看,Bitwarden 云托管实例和自托管实例的实现方式有所不同。主要区别在于自托管客户端需要通过 Bitwarden 云托管实例中继消息。这是必需的,因为 Bitwarden 只允许向商店分发的 Android 和 iOS 移动应用程序发送通知。

从客户端的角度来看,移动操作系统(Android 和 iOS)的实现方式有所不同。这是因为每个操作系统处理获取和刷新推送令牌的方式不同。

我们将首先了解服务器端的实现,然后再了解客户端获取推送令牌的情况。

服务器实现

发送推送令牌到 Azure 通知中心

移动客户端(无论是 iOS 还是 Android)都会收到一个不透明令牌,该令牌在特定平台的通知服务中代表该物理设备。客户端通过向 Bitwarden API 上的 /devices/identifier/{deviceIdentifier}/token 端点发出 POST 请求,将此令牌发送到服务器。在移动客户端上,这是在 的 OnRegisteredAsync() 方法中完成的。

然后,Bitwarden API 负责将此令牌提交到 Azure 通知中心。在服务器上,不透明推送令牌通过请求中的 access_token 中显示的用户和 URL 路径中的物理设备与特定用户关联。这将存储在 Device 表中的 SQL 中。

必须认识到,此时我们已将令牌与用户和物理设备的组合关联起来了,因为这两者都被用作在 Azure 通知中心中注册为标记。这就是我们如何确保仅在相应的用户在其他设备上触发后续通知时,才会将后续通知发送到设备。

云端实现

自托管实现

在通过中继推送通知服务时,理解上下文的变化非常重要。中继在运行 Bitwarden API 的两个不同服务器(自托管实例和 Bitwarden 云实例)之间进行通信。这些服务器中的每一个都有不同的 IPushNotificationService 实现。一旦 Bitwarden Cloud API /push/register 端点收到消息,就会像服务本身触发的任何其他推送通知一样对其进行处理。

使用推送令牌发送通知到设备

当客户端更改数据或发送无密码身份验证请求时,服务器就会负责向所有移动客户端发送推送通知,以让它们了解这一变更。

云端实现

向 Azure 通知中心注册时,每个推送令牌都与用户和设备相关联,如上所述。此时,这些标记被用于定位特定通知。对于服务器希望发送的每种类型的通知,都会标记有设备标识符和用户 ID。然后,Azure 通知中心使用这些标记来查找推送令牌并将通知发送到正确的设备。这样可以确保我们只会在用户和设备匹配时才会向设备发送通知。

自托管实现

值得注意的是,从 Cloud API 的角度来看,它处理从 /send 端点接收的消息的方式与处理 Bitwarden 云服务器上的操作生成的消息的方式相同;两者没有区别,并且无论哪种方式都会执行相同的代码。

在推送通知有效负载中不会发送任何解密数据,推送中继代理的数据也不会存储在 Bitwarden Cloud 数据库中。这使得我们的自托管实例能够将其数据与 Bitwarden 云隔离,并且仍然可以使用推送通知。

客户端注册

在移动客户端获取推送令牌

在移动客户端上获取不透明设备推送令牌的过程因移动操作系统而异。

Android

在这里,我们使用状态中的 PushRegisteredToken 来代表最近从 FCM 收到的令牌。它的范围对于给定设备只存在一次,因为 FCM 会将推送令牌分配给给定设备,而与设备上的 Bitwarden 账户无关。

此时,PushRegisteredToken 代表 FCM 分配给设备的令牌。但是,Bitwarden 会在设备上存储每个用户的推送令牌,以便有针对性地定位通知。为了捕获这种粒度级别,我们将 PushCurrentToken 存储在用户级别的状态中。 PushCurrentToken 代表单个用户的推送令牌,该令牌可能与 FCM 分配的令牌不一样。

如果分配给设备的当前令牌与用户的令牌不同,我们会调用 PushNotificationListenerService 上的 OnRegisteredSync() 来:

  • 通过 Bitwarden API 为活动用户注册新的 PushRegisteredToken

  • 将 PushCurrentToken 设置为活动用户的新值

正如我们所看到的,RegisterSync() 仅为活动用户处理注册推送令牌。在设备上有多个用户的情况下,只有在 FCM 发布新令牌时处于活动状态的用户才能通过上述过程获得更新。

iOS

在 iOS 设备上,推送令牌注册是通过 Apple 推送通知服务 (APN) 进行的。

我们每天为设备上的每个账户注册一次推送令牌。不过,从 iOS 收到的令牌很可能每天都是相同的。在某些特殊情况下,例如从备份中恢复设备,会为设备生成不同的令牌。尽管通常不会出现这种情况,但我们每天都会进行检查,以确保 Bitwarden Azure 通知中心是最新的。

如果我们运行的是 Bitwarden 云实例,Bitwarden API 将负责直接与 Azure 通知中心通信以注册推送令牌。这是在 上的 CreateOrUpdateRegistrationAsync() 方法中完成的。

对于自托管实例,自托管实例无法直接与 Bitwarden 的 Azure 通知中心通信。为了向自托管实例提供推送通知,自托管 Bitwarden API 必须通过 上的 CreateOrUpdateRegistrationAsync() 方法向 Azure 通知中心注册。

此 的实现允许自托管 Bitwarden API 通过调用 Bitwarden Cloud API 中 上的 /push/register 端点来注册推送令牌。这将作为 暴露给自托管实例。然后,Bitwarden Cloud API 上的 会像云端注册一样注册推送令牌 - 将其发送到 Azure 通知中心。

对于发送给移动设备的通知,这是在 中处理的。此服务使用 Microsoft.Azure.NotificationHubs SDK 将通知发送到 Azure 通知中心。

与注册推送令牌一样,自托管实例使用 Bitwarden Cloud API 上的 作为代理与 Azure 通知中心进行通信。

自托管 Bitwarden API 调用 Bitwarden Cloud API 上 PushController 上的 /send 端点,将推送负载传输到 Bitwarden Cloud API。然后,云 API 使用与云生成消息相同的 将数据传输到 Azure 通知中心。

Android 推送令牌由 接收。Firebase Cloud Messaging (FCM) 是用于向 Android 设备推送通知的平台通知服务。当 Android 操作系统最初为应用程序获取令牌或更新令牌时,会触发此服务中的 OnNewToken() 方法。

在 OnNewToken() 方法中,我们会更新设备状态中的 PushRegisteredToken 并触发 中的 RegisterAsync() 方法。

上的 RegisterSync() 的职责是判断分配给设备的 PushRegisteredToken 是否与分配给当前用户的 PushCurrentToken 不一样。

对于设备上的其他用户,RegisterSync() 会在他们下次登录应用程序或应用程序切换到他们的账户时启动。这是在 的初始化过程中完成的。对此用户进行相同的比较,在这种情况下,PushRegisteredToken 仍然与该用户的 PushCurrentToken 不同(因为到目前为止,我们只更新了初始用户的 PushCurrentToken)。此时,Bitwarden API 会收到通知,表明后续用户已注册新令牌。

Android 推送通知文档适用于从 Google Play 商店安装的 Bitwarden 应用程序。F-Droid 版本不支持推送通知。这些构建使用 和 。

当用户登录 iOS 应用程序或切换账户时,应用程序会加载 。在 初始化过程中会中,我们会首先检查以确保设备已接受到推送通知。如果没有,则会显示 Bitwarden 推送通知提示。该提示解释了 iOS 请求 Bitwarden 移动应用程序推送通知的原因。

如果用户接受此提示,或者他们已经接受了该提示,则应用程序会检查当前用户是否在最近一天内注册过推送通知。如果他们之前从未注册过,或者已经过去一天以上,Bitwarden 应用程序会向 iOS 注册推送通知,并请求推送令牌。注册是在 的 RegisterAsync() 方法中完成的。RegisterAsync() 会执行 iOS 平台特定的方法,以开始从 APN 请求令牌的过程。

异步接收带有推送令牌的 APN 响应。当设备获得令牌后,会触发 中的 OnRegisteredSuccess() 方法。然后调用 的 OnRegisteredAsync() 方法,同时传递新获得的令牌。该方法负责将推送令牌发送到后端 API,以便将设备和用户组合注册为推送通知。

NotificationHubPushRegistrationService
RelayPushRegistrationService
IPushRegistrationService
PushController
https://push.bitwarden.com
PushController
NotificationHubPushNotificationService
PushController
NotificationHubPushNotificationService
FirebaseMessagingService
AndroidPushNotificationService
AndroidPushNotificationService
GroupingsPage
NoopPushNotificationListenerService
NoopPushNotificationService
GroupingsPage
GroupingsPage
iOSPushNotificationService
iOSPushNotificationHandler
PushNotificationListenerService
PushNotificationListenerService