Privet

Privet 是一种由云服务使用的云设备本地发现 API。本文档分为以下几个部分:

  1. 简介:Privet 简介
  2. 发现:本地发现机制
  3. 公告:本地发现公告
  4. API:适用于一般云设备的 Privet API
  5. 打印机 API:打印机使用的 Privet API
  6. 附录:补充图表

1. 简介

联网的云设备具有诸多优势。它们可以使用在线转换服务,在设备离线时托管作业队列,并且可以从世界上的任何地方访问。不过,由于给定用户可访问许多云设备,我们需要提供一种基于位置信息查找最近设备的方法。Privet 协议旨在将云设备的灵活性与合适的本地发现机制绑定在一起,以便在新的环境中轻松发现设备。

此协议的目标如下:
  • 使云设备可在本地被发现
  • 向云服务注册云设备
  • 将已注册的设备与其云端表示形式相关联
  • 启用离线功能
  • 简化实现,以便小型设备可以利用它

Privet 协议包含 2 个主要部分:发现和 API。发现功能用于在本地网络上查找设备,而 API 用于获取有关设备的信息并执行某些操作。在本文档中,设备是指实现 Privet 协议的云连接设备。

2. 发现

发现是一种基于 Zeroconf (mDNS + DNS-SD) 的协议。设备必须实现 IPv4 链路本地寻址。设备必须符合 mDNS 和 DNS-SD 规范。

设备必须根据上述规范执行名称冲突解决。

2.1. 服务类型

DNS 服务发现使用以下格式表示服务类型:_applicationprotocol._transportprotocol。对于 Privet 协议,DNS-SD 的服务类型应为:_privet._tcp

设备也可以实现其他服务类型。建议为设备实现的所有服务类型使用相同的服务实例名称。例如:打印机可以实现“Printer XYZ._privet._tcp”和“Printer XYZ._printer._tcp”服务。这样可以简化用户设置。不过,Privet 客户端只会查找“_privet._tcp”。

除了主要服务类型之外,设备还必须为其对应的子类型宣传 PTR 记录(请参阅 DNS-SD 规范:“7.1. 选择性实例枚举(子类型)”。 格式应如下所示: _<subtype>._sub._privet._tcp

目前,唯一支持的设备子类型是 printer。因此,所有打印机都必须宣传两个 PTR 记录:

  • _privet._tcp.local.
  • _printer._sub._privet._tcp.local.

2.2. TXT 记录

DNS 服务发现定义了用于在 TXT 记录中添加有关服务的可选信息的字段。TXT 记录由键值对组成。每个键值对都以长度字节开头,后跟最多 255 个字节的文本。键是第一个“=”字符之前的文本,值是第一个“=”字符之后的文本,直到末尾。规范允许记录中没有值,在这种情况下,将没有“=”字符或“=”字符后没有文本。(请参阅 DNS-SD 规范:有关 DNS TXT 记录格式,请参阅“2.1. DNS-SD TXT 记录大小”(建议的长度)。

Privet 要求设备在 TXT 记录中发送以下键值对。键/值字符串不区分大小写,例如“CS=online”和“cs=ONLINE”是相同的。TXT 记录中的信息必须与通过 /info API 可访问的信息相同(请参阅 4.1.API 部分)。

建议将 TXT 记录大小保持在 512 字节以下。

2.2.1. txtvers

TXT 结构的相应版本。txtvers 必须是 TXT 结构的第一个记录。目前,唯一支持的版本是 1。

txtvers=1

2.2.2. ty

提供设备的用户可读名称。例如:

ty=Google Cloud Ready Printer Model XYZ

2.2.3. 备注(可选)

提供设备的用户可读名称。例如:

note=1st floor lobby printer

注意:这是一个可选的键,可以跳过。不过,如果存在,用户应该能够修改此值。注册设备时必须使用相同的说明。

2.2.4. url

设备所连接的服务器网址(包括协议)。例如:

url=https://www.google.com/cloudprint

2.2.5. 类型

相应设备支持的设备子类型(以英文逗号分隔的列表)。格式为: “type=_subtype1,_subtype2”。目前,唯一受支持的设备子类型是 printer

type=printer

列出的每个子类型都应使用相应的 PTR 记录进行宣传。对于每个受支持的服务子类型,都应有一个对应的项。服务子类型名称 (<subtype>._sub._privet._tcp) 应与此处的设备类型相同。

2.2.6. id

设备 ID。如果设备尚未注册,则应存在此键,但值应为空。例如:

  id=11111111-2222-3333-4444-555555555555
  id=

2.2.7. cs

指示设备的当前连接状态。此规范中定义了四种可能的值。

  • “online”表示设备目前已连接到云端。
  • “离线”表示设备在本地网络上可用,但无法与服务器通信。
  • “正在连接”表示设备正在执行启动序列,但尚未完全联网。
  • “not-configured”表示设备的互联网访问权限尚未配置。此值目前未使用,但在未来版本的规范中可能会有用。
例如:
  • cs=online
  • cs=offline
  • cs=connecting

如果设备已向云注册,则在启动时,它应检查与服务器的连接,以检测其连接状态(例如,调用云 API 以获取设备设置)。设备可能会使用其通知渠道(例如 XMPP)连接状态来报告此值。 未注册的设备在启动时可能会 ping 某个网域,以检测其连接状态(例如,云打印设备会 ping www.google.com)。

3. 通告

在设备启动、关闭或状态更改时,设备必须按照 mDNS 规范中所述执行通告步骤。应至少发送两次相应的公告,两次公告之间至少间隔 1 秒。

3.1. 启动

在设备启动时,必须按照 mDNS 规范中所述执行探测和通告步骤。在这种情况下,应发送 SRV、PTR 和 TXT 记录。建议尽可能将所有记录分组到一个 DNS 响应中。如果不是,建议按以下顺序添加:SRV、PTR、TXT 记录。

3.2. 关停

在设备关机时,它应尝试通过发送 TTL=0 的“告别数据包”(如 mDNS 文档中所述)来通知所有相关方。

3.3. 更新

如果 TXT 中描述的任何信息发生变化,设备必须发送更新公告。在这种情况下,只需发送新的 TXT 记录即可。例如,设备注册后,必须发送包含新设备 ID 的更新公告。

4. API

发现云设备后,客户端即可通过本地网络直接与该设备通信。所有 API 都基于 HTTP 1.1。数据格式基于 JSON。API 请求可以是 GET 请求,也可以是 POST 请求。

每个请求都必须包含有效的“X-Privet-Token”标头。唯一允许具有空“X-Privet-Token”标头的请求是 /privet/info 请求(请注意,标头必须仍然存在)。如果缺少“X-Privet-Token”标头,设备必须返回以下 HTTP 400 错误:

HTTP/1.1 400 Missing X-Privet-Token header.

如果“X-Privet-Token”标头为空或无效,设备必须响应“invalid X-Privet-Token error”(invalid_x_privet_token,详情请参阅“错误”部分)。唯一的例外是 /info API。如需详细了解这样做的原因以及应如何生成令牌,请参阅附录 A:XSSI 和 XSRF 攻击及防范。

如果所请求的 API 不存在或不受支持,设备必须返回 HTTP 404 错误。

4.1. API 可用性

在公开任何 API(包括 /info API)之前,设备必须联系服务器以检查本地设置。本地设置必须在重新启动之间保持不变。如果服务器不可用,则应使用上次已知的本地设置。如果设备尚未注册,则应遵循默认设置。

Cloud Print 设备必须按照以下步骤注册、接收和更新本地设置。

4.1.1. 注册

设备注册时,必须指定“local_settings”参数,如下所示:

{
       "current": {
                "local_discovery": true,
                "access_token_enabled": true,
                "printer/local_printing_enabled": true,
                "printer/conversion_printing_enabled": true,
                "xmpp_timeout_value": 300
        }
}
您可以设置以下设置:
值名称值类型说明
local_discovery布尔值指示是否允许本地发现功能。如果为“false”,则必须停用所有本地 API(包括 /info)和 DNS-SD 发现。默认情况下,新注册的设备应传递“true”。
access_token_enabled布尔值(可选)指示是否应在本地网络上公开 /accesstoken API。默认值应为“true”。
printer/local_printing_enabled布尔值(可选)指示是否应在本地网络上公开本地打印功能(/printer/createjob、/printer/submitdoc、/printer/jobstate)。默认值应为“true”。
printer/conversion_printing_enabled布尔值(可选)指示本地打印是否可以将作业发送到服务器进行转换。仅在启用本地打印时才有意义。
xmpp_timeout_valueint(可选)表示 XMPP 渠道 ping 之间的时间间隔(以秒为单位)。默认情况下必须为 300(5 分钟)或更长时间。

重要提示:如果缺少任何可选值,则表示设备完全不支持相应的功能。

4.1.2. 启动

在设备启动时,它应与服务器联系,以检查哪些 API 可在本地网络中公开。对于连接到云打印的打印机,它们应调用:

/cloudprint/printer?printerid=<printer_id>
/cloudprint/list

/cloudprint/printer 优先于 /cloudprint/list,但两者均可正常运行。

此 API 会返回当前设备参数,包括本地 API 的设置。服务器的回复将采用以下格式:

"local_settings": {
        "current": {
                "local_discovery": true,
                "access_token_enabled": true,
                "printer/local_printing_enabled": true,
                "printer/conversion_printing_enabled": true,
                "xmpp_timeout_value": 300
         },
         "pending": {
                "local_discovery": true,
                "access_token_enabled": true,
                "printer/local_printing_enabled": false,
                "printer/conversion_printing_enabled": false,
                "xmpp_timeout_value": 500
         }
}

“current”对象表示当前生效的设置。

“pending”对象表示应应用于设备的设置(此对象可能缺失)。

设备看到“待处理”设置后,必须更新其状态(见下文)。

4.1.3. 更新

如果需要更新设置,系统会向设备发送 XMPP 通知。通知将采用以下格式:

<device_id>/update_settings

收到此类通知后,设备必须查询服务器以获取最新设置。 云打印设备必须使用:

/cloudprint/printer?printerid=<printer_id>

一旦设备因 /cloudprint/printer API(在启动时或由于通知)而看到“待处理”部分,就必须更新其内部状态以记住新设置。它必须调用服务器 API 来确认新设置。对于云打印机,设备必须调用 /cloudprint/update API 并使用“local_settings”参数(与注册期间一样)。

重新连接到 XMPP 渠道时,设备必须调用 /cloudprint/printer API 来检查自上次以来本地设置是否已更改。

4.1.3.1. 本地设置待处理

设备用于调用服务器 API 的“local_settings”参数绝不能包含“pending”部分。

4.1.3.2. 本地设置当前

只有设备可以更改“local_settings”的“current”部分。 其他所有人都会更改“待处理”部分,并等待设备将更改传播到“当前”部分。

4.1.4. 离线

如果在启动期间无法联系服务器,设备在收到通知后必须使用最近一次的已知本地设置。

4.1.5. 从服务中删除设备

如果设备已从服务(例如 GCP)中删除,系统会向设备发送 XMPP 通知。通知将采用以下格式:

<device_id>/delete

收到此类通知后,设备必须前往服务器检查其状态。云打印设备必须使用:

/cloudprint/printer?printerid=<printer_id>

设备必须收到成功的 HTTP 回答,其中 success=false 且没有设备/打印机说明。这意味着设备已从服务器中移除,并且设备必须擦除其凭据并进入默认出厂设置模式。

每当设备收到指示其已因 /cloudprint/printer API(启动、更新设置通知、每日 ping)而被删除的回复时,都必须删除其凭据并进入默认模式。

4.2. /privet/info API

信息 API 是强制性的,必须由每个设备实现。这是一个针对“/privet/info”网址的 HTTP GET 请求:GET /privet/info HTTP/1.1

信息 API 会返回有关设备及其支持的功能的基本信息。此 API 绝不能更改设备状态或执行任何操作,因为它容易受到 XSRF 攻击。这是唯一允许具有空“X-Privet-Token”标头的 API。客户端应调用 /privet/info API,并将“X-Privet-Token”标头设置为 X-Privet-Token:“”

信息 API 必须返回与发现期间 TXT 记录中提供的数据一致的数据。

4.2.1. 输入

/privet/info API 没有输入参数。

4.2.2. 返回

/privet/info API 会返回有关设备和受支持功能的基本信息。

TXT 列表示 DNS-SD TXT 记录中的相应字段。

值名称值类型说明TXT
版本字符串支持的最高 API 版本(主版本.次版本),目前为 1.0
name字符串设备的直观易懂的名称。ty
说明字符串(可选)设备说明。应可由用户修改。note
网址字符串相应设备正在与之通信的服务器的网址。网址必须包含协议规范,例如:https://www.google.com/cloudprint。网址
类型字符串列表支持的设备类型列表。类型
id字符串设备 ID,如果设备尚未注册,则为空。 id
device_state字符串设备的状态。
空闲表示设备已准备就绪
正在处理表示设备正忙,功能可能会暂时受限
已停止表示设备无法正常运行,需要用户干预
connection_state字符串与服务器 (base_url) 的连接状态
online - 连接可用
offline - 无连接
connecting - 正在执行启动步骤
not-configured - 尚未配置连接
注册设备可能会根据通知渠道(例如 XMPP 连接状态)的状态报告其连接状态。
cs
制造商字符串设备制造商的名称
模型字符串设备的型号
serial_number字符串唯一设备标识符。在此规范中,此字段必须是 UUID。(GCP 1.1 规范)
(可选)我们强烈建议您在所有位置使用相同的序列号 ID,以便不同的客户端可以识别同一设备。例如,实现 IPP 的打印机可能会在“printer-device-id”字段中使用此序列号 ID。
固件字符串设备固件版本
运行时间int自设备启动以来的秒数。
setup_url字符串(可选)包含设置说明的网页的网址(包括协议)
support_url字符串(可选)包含支持信息、常见问题解答信息的网页的网址(包括协议)
update_url字符串(可选)包含更新固件说明的网页的网址(包括协议)
x-privet-token字符串必须传递给所有 API 的 X-Privet-Token 标头的值,以防止 XSSI 和 XSRF 攻击。详情请参阅 6.1。
APIAPI 说明支持的 API 列表(如下所述)
semantic_stateJSON(可选)采用 CloudDeviceState 格式的设备的语义状态。

api - 是一个 JSON 列表,其中包含可通过本地网络使用的 API 列表。请注意,并非所有 API 都可能同时通过本地网络提供。例如,新连接的设备应仅支持 /register API:

"api": [
        "/privet/register",
]
设备注册完成后,设备应停止支持 /register API。设备还应与服务进行核对,以确定可以通过本地网络公开哪些 API。例如:
"api": [
        "/privet/accesstoken",
        "/privet/capabilities",
        "/privet/printer/submitdoc",
]

目前可用的 API 如下:

  • /privet/register - 用于通过本地网络注册设备的 API。(有关详情,请参阅 /privet/register API)。一旦设备成功在云端注册,就必须隐藏此 API。
  • /privet/accesstoken - 用于从设备请求访问令牌的 API(有关详情,请参阅 /privet/accesstoken API)。
  • /privet/capabilities - 用于检索设备功能的 API(有关详情,请参阅 /privet/capabilities API)。
  • /privet/printer/* - 特定于设备类型“打印机”的 API,如需了解详情,请参阅特定于打印机的 API。
以下是 /privet/info 响应的示例。(请注意,由于这是已注册的设备,因此缺少 /privet/register API):
{
        "version": "1.0",
        "name": "Gene’s printer",
        "description": "Printer connected through Chrome connector",
        "url": "https://www.google.com/cloudprint",
        "type": [
                "printer"
        ],
        "id": "11111111-2222-3333-4444-555555555555",
        "device_state": "idle",
        "connection_state": "online",
        "manufacturer": "Google",
        "model": "Google Chrome",
        "serial_number": "1111-22222-33333-4444",
        "firmware": "24.0.1312.52",
        "uptime": 600,
        "setup_url": "http://support.google.com/cloudprint/answer/1686197/?hl=en",
        "support_url": "http://support.google.com/cloudprint/?hl=en",
        "update_url": "http://support.google.com/cloudprint/?hl=en",
        "x-privet-token": "AIp06DjQd80yMoGYuGmT_VDAApuBZbInsQ:1358377509659",
        "api": [
                "/privet/accesstoken",
                "/privet/capabilities",
                "/privet/printer/submitdoc",
        ]
}
以下是墨水用尽的打印机的 /privet/info 响应示例(请注意 semantic_state 字段):
{
        "version": "1.0",
        "name": "Gene’s printer",
        "description": "Printer connected through Chrome connector",
        "url": "https://www.google.com/cloudprint",
        "type": [
                "printer"
        ],
        "id": "11111111-2222-3333-4444-555555555555",
        "device_state": "stopped",
        "connection_state": "online",
        "manufacturer": "Google",
        "model": "Google Chrome",
        "serial_number": "1111-22222-33333-4444",
        "firmware": "24.0.1312.52",
        "uptime": 600,
        "setup_url": "http://support.google.com/cloudprint/answer/1686197/?hl=en",
        "support_url": "http://support.google.com/cloudprint/?hl=en",
        "update_url": "http://support.google.com/cloudprint/?hl=en",
        "x-privet-token": "AIp06DjQd80yMoGYuGmT_VDAApuBZbInsQ:1358377509659",
        "api": [
                "/privet/accesstoken",
                "/privet/capabilities",
                "/privet/printer/submitdoc",
        ],
        "semantic_state": {
                "version": "1.0",
                "printer": {
                        "state": "STOPPED",
                        "marker_state": {
                                "item": [
                                        {
                                                "vendor_id": "ink",
                                                "state": "EXHAUSTED",
                                                "level_percent": 0
                                        }
                                ]
                        }
                }
        }
}

4.2.3. 错误

仅当缺少 X-Privet-Token 标头时,/privet/info API 才应返回错误。必须是 HTTP 400 错误:

HTTP/1.1 400 Missing X-Privet-Token header.

4.3. /privet/register API

/privet/register API 是可选的。这是一个 HTTP POST 请求。/privet/register API 必须检查是否存在有效的 X-Privet-Token 标头。设备必须在“/privet/register”网址上实现此 API:

POST /privet/register?action=start&user=user@domain.com HTTP/1.1
POST /privet/register?action=complete&user=user@domain.com HTTP/1.1

设备应仅在允许匿名注册时公开 /privet/register API。例如:

  • 当设备开启(或在点击设备上的特殊按钮后)且尚未注册时,应公开 /privet/register API,以允许本地网络中的用户声明打印机。
  • 注册完成后,设备应停止公开 /privet/register API,以防止本地网络上的其他用户重新声明设备。
  • 某些设备可能具有不同的设备注册方式,并且根本不应公开 /privet/register API(例如 Chrome 云打印连接器)。

注册流程包含 3 个步骤(请参阅 Cloud Print 的匿名注册)。

  1. 启动匿名注册流程。
  2. 客户端通过调用 /privet/register API 来启动此流程。设备可能会在此时等待用户确认。
  3. 获取声明令牌。

客户端轮询以确定设备何时准备好继续。设备准备就绪后,会向服务器发送请求,以检索注册令牌和注册网址。收到的令牌和网址应返回给客户端。在此步骤中,如果设备收到另一个用于初始化注册的调用,则应执行以下操作:

  • 如果这是开始注册的同一用户,则舍弃所有之前的数据(如果有),然后开始新的注册流程。
  • 如果这是不同的用户,则返回 device_busy 错误和 30 秒超时。

完成注册流程。

客户端声明设备后,应通知设备完成注册。注册流程完成后,设备应发送更新公告,其中包含新获取的设备 ID。

注意:当设备正在处理 /privet/register API 调用时,不得同时处理其他 /privet/register API 调用。设备必须返回 device_busy 错误和 30 秒超时。

强烈建议用户在设备上确认注册。如果已实现,设备必须在收到 /privet/register?action=start API 调用后等待用户确认。客户端将调用 /privet/register?action=getClaimToken API,以了解用户确认何时完成以及声明令牌何时可用。如果用户在设备上取消注册(例如,按“取消”按钮),则必须返回 user_cancel 错误。如果用户未在特定时间范围内确认注册,则必须返回 confirmation_timeout 错误。如需了解详情,请参阅“默认值”部分。

4.3.1. 输入

/privet/register API 具有以下输入参数:
名称
action可以是以下值之一:
start - 开始注册流程
getClaimToken - 检索设备的声明令牌
cancel - 取消注册流程
complete - 完成注册流程
用户将声明此设备的所有权的用户的电子邮件地址。

设备必须检查所有操作(开始、getClaimToken、取消、完成)中的电子邮件地址是否一致。

4.3.2. 返回

/privet/register API 会返回以下数据:
值名称值类型说明
action字符串与输入参数中的操作相同。
用户字符串(可选)与输入参数中的用户相同(如果输入中省略了用户,则可能缺失)。
token字符串(可选)注册令牌(对于“getClaimToken”响应是必需的,对于“start”“complete”“cancel”则省略)。
claim_url字符串(可选)注册网址(对于“getClaimToken”响应是必需的,对于“start”“complete”“cancel”则省略)。对于云打印机,它必须是服务器发送的“complete_invite_url”。
automated_claim_url字符串(可选)注册网址(对于“getClaimToken”响应是必需的,对于“start”“complete”“cancel”则省略)。对于云打印机,它必须是从服务器收到的“automated_invite_url”。
device_id字符串(可选)新设备 ID(对于“start”响应,此字段可省略;对于“complete”响应,此字段为必需字段)。

设备必须仅在注册完成后,才会在 /privet/info API 响应中返回其设备 ID。

示例 1:

{
        "action": "start",
        "user": "user@domain.com",
}

示例 2:

{
        "action": "getClaimToken",
        "user": "user@domain.com",
        "token": "AAA111222333444555666777",
        "claim_url": "https://domain.com/SoMeUrL",
}

示例 3:

{
        "action": "complete",
        "user": "user@domain.com",
        "device_id": "11111111-2222-3333-4444-555555555555",
}

4.3.3. 错误

/privet/register API 可能会返回以下错误(详情请参阅“错误”部分):
错误说明
device_busy设备正忙,无法执行所请求的操作。超时后重试。
pending_user_action在响应“getClaimToken”时,此错误表示设备仍在等待用户确认,应在超时后重试“getClaimToken”请求。
user_cancel用户明确从设备取消了注册流程。
confirmation_timeout用户确认超时。
invalid_action调用了无效操作。例如,如果客户端在调用 action=start 和 action=getClaimToken 之前调用了 action=complete。
invalid_params请求中指定的参数无效。(未知参数应被安全地忽略,以实现未来兼容性)。例如,如果客户端调用了 action=unknown 或 user=,则返回此值。
device_config_error设备端上的日期/时间(或其他某些设置)有误。用户需要前往(设备内部网站)并配置设备设置。
离线设备目前处于离线状态,无法与服务器通信。
server_error注册过程中出现服务器错误。
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

成功完成注册后,设备必须停止公开 /privet/register API。如果设备未公开 /privet/register API,则必须返回 HTTP 404 错误。 因此,如果设备已注册,则调用此 API 必须返回 404。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

4.4. /privet/accesstoken API

/privet/accesstoken API 是可选的。这是一个 HTTP GET 请求。/privet/accesstoken API 必须检查是否存在有效的“X-Privet-Token”标头。设备必须在“/privet/accesstoken”网址上实现此 API:
GET /privet/accesstoken HTTP/1.1

当设备收到 /accesstoken API 调用时,应调用服务器以检索指定用户的访问令牌,并将该令牌返回给客户端。然后,客户端将使用该访问令牌通过云访问此设备。

Cloud Print 设备必须调用以下 API:

/cloudprint/proximitytoken
并从本地 API 传递“printerid=<printer_id>”和“user”参数。如果成功,服务器响应将包含以下对象:
"proximity_token": {
        "user": "user@domain.com",
        "token": "AAA111222333444555666777",
        "expires_in": 600
}
Cloud Print 设备必须将响应中“proximity_token”对象的值传递给本地 /privet/accesstoken API 调用。如果设备可以传递所有参数(包括本规范中未描述的参数),则更具优势(可应对未来变化)。

4.4.1. 输入

/privet/accesstoken API 具有以下输入参数:
名称
用户打算使用此访问令牌的用户的电子邮件地址。在请求中可能为空。

4.4.2. 返回

/privet/accesstoken API 返回以下数据:
值名称值类型说明
token字符串服务器返回的访问令牌
用户字符串与输入参数中的用户相同。
expires_inint相应令牌到期前的秒数。从服务器接收并在此响应中传递。

示例:

{
        "token": "AAA111222333444555666777",
        "user": "user@domain.com",
        "expires_in": 600
}

4.4.3. 错误

/privet/accesstoken API 可能会返回以下错误(详情请参阅“错误”部分):
错误说明
离线设备目前处于离线状态,无法与服务器通信。
access_denied权限不足。访问遭拒。当请求被服务器明确拒绝时,设备应返回此错误。
invalid_params请求中指定的参数无效。(未知参数应被安全地忽略,以实现未来兼容性)。例如,如果客户端调用了 /accesstoken?user= 或 /accesstoken。
server_error服务器错误。
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

如果设备未公开 /privet/accesstoken API,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

4.5. /privet/capabilities API

/privet/capabilities API 是可选的。这是一个 HTTP GET 请求。/privet/capabilities API 必须检查是否存在有效的“X-Privet-Token”标头。设备必须在“/privet/capabilities”网址上实现此 API:
GET /privet/capabilities HTTP/1.1
当设备收到 /capabilities API 调用时,如果设备能够联系服务器,则应联系服务器以获取更新的功能。例如,如果打印机支持通过云打印服务将打印作业(本地接收)发布到自身,则应返回云打印服务会返回的功能。在这种情况下,Cloud Print 可能会通过添加新功能来改变原始打印机的功能,这些新功能可能会在将作业发送到打印机之前执行。最常见的情况是支持的证件类型列表。如果打印机处于离线状态,则应返回其支持的文档类型。不过,如果打印机处于在线状态并已注册到云打印,则必须返回“*/*”作为支持的类型之一。在这种情况下,云打印服务会执行必要的转换。对于离线打印,打印机必须至少支持“image/pwg-raster”格式。

4.5.1. 输入

/privet/capabilities API 具有以下输入参数:
名称
离线(可选)只能是“offline=1”。在这种情况下,设备应返回离线使用时的功能(如果这些功能与“在线”功能不同)。

4.5.2. 返回

/privet/capabilities API 以云设备说明 (CDD) JSON 格式返回设备功能(有关详情,请参阅 CDD 文档)。打印机必须至少在此处返回受支持类型的列表。例如,当前处于在线状态的云端就绪打印机可能会返回如下内容(至少):
{
        "version": "1.0",
        "printer": {
                "supported_content_type": [
                        {
                                "content_type": "application/pdf",
                                "min_version": "1.4"
                        },
                        { "content_type": "image/pwg-raster" },
                        { "content_type": "image/jpeg" },
                        { "content_type": "*/*" }
                ]
        }
}
当它与服务器断开连接时,可能会返回:
{
        "version": "1.0",
        "printer": {
                "supported_content_type": [
                        {
                                "content_type": "application/pdf",
                                "min_version": "1.4"
                        },
                        { "content_type": "image/pwg-raster" },
                        { "content_type": "image/jpeg" }
                ]
        }
}

注意:打印机使用顺序来表示支持的内容类型优先级。例如,在上述示例中,打印机指定它更偏好“application/pdf”数据,而不是“image/pwg-raster”和“image/jpeg”。客户端应尽可能遵循打印机优先级(详情请参阅 CDD 文档)。

4.5.3. 错误

/privet/capabilities API 可能会返回以下错误(详情请参阅“错误”部分):
错误说明
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

如果设备未公开 /privet/capabilities API,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

4.6. 错误

上述 API 返回的错误采用以下格式:
值名称值类型说明
错误字符串错误类型(按 API 定义)
说明字符串(可选)直观易懂的错误说明。
server_api字符串(可选)如果出现服务器错误,此字段会包含失败的服务器 API。
server_codeint(可选)如果出现服务器错误,此字段会包含服务器返回的相应错误代码。
server_http_codeint(可选)如果出现服务器 HTTP 错误,此字段会包含服务器返回的 HTTP 错误代码。
超时int(可选)客户端在重试之前等待的秒数(仅针对可恢复的错误)。客户端必须将实际超时时间从此值随机化为比此值大 20% 的值。

如果缺少 X-Privet-Token 标头,所有 API 都必须返回 HTTP 400 错误。

HTTP/1.1 400 Missing X-Privet-Token header.

示例 1:

{
        "error": "server_error",
        "description": "Service unavailable",
        "server_api": "/submit",
        "server_http_code": 503
}

示例 2:

{
        "error": "printer_busy",
        "description": "Printer is currently printing other job",
        "timeout": 15
}

5. Printer API

此协议支持的设备类型之一是打印机。支持此类型的设备可以实现一些特定于打印机的功能。理想情况下,向云端打印机发送的打印作业将通过云打印服务器:

在某些情况下,客户可能需要在本地发送文件。当客户端没有 Google ID 或无法与云打印服务器通信时,可能需要此功能。在这种情况下,打印任务将本地提交到打印机。打印机反过来会使用云打印服务进行作业排队和转换。打印机将重新向云打印服务发布本地提交的作业,然后请求该作业,因为该作业是通过云提交的。此流程将提供灵活的服务(转换)和打印作业管理/跟踪用户体验。

由于 Cloud Print 服务实现了转换,因此打印机应在支持的内容类型列表中宣传支持所有输入格式(“*/*”):

{
        "version": "1.0",
        "printer": {
                "supported_content_type": [
                        { "content_type": "image/pwg-raster" },
                        { "content_type": "*/*" }
                ]
        }
}

在某些情况下,用户可能需要完全离线的解决方案。由于打印机支持的输入格式数量有限,因此客户端需要将文档转换为打印机原生支持的几种格式。

此规范要求所有打印机都必须至少支持 PWG Raster ("image/pwg-raster") 格式,以用于离线打印。打印机可能支持其他格式(例如 JPEG),如果客户端支持,则可以发送该格式的文档。打印机必须通过 /capabilities API 公开支持的类型,例如:

{
        "version": "1.0",
        "printer": {
                "supported_content_type": [
                        { "content_type": "image/pwg-raster" },
                        { "content_type": "image/jpeg" }
                ]
        }
}
客户端可以通过两种方式在本地网络上发起打印。

简单打印 - 客户端通过本地网络将文档发送到 /submitdoc API(不指定 job_id 参数)。提交的文档将使用默认打印凭据设置进行打印,无需任何打印作业状态。如果打印机仅支持此类打印,则必须在 /privet /info API 响应中仅宣传/submitdoc API。

"api": [
        "/privet/accesstoken",
        "/privet/capabilities",
        "/privet/printer/submitdoc",
]

高级打印 - 客户端应先通过在请求中调用 /privet/printer/createjob API 并提供有效的 CJT 作业票证,在打印机上创建打印作业。打印机必须将打印票证存储在内存中,并将 job_id 返回给客户端。然后,客户端将调用 /printer/submitdoc API 并指定之前收到的 job_id。届时,打印机将开始打印。客户端将通过调用 /privet/printer/jobstate API 轮询打印机以获取打印作业状态。

在多客户环境中,无法保证此 API 的调用方式。一个客户端可以在另一个客户端的/createjob->/submitdoc 调用之间调用 /createjob。为消除可能出现的死锁并提高易用性,我们建议在打印机上设置一个包含少量待处理打印作业的队列(至少 3-5 个):

  • /createjob 会占用队列中的第一个可用位置。
  • 作业生命周期(在队列中)至少为 5 分钟。
  • 如果队列中的所有位置都已占用,则系统会移除最旧的非打印作业,并将新作业放置在该位置。
  • 如果设备上当前有正在打印的打印作业(简单打印或高级打印),/submitdoc 应返回繁忙状态,并建议一个超时时间来重试此打印作业。
  • 如果 /submitdoc 指的是已从队列中移除的作业(由于替换或超时),打印机应返回错误 invalid_print_job,并且客户端将从 /createjob 步骤重试该流程。客户端必须等待最长 5 秒的随机超时时间,然后才能重试。

如果内存限制导致无法在设备上存储多个待处理的作业,则打印作业队列的长度可能为 1。它仍应遵循与上述相同的协议。作业完成或因错误而失败后,打印机应至少存储 5 分钟的作业状态信息。用于存储已完成作业状态的队列大小应至少为 10。如果需要存储更多作业状态,则最早的作业状态可能会在 5 分钟超时之前从队列中移除。

注意:目前,客户端将轮询作业状态。未来,我们可能会要求打印机在任何打印作业状态发生变化时发送 TXT DNS 通知。

5.1. /privet/printer/createjob API

/privet/printer/createjob API 是可选的(请参阅上文中的“简单打印”)。这是一个 HTTP POST 请求。/privet/printer/createjob API 必须检查是否存在有效的“X-Privet-Token”标头。 设备必须在“/privet/printer/createjob”网址上实现此 API:

POST /privet/printer/createjob HTTP/1.1
当接收到 /privet/printer/createjob API 调用时,打印机必须创建新的打印作业 ID,以 CJT 格式存储接收到的打印票证,并将打印作业 ID 返回给客户端。

5.1.1. 输入

/privet/printer/createjob API 在网址中没有输入参数。请求正文应包含 CJT 格式的打印作业票证数据。

5.1.2. 返回

/privet/printer/createjob API 返回以下数据:
值名称值类型说明
job_id字符串新创建的打印作业的 ID。
expires_inint相应打印作业的有效秒数。

示例:

{
        "job_id": "123",
        "expires_in": 600
}

5.1.3. 错误

/privet/printer/createjob API 可能会返回以下错误(详情请参阅“错误”部分):
错误说明
invalid_ticket提交的打印凭据无效。
printer_busy打印机正忙,目前无法处理 /创建作业。超时后重试。
printer_error打印机处于错误状态,需要用户互动才能修复。 说明应包含更详细的解释(例如“纸盒 1 中出现卡纸”)。
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

如果设备未公开 /privet/printer/createjob,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

5.2. /privet/printer/submitdoc API

/privet/printer/submitdoc API 必须实现通过本地网络(离线或重新发布到 Cloud Print)进行打印。这是一个 HTTP POST 请求。/privet/printer/submitdoc API 必须检查是否存在有效的“X-Privet-Token”标头。设备必须在“/privet/printer/submitdoc”网址上实现此 API:
POST /privet/printer/submitdoc HTTP/1.1
当收到 /privet/printer/submitdoc API 调用时,打印机应开始打印。如果无法开始打印,则必须返回错误 printer_busy 和建议的超时时间段,以便客户端等待一段时间后再重试。

如果打印机无法将所有数据都保存在其内部缓冲区中,则应使用 TCP 机制来减慢数据传输速度,直到打印机打印完部分文档,从而使部分缓冲区再次可用。(例如,打印机可能会在 TCP 层上设置 windowsize=0,这会导致客户端等待。)

将文档提交到打印机可能需要花费大量时间。客户端应能够在打印进行时检查打印机和作业的状态(高级打印)。 为此,打印机必须允许客户端在处理 /privet/printer/submitdoc API 调用时调用/privet/info 和 /privet/printer/jobstate API。建议所有客户端都启动一个新线程来执行 /privet/printer/submitdoc API 调用,以便主线程可以使用 /privet/info 和 /privet/printer/jobstate API 来检查打印机和作业状态。

注意:本地打印作业完成或中止后,强烈建议(并且在未来版本的此规范中将要求)向 /cloudprint/submit 接口报告作业的最终状态,以用于结算和改善用户体验。需要提供“printerid”“title”“contentType”和“final_semantic_state”(采用 PrintJobState 格式)参数,以及“tag”(重复参数)和“ticket”(采用 CloudJobTicket 格式的作业凭据)参数。 请注意,所提供的 PrintJobState 必须是最终状态,即其类型必须为 DONE 或 ABORTED,并且在 ABORTED 的情况下必须提供原因(有关详情,请参阅 JobState)。另请注意,其规范中并未提及使用 /cloudprint/submit 接口报告本地打印作业,因为该部分旨在描述接口的主要用途:提交打印作业,并使用“content”参数提供要打印的文档。

5.2.1. 输入

/privet/printer/submitdoc API 具有以下输入参数:
名称
job_id(可选)打印作业 ID。对于简单的打印情况(见上文),可以省略。必须与打印机返回的字符串一致。
user_name(可选)直观易懂的用户名。此值并非最终值,仅用于打印作业注释。如果作业重新发布到云打印服务,则此字符串应附加到云打印作业。
client_name(可选)发出此请求的客户端应用的名称。仅用于显示目的。如果作业重新发布到 Cloud Print 服务,则应将此字符串附加到 Cloud Print 作业。
job_name(可选)要记录的打印作业的名称。如果作业重新发布到云打印服务,则此字符串应附加到云打印作业。
离线(可选)只能是“offline=1”。在这种情况下,打印机应仅尝试离线打印(不重新向云打印服务器发布)。

请求正文应包含有效的待打印文档。“Content-Length”应包含正确的请求长度。“Content-Type”标头应设置为文档 MIME 类型,并与 CDD 中的一种类型相匹配(除非 CDD 指定了“*/*”)。

强烈建议客户在此请求中提供有效的用户名(或电子邮件地址)、客户名称和作业名称。这些字段仅在界面中用于改善用户体验。

5.2.2. 返回

/privet/printer/submitdoc API 会返回以下数据:
值名称值类型说明
job_id字符串新创建的打印作业(简单打印)或请求中指定的 job_id(高级打印)的 ID。
expires_inint相应打印作业的有效秒数。
job_type字符串所提交文档的内容类型。
job_sizeint 64 位打印数据的大小(以字节为单位)。
job_name字符串(可选)与输入中的作业名称相同(如有)。

示例:

{
        "job_id": "123",
        "expires_in": 500,
        "job_type": "application/pdf",
        "job_size": 123456,
        "job_name": "My PDF document"
}

5.2.3. 错误

/privet/printer/submitdoc API 可能会返回以下错误(有关详情,请参阅“错误”部分):
错误说明
invalid_print_job请求中指定的作业 ID 无效/已过期。在超时后重试。
invalid_document_type打印机不支持文档 MIME 类型。
invalid_document提交的证件无效。
document_too_large文档超过了允许的大小上限。
printer_busy打印机正忙,目前无法处理文档。超时后重试。
printer_error打印机处于错误状态,需要用户互动才能修复。 说明应包含更详细的解释(例如“纸盒 1 中出现卡纸”)。
invalid_params请求中指定的参数无效。(未知参数应安全地忽略,以实现未来兼容性)
user_cancel用户明确从设备中取消了打印流程。
server_error将文档发布到云打印失败。
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

如果设备未公开 /privet/printer/submitdoc,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

注意:/privet/printer/submitdoc API 可能需要在打印机端进行特殊处理(因为附加的载荷较大)。在某些情况下(取决于打印机 HTTP 服务器实现和平台),打印机可能会在返回 HTTP 错误之前关闭套接字。在其他情况下,打印机可能会返回 503 错误(而不是 Privet 错误)。打印机应尽可能尝试返回 Privet。不过,实现 Privet 规范的每个客户端都应该能够处理 /privet/printer/submitdoc API 的套接字关闭(无 HTTP 错误)和 503 HTTP 错误情况。在这种情况下,客户端应将其作为 Privet“printer_busy”错误进行处理,并将“timeout”设置为 15 秒。为避免无限重试,客户端可以在尝试合理次数(例如 3 次)后停止重试。

5.3. /privet/printer/jobstate API

/privet/printer/jobstate API 是可选的(请参阅上文中的“简单打印”)。这是一个 HTTP GET 请求。 /privet/printer/jobstate API 必须检查是否存在有效的“X-Privet-Token”标头。设备必须在“/privet/printer/jobstate”网址上实现此 API:
GET /privet/printer/jobstate HTTP/1.1
当收到 /privet/printer/jobstate API 调用时,打印机应返回所请求的打印作业的状态或 invalid_print_job 错误。

5.3.1. 输入

/privet/printer/jobstate API 具有以下输入参数:
名称
job_id要返回状态的打印作业 ID。

5.3.2. 返回

/privet/printer/jobstate API 会返回以下数据:
值名称值类型说明
job_id字符串相应状态信息所对应的打印作业 ID。
字符串草稿 - 打印作业已在设备上创建(尚未收到任何 /privet/printer/submitdoc 调用)。
已加入队列 - 打印作业已收到并加入队列,但尚未开始打印。
in_progress - 打印作业正在打印。
已停止 - 打印作业已暂停,但可以手动或自动重新开始。
完成 - 打印作业已完成。
已中止 - 打印作业失败。
说明字符串(可选)直观易懂的打印作业状态说明。如果 state< 为 stoppedaborted,则应包含其他信息。semantic_state 字段通常会向客户端提供更好、更有意义的说明。
expires_inint相应打印作业的有效秒数。
job_type字符串(可选)所提交文档的内容类型。
job_sizeint 64 位(可选)打印数据的大小(以字节为单位)。
job_name字符串(可选)与输入中的作业名称相同(如有)。
server_job_id字符串(可选)从服务器返回的作业的 ID(如果作业已发布到 Cloud Print 服务)。离线打印时省略。
semantic_stateJSON(可选)作业的语义状态,采用 PrintJobState 格式。

示例(通过 Cloud Print 报告进行打印):

{
        "job_id": "123",
        "state": "in_progress",
        "expires_in": 100,
        "job_type": "application/pdf",
        "job_size": 123456,
        "job_name": "My PDF document",
        "server_job_id": "1111-2222-3333-4444"
}

示例(离线打印错误):

{
        "job_id": "123",
        "state": "stopped",
        "description": "Out of paper",
        "expires_in": 100,
        "job_type": "application/pdf",
        "job_size": 123456,
        "job_name": "My PDF document"
}

示例(用户中止了打印作业):

{
        "job_id": "123",
        "state": "aborted",
        "description": "User action",
        "expires_in": 100,
        "job_type": "application/pdf",
        "job_size": 123456,
        "job_name": "My PDF document",
        "semantic_state": {
                "version": "1.0",
                "state": {
                        "type": "ABORTED",
                        "user_action_cause": {"action_code": "CANCELLED"}
                },
                "pages_printed": 7
        }
}

示例(打印作业因缺纸而停止)。请注意对设备状态的引用。客户端需要调用 /privet/info API 才能获取有关设备状态的更多详细信息:

{
        "job_id": "123",
        "state": "stopped",
        "description": "Out of paper",
        "expires_in": 100,
        "job_type": "application/pdf",
        "job_size": "123456",
        "job_name": "My PDF document",
        "semantic_state": {
                "version": "1.0",
                "state": {
                        "type": "STOPPED",
                        "device_state_cause": {"error_code": "INPUT_TRAY"}
                },
                "pages_printed": 7
        }
}

5.3.3. 错误

/privet/printer/jobstate API 可能会返回以下错误(详情请参阅“错误”部分):
错误说明
invalid_print_job请求中指定的作业 ID 无效/已过期。
server_error获取打印作业状态(针对发布到 Cloud Print 的打印作业)失败。
invalid_x_privet_token请求中的 X-Privet-Token 无效或为空。

如果设备未公开 /privet/printer/jobstate,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。

6. 附录

6.1. 默认行为和设置

本部分将说明我们希望所有 Privet 兼容设备都具备的默认行为。
  • 开箱即用的设备应仅支持 /privet/info/privet/register API。应停用所有其他 API(例如 /privet/accesstoken、本地打印)。
  • 注册需要与设备进行物理交互。
    • 用户必须在设备上执行物理操作(例如按某个按钮)来确认其对设备的访问权限。
    • 用户执行上述操作后,打印机应发送 /cloudprint/register 请求。在采取相应操作之前,不应发送此请求(请参阅序列图 1)。
    • 如果设备正在处理 /privet/register 请求(例如,等待上述操作),则必须拒绝所有其他 /privet/register 请求。在这种情况下,设备必须返回 device_busy 错误。
    • 如果设备在 60 秒内未收到上述物理操作,则应使任何 /register 请求超时。在这种情况下,设备必须返回 confirmation_timeout 错误。
    • 可选:建议执行,但并非强制要求,以下操作可改善用户体验:
      • 打印机可能会闪烁指示灯或屏幕,以指示用户需要采取行动来确认注册。
      • 打印机可能会在其屏幕上显示“正在为用户‘abc@def.com’向 Google 云打印注册 - 按 OK 继续”,其中 abc@def.com 是 /register API 调用中的用户参数。这样一来,用户就能更清楚地了解:
        • 他们确认的是自己的注册请求
        • 如果对方未触发该请求,会发生什么情况。
      • 除了在打印机上执行确认操作(例如,“按 OK 按钮”),打印机还可以为用户提供一个用于取消请求的按钮(例如,(按“取消”拒绝)。这样一来,未触发注册请求的用户就可以在 60 秒超时之前取消该请求。在这种情况下,设备必须返回 user_cancel 错误。
  • 所有权转移:
    • 设备可能已从云服务中明确删除。
      • 如果设备收到成功响应,但 /cloudprint/printer(针对 GCP)调用未返回任何设备说明,则设备必须恢复为默认(开箱即用)模式。
      • 如果设备的凭据不再有效(明确是因为服务器返回“凭据无效”响应),则必须恢复为默认(开箱即用)模式。
    • 本地恢复出厂设置必须清除设备的凭据并将其设置为默认状态。
    • 可选:设备可能会提供一个菜单项,用于清除凭据并将其置于默认模式。
  • 支持 XMPP 通知的设备必须能够 ping 服务器。必须能够通过“local_settings”从服务器控制 ping 超时。
  • 设备每天(24 小时)最多只能显式 ping 服务器(GCP 的 /cloudprint/printer API,以及 XMPP ping)一次,以确保它们处于同步状态。建议在 24-32 小时的时间范围内随机化检查窗口。
  • 可选:对于云打印设备,建议(但并非必需)提供一种手动方式(按钮),以便用户从设备发起对新打印作业的检查。部分打印机已具备此功能。
  • 可选。企业打印机可能提供完全停用本地发现的选项。在这种情况下,设备必须更新服务器上的这些本地设置。新的本地设置必须为空(将“local_discovery”设置为“false”意味着可以从 GCP 服务重新启用)。

6.1.2 默认注册图

6.2. XSSI 和 XSRF 攻击及防范

本部分将说明设备上可能发生的 XSSI 和 XSRF 攻击,以及如何防范这些攻击(包括令牌生成技术)。
如需了解详情,请参阅: http://googleonlinesecurity.blogspot.com/2011/05/website-security-for-webmasters.html
通常,当网站使用 Cookie 身份验证机制时,可能会遭受 XSSI 和 XSRF 攻击。虽然 Google 的云打印服务不使用 Cookie,但此类攻击仍有可能发生。本地网络访问权限在设计上会隐式信任请求。

6.2.1. XSSI

恶意网站可能会猜测 Privet 兼容设备的 IP 地址和端口号,并尝试使用 <script> 标记内的“src=<api name>”调用 Privet API:
<script type="text/javascript" src="http://192.168.1.42:8080/privet/info"></script>
如果没有保护,恶意网站将能够执行 API 调用并访问结果。
为防止此类攻击,所有 Privet API 调用都必须在请求中包含“X-Privet-Token”标头。“src=<api>”脚本代码无法添加标头,从而有效防范此类攻击。

6.2.2. XSRF

http://en.wikipedia.org/wiki/Cross-site_request_forgery
恶意网站可能会猜测 Privet 兼容设备的 IP 地址和端口号,并尝试使用 <iframe>、表单或其他跨网站加载机制来调用 Privet API。攻击者无法访问请求的结果,但如果请求会执行操作(例如打印),他们可以触发该操作。

为防范此类攻击,我们需要采取以下保护措施:

  • 将 /privet/info API 开放给 XSRF
  • /privet/info API 不得对设备执行任何操作
  • 使用 /privet/info API 接收 x-privet-token
  • 所有其他 API 都必须检查“X-Privet-Token”标头中是否存在有效的 x-privet-token。
  • x-privet-token 的有效期应仅为 24 小时。

即使攻击者能够执行 /privet/info API,也无法从响应中读取 x-privet-token,因此无法调用任何其他 API。

强烈建议使用以下算法生成 XSRF 令牌:

XSRF_token = base64( SHA1(device_secret + DELIMITER + issue_timecounter) + DELIMITER + issue_timecounter )

XSRF 令牌生成元素:

  • DELIMITER 是一个特殊字符,通常为“:”
  • issue_timecounter 是自某个事件(时间戳的纪元)或设备启动时间(对于 CPU 计数器)以来的秒数。当设备启动并运行后,issue_timecounter 会不断增加(请参阅下面的令牌验证)。
  • SHA1 - 使用 SHA1 算法的哈希函数
  • base64 - base64 编码
  • device_secret - 特定于设备的密钥。设备密钥必须在每次重启时更新。

建议采用以下方式生成设备 Secret:

  • 每次重新启动时都生成新的 UUID
  • 每次重新启动时生成一个 64 位随机数

设备无需存储其已发布的所有 XSRF 令牌。当设备需要验证 XSRF 令牌是否有效时,应先对令牌进行 base64 解码。从后半部分(明文)获取 issue_timecounter,并尝试生成 device_secret + DELIMITER + issue_timecounter 的 SHA1 哈希,其中 issue_timecounter 来自令牌。如果新生成的 SHA1 与令牌中的 SHA1 一致,设备现在必须检查 issue_timecounter 是否在当前时间计数器的有效期(24 小时)内。为此,设备会获取当前时间计数器(例如 CPU 计数器),然后从中减去 issue_timecounter。结果必须是自令牌签发以来的秒数。

重要提示:这是实现 XSRF 保护的推荐方式。Privet 规范的客户端不得尝试了解 XSRF 令牌,而应将其视为黑盒。图 6.2.3 展示了实现 X-Privet-Token 和验证典型请求的推荐方式。

6.2.3 X-Privet 令牌生成和验证序列图

6.3. 工作流程图

本部分将展示不同情况下的工作流程。

6.3.1. 打印机开箱即用工作流程

6.3.2. 注册打印机启动

6.3.3 XMPP 通知处理工作流

6.3.4. 检查打印机设置工作流