Privet 是云服务使用的 Cloud Device Local Discovery API。本文档分为以下几个部分:
1. 简介
云联网设备具有诸多优势。他们可以使用在线转化服务,在设备离线时托管作业队列,并且可从世界各地访问这些数据。不过,对于一个给定用户可以访问的许多云设备,我们需要提供一种方法来根据位置查找最近的设备。Privet 协议的目的是将云端设备的灵活性与适当的本地发现机制绑定,以便在新环境中轻松发现设备。
此协议的目标是:- 让云端设备可供本地发现
- 向云服务注册云端设备
- 将已注册的设备与其云表示法相关联
- 启用离线功能
- 简化实现流程,以便小型设备使用
Privet 协议由两大部分组成:发现和 API。发现广告用于在本地网络中查找设备,而 API 则用于获取设备的相关信息并执行一些操作。在本文档中,设备是指实现 Privet 协议的云连接设备。
2. Discovery
Discovery 是基于零配置 (mDNS + DNS-SD) 的协议。设备必须实现 IPv4 链路本地寻址。设备必须符合 mDNS 和 DNS-SD 规范。
- http://www.rfc-editor.org/rfc/rfc3927.txt(IPv4 链接本地)
- http://www.rfc-editor.org/rfc/rfc4862.txt(IPv6 链接本地)
- http://www.rfc-editor.org/rfc/rfc6762.txt (mDNS)
- http://www.rfc-editor.org/rfc/rfc6763.txt (DNS-SD)
设备必须根据上述规范执行名称冲突解决。
2.1. 服务类型
DNS Service Discovery 对服务类型使用以下格式:_applicationprotocol._transportprotocol。对于 Privet 协议,DNS-SD 的服务类型应为:_privet._tcp
设备也可以实现其他服务类型。建议对设备实现的所有服务类型使用相同的服务实例名称。例如:打印机可以实现“打印机 XYZ._privet._tcp”和“打印机 XYZ._printer._tcp”服务。这将简化用户的设置。但是,Privet 客户端只会查找“_privet._tcp”。
除了主服务类型之外,设备还必须针对其对应的子类型通告 PTR 记录(请参阅 DNS-SD 规范:7.1 级)。选择性实例枚举(子类型)”。格式应如下所示: _<subtype>._sub._privet._tcp
目前唯一支持的设备子类型是打印机。因此,所有打印机都必须通告两条 PTR 记录:
- _privet._tcp.local。
- _printer._sub._privet._tcp.local。
2.2. TXT 记录
DNS Service Discovery 定义了在 TXT 记录中添加有关服务的可选信息的字段。TXT 记录由键值对组成。每个键值对从长度字节开始,后跟最多 255 个字节的文本。键是第一个“=”字符之前的文本,值是第一个“=”字符之后的文字,直到结尾。该规范不允许在记录中出现任何值,在这种情况下,既不会包含“=”字符,也不会包含“=”字符之后的任何文字。(请参阅 DNS-SD 规范:6.1 版本。DNS TXT 记录常规格式规则”和“6.2. DNS-SD TXT Record Size”(推荐长度)。
Privet 要求设备在 TXT 记录中发送以下键值对。键值对字符串不区分大小写,例如“CS=online”和“cs=Online”是相同的。TXT 记录中的信息必须与通过 /info API 访问的信息相同(请参阅 4.1。 API 部分)。
建议将 TXT 记录大小保持在 512 字节以下。
2.2.1. txt
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=https://www.google.com/cloudprint
2.2.5. type
此设备支持的设备类型子类型列表(以英文逗号分隔)。格式为:"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
指示设备的当前连接状态。本规范中定义了 4 个可能的值。
- "online" 表示设备当前连接到了云。
- "离线”表示设备在本地网络中可用,但无法与服务器通信。
- "正在连接”表示设备正在执行其启动序列,尚未完全在线。
- "not-configured" 表示设备的互联网访问权限尚未配置。此值目前未使用,但可能在本规范的未来版本中很有用。
- cs=online
- cs=离线
- cs=连接
如果设备已注册云,则在启动时应检查与服务器的连接,以检测其连接状态(例如,调用 Cloud 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”标头为空或无效,设备必须在响应中提供“X-Privet-Token 错误无效”(invalid_x_privet_token,详见“错误”部分)。唯一的例外是 /info API。如需详细了解为何要执行此操作以及应如何生成令牌,请参阅附录 A:XSSI 和 XSRF 攻击与预防。
如果请求的 API 不存在或不受支持,设备必须返回 HTTP 404 错误。
4.1. API 可用性
在提供任何 API(包括 /info API)之前,设备必须联系服务器以检查本地设置。重启之间的设备必须保留本地设置。如果服务器不可用,则应使用最后的已知本地设置。如果设备尚未注册,应采用默认设置。
云打印设备必须按照以下步骤注册、接收和更新本地设置。
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”。 |
访问令牌已启用 | 布尔值(可选) | 指示是否应在本地网络上公开 /accesstoken API。默认情况下,值应为“true”。 |
打印机/本地打印_已启用 | 布尔值(可选) | 指示是否应在本地网络上提供本地打印功能(/printer/createjob、/printer/submitdoc、/printer/jobstate)。默认情况下,值应为“true”。 |
打印机/转化_已启用 | 布尔值(可选) | 指示本地打印是否可以将作业发送到服务器进行转换。只有在启用本地打印后才有意义。 |
超时时间值 | int(可选) | 表示 XMPP 通道 ping 之间的秒数。默认情况下,不少于 300 分钟(5 分钟)。 |
重要提示:缺少任何可选值表示设备完全不支持对应的功能。
4.1.2. 启动
设备启动时,它应联系服务器以检查本地网络中有哪些 API 可供公开。对于连接到云打印的打印机,他们应调用:
/cloudprint/printer?printerid=<printer_id>或
/cloudprint/list
首选的 /fitbit/printer 优于 /fitbit/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>
设备因 /fitbit/printer API 启动后或启动时显示“待处理”部分时,必须更新其内部状态以记住新设置。它必须调用服务器 API 以确认新设置。对于云端打印机,设备必须调用 /借用/更新 API,并在注册期间使用“local_settings”参数。
重新连接到 XMPP 通道时,设备必须调用 /fitbit/printer API,以检查自上次以来本地设置是否发生了变化。
4.1.3.1. 本地设置待处理
设备用于调用服务器 API 的“local_settings”参数绝不能包含“待处理”部分。
4.1.3.2. 当前本地设置
只有设备可以更改“local_settings”的“当前”部分。 其他人会更改“待处理”部分,并等到设备将更改应用到“当前”部分。
4.1.4. 线下
如果在启动过程中无法连接服务器,设备必须在通知后使用上次的已知本地设置。
4.1.5. 从服务中删除设备
如果设备已从服务中删除(例如 GCP),系统会向设备发送 XMPP 通知。通知采用以下格式:
<device_id>/delete
收到此类通知后,设备必须前往服务器查看其状态。云打印设备必须使用:
/cloudprint/printer?printerid=<printer_id>
设备必须收到成功 HTTP 响应且成功=false 且无设备/打印机说明。这意味着设备已从服务器中移除,设备必须清除其凭据并进入默认出厂设置模式。
每当设备收到回复(表明设备因 /fitbit/printer API 而被删除,如启动、更新设置通知、每日 ping)时,都必须删除其凭据并进入默认模式。
4.2. /privet/info API
信息 API 是必需的,必须由所有设备实现。它是针对“/privet/info”网址的 HTTP GET 请求,网址为:GET /privet/info HTTP/1.1
info API 会返回有关其支持的设备和功能的基本信息。此 API 绝不能更改设备状态或执行任何操作,因为它容易受到 XSRF 攻击。 这是唯一一个可以拥有空“X-Privet-Token”标头的 API。客户端应使用“X-Privet-Token”标头设置为 X-Privet-Token 来调用 /privet/info API: “"”
发现期间,info API 返回的数据必须与 TXT 记录中提供的数据一致。
4.2.1.输入
/privet/info API 没有输入参数。
4.2.2. 返回
/privet/info API 会返回有关设备和支持的功能的基本信息。
TXT 列指示 DNS-SD TXT 记录中的相应字段。
值名称 | 值类型 | 说明 | TXT |
---|---|---|---|
version | 字符串 | 支持的 API 的最高版本 (major.minor),当前为 1.0 | |
name | 字符串 | 简单易懂的设备名称。 | ty |
广告内容描述 | 字符串 | (可选)设备说明。应可由用户修改。 | note |
url | 字符串 | 此设备正在通信的服务器的网址。网址必须包含协议规范,例如:https://www.google.com/fitbit。 | url |
类型 | 字符串列表 | 支持的设备类型列表。 | 类型 |
id | 字符串 | 设备 ID,如果设备尚未注册,则为空。 | id |
设备状态 | 字符串 | 设备的状态。 “空闲”表示设备已准备就绪 处理表示设备处于忙碌状态,功能可能在一段时间内受到限制 “停止”表示设备未工作,需要用户干预 | |
连接状态 | 字符串 | 到服务器的连接状态(base_url) 在线 - 可用连接 离线 - 无连接 连接 - 执行启动步骤 未配置 - 连接尚未配置 注册的设备可能会根据通知渠道的状态(例如 XMPP 连接状态)报告其连接状态。 | cs |
制造商 | 字符串 | 设备制造商的名称 | |
模式 | 字符串 | 设备的型号 | |
序列号 | 字符串 | 唯一设备标识符。在本规范中,此值必须是 UUID。(GCP 1.1 规范) (可选)我们强烈建议您在任何位置使用相同的序列号,以便不同的客户端可以识别同一设备。例如,实现 IPP 的打印机可以在“printer-device-id”字段中使用此序列号。 | |
固件 | 字符串 | 设备固件版本 | |
uptime | int | 设备启动的秒数。 | |
设置网址 | 字符串 | (可选)包含设置说明的网页网址(包括协议) | |
support_url | 字符串 | (可选)支持页面的网址(包括协议)、常见问题解答信息 | |
更新网址 | 字符串 | (可选)包含更新固件说明的网页网址(包括协议) | |
x-privet-token | 字符串 | 必须传递给所有 API 的 X-Privet-Token 标头的值,以防止 XSSI 和 XSRF 攻击。如需了解详情,请参阅 6.1。 | |
API | API 说明 | 支持的 API 列表(如下所述) | |
语义状态 | JSON | (可选)设备的语义状态,采用 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/features - 用于检索设备功能的 API(如需了解详情,请参阅 /privet/features API)。
- /privet/printer/* - 设备类型“printer”专用 API,如需了解详情,请参阅打印机专用 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. 错误
/privet/info API 应该仅在缺少 X-Privet-Token 标头时返回错误。必须是 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 个步骤(请参阅云打印的匿名注册)。
- 启动匿名注册流程。
- 客户端通过调用 /privet/register API 来启动此流程。此时,设备可能会等待用户确认。
- 获取声明令牌。
客户端会轮询以确定设备何时可以继续运行。设备准备就绪后,会向服务器发送请求以检索注册令牌和注册网址。接收到的令牌和网址应返回给客户端。在此步骤中,如果设备再次收到初始化注册的调用,则:
- 如果该用户是开始注册的用户,请删除所有先前的数据(如果有),并开始新的注册流程。
- 如果这是其他用户 - 返回 device_忙碌 错误和 30 秒超时。
完成注册流程。
在客户端认领设备后,客户端应通知设备完成注册。注册流程完成后,设备应发送更新通知,包括新获取的设备 ID。
注意:当设备正在处理 /privet/register API 调用时,无法同时处理其他 /privet/register API 调用。设备必须返回 device_忙碌错误和 30 秒超时。
强烈建议在设备上确认用户注册。实现后,设备在收到 /privet/register?action=start API 调用后必须等待用户确认。客户端将调用 /privet/register?action=getClaimToken API 来确定用户确认是否已完成以及声明令牌何时可用。如果用户在设备上取消注册(例如,按“取消”按钮),必须返回 user_cancel 错误。如果用户在特定时间范围内未确认注册,必须返回 Confirm_timeout 错误。如需了解详情,请参阅“默认”部分。
4.3.1. 输入
/privet/register API 具有以下输入参数:名称 | 值 |
---|---|
操作 | 可以是以下任意一项:
start - 开始注册流程 getClaimToken - 检索设备的声明令牌 cancel - 取消注册流程 complete - 完成注册流程 |
用户 | 认领此设备的用户的电子邮件地址。 |
设备必须检查来自所有操作(启动、getClaimToken、取消、完成)的电子邮件地址是否匹配。
4.3.2. 返回
/privet/register API 会返回以下数据:值名称 | 值类型 | 说明 |
---|---|---|
操作 | 字符串 | 与输入参数的操作相同。 |
用户 | 字符串(可选) | 与输入参数中的用户相同(如果在输入中省略,则可能会缺失)。 |
token | 字符串(可选) | 注册令牌(对于“getClaimToken”响应是必需的,对于“start”、“complete”和“cancel”除外)。 |
声明网址 | 字符串(可选) | 注册网址(对于“getClaimToken”响应为必需参数,对于“start”、“complete”和“cancel”除外)。对于云端打印机,它必须是从服务器收到的“complete_appeal_url”。 |
automated_claim_url | 字符串(可选) | 注册网址(对于“getClaimToken”响应为必需参数,对于“start”、“complete”和“cancel”除外)。对于云端打印机,它必须是从服务器收到的“auto_appeal_url”。 |
device_id | 字符串(可选) | 新设备 ID(对于“开始”响应省略,对于“完成”为必需参数)。 |
只有在完成注册后,设备才能在 /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 可能会返回以下错误(如需了解详情,请参阅“错误”部分):错误 | 说明 |
---|---|
设备繁忙 | 设备正忙,无法执行请求的操作。请在超时后重试。 |
待处理用户操作 | 为响应“getClaimToken”,此错误表示设备仍处于待处理状态,应在超时后重试“getClaimToken”请求。 |
user_cancel | 用户明确取消了设备的注册流程。 |
确认超时 | 用户确认超时。 |
无效操作 | 调用无效操作。例如,如果客户端在调用 action=start 和 action=getClaimToken 之前调用了 action=complete。 |
参数无效 | 请求中指定的参数无效。(应安全忽略未知参数,以确保未来的兼容性)。例如,如果客户端调用 action=unknown 或 user=,则返回此值。 |
device_config_error | 设备端的日期/时间(或其他一些设置)有误。用户需要转到设备内部网站并配置设备设置。 |
离线 | 设备目前处于离线状态,无法与服务器通信。 |
服务器错误 | 注册过程中服务器出错。 |
无效令牌令牌 | 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 调用后,应调用服务器来检索指定用户的访问令牌,然后将令牌返回给客户端。然后,客户端会使用访问令牌通过云端访问此设备。
云打印设备必须调用以下 API:
/cloudprint/proximitytoken并传递本地 API 中的“printerid=<printer_id”和“user”参数。如果成功,服务器响应将包含以下对象:
"proximity_token": { "user": "user@domain.com", "token": "AAA111222333444555666777", "expires_in": 600 }云打印设备必须将响应中“neighborhood_token”对象的值传递给本地 /privet/accesstoken API 调用。如果设备可以传递所有参数(包括本规范中未介绍的参数),则更加有利(未来)。
4.4.1. 输入
/privet/accesstoken API 具有以下输入参数:名称 | 值 |
---|---|
用户 | 打算使用此访问令牌的用户的电子邮件地址。请求可以为空。 |
4.4.2. 返回
/privet/accesstoken API 会返回以下数据:值名称 | 值类型 | 说明 |
---|---|---|
token | 字符串 | 服务器返回的访问令牌 |
用户 | 字符串 | 与输入参数中的用户相同。 |
expires_in | int | 此令牌到期前的秒数。从服务器接收并在此响应中传递。 |
示例:
{ "token": "AAA111222333444555666777", "user": "user@domain.com", "expires_in": 600 }
4.4.3. 错误
/privet/accesstoken API 可能会返回以下错误(有关详情,请参阅“错误”部分):错误 | 说明 |
---|---|
离线 | 设备目前处于离线状态,无法与服务器通信。 |
拒绝访问 | 权限不足。访问遭拒。如果服务器明确拒绝了请求,设备应返回此错误。 |
参数无效 | 请求中指定的参数无效。(应安全忽略未知参数,以确保未来的兼容性)。例如,如果客户端调用 /accesstoken?user= 或 /accesstoken。 |
服务器错误 | 服务器错误。 |
无效令牌令牌 | X-Privet-Token 在请求中无效或为空。 |
如果设备未公开 /privet/accesstoken API,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。
4.5. /privet/Capabilities API
/privet/features API 是可选的。这是一个 HTTP GET 请求。/privet/features API 必须检查有效的“X-Privet-Token”标头。设备必须在“/privet/features”网址中实现此 API:GET /privet/capabilities HTTP/1.1当设备收到 /features API 调用时,如果设备可以,它应该与服务器联系以获取更新后的功能。例如,如果打印机支持通过云打印服务向自身发布打印任务,则应返回云打印服务返回的功能。在这种情况下,云打印可能会添加原始打印机的功能,因为这些功能可能会添加在将作业发送到打印机之前执行的新功能。最常见的情况是受支持的文档类型列表。如果打印机处于离线状态,应返回其支持的文档类型。但是,如果打印机在线并注册了云打印,则必须返回“*/*”作为受支持的类型之一。在这种情况下,云打印服务将执行必要的转换。对于离线打印,打印机必须至少支持“image/pwg-raster”格式。
4.5.1. 输入
/privet/Capabilities API 具有以下输入参数:名称 | 值 |
---|---|
离线 | (可选)只能设为“离线=1”。在这种情况下,设备应返回相应功能以供离线使用(如果这些功能与“在线”功能不同)。 |
4.5.2. 返回
/privet/Capabilities API 会以 Cloud Device description (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" } ] } }
注意:打印机会使用顺序表示支持的内容类型优先级。例如,在上面的示例中,打印机指定“product/pdf”数据优于“image/pwg-raster”和“image/jpeg”。如有可能,客户端应遵循打印机优先级设置(如需了解详情,请参阅 CDD 文档)。
4.5.3. 错误
/privet/Capabilities API 可能会返回以下错误(如需了解详情,请参阅“错误”部分):错误 | 说明 |
---|---|
无效令牌令牌 | X-Privet-Token 在请求中无效或为空。 |
如果设备未公开 /privet/features API,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。
4.6. 错误
上述 API 会返回错误,格式如下:值名称 | 值类型 | 说明 |
---|---|---|
错误 | 字符串 | 错误类型(按 API 定义) |
广告内容描述 | 字符串(可选) | 直观易懂的错误说明。 |
server_api | 字符串(可选) | 如果发生服务器错误,此字段包含失败的服务器 API。 |
服务器代码 | int(可选) | 如果发生服务器错误,此字段包含服务器返回的错误代码。 |
server_http_code | int(可选) | 如果发生服务器 HTTP 错误,此字段将包含返回的 HTTP 错误代码服务器。 |
timeout | int(可选) | 客户端在重试前等待的秒数(仅适用于可恢复的错误)。客户端必须将此值的实际超时时间随机设置为 + 20% 的值。 |
如果缺少 X-Privet-Token 标头,所有 API 都必须返回 HTTP 400 错误。
HTTP/1.1 400 缺少 X-Privet-Token 标头。
示例 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. 打印机 API
此协议支持的设备类型之一是打印机类型。支持此类的设备可以实现一些打印机专用的功能。理想情况下,通过云端打印机进行打印时,将通过云端服务器进行打印:
![](https://developers.google.cn/cloud-print/images/privet_image07.jpg?authuser=9&hl=sv)
在某些情况下,客户端可能需要在本地发送文档。如果客户端没有 Google ID 或无法与云打印服务器通信,则可能需要使用此方法。在这种情况下,系统会将打印作业在本地提交至打印机。反过来,打印机将使用云打印服务进行作业排队和转换。由于通过云端提交作业,打印机会将作业重新发布到本地的云打印服务,然后提出请求。此过程会在服务(转化)和打印任务管理/跟踪方面提供灵活的用户体验。
![](https://developers.google.cn/cloud-print/images/privet_image08.jpg?authuser=9&hl=sv)
由于云打印服务实现了转换,因此打印机应在支持的内容类型列表中通告支持所有输入格式 ("*/*"):
{ "version": "1.0", "printer": { "supported_content_type": [ { "content_type": "image/pwg-raster" }, { "content_type": "*/*" } ] } }
在某些情况下,需要使用完全离线的解决方案。由于打印机支持有限数量的输入格式,因此客户端需要将文档转换为多种原生支持的打印机格式。
![](https://developers.google.cn/cloud-print/images/privet_image04.jpg?authuser=9&hl=sv)
该规范要求所有打印机至少支持离线打印保护套的 PWG 光栅格式 (“qu/>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 会返回以下数据:值名称 | 值类型 | 说明 |
---|---|---|
作业 ID | 字符串 | 新创建的打印任务的 ID。 |
expires_in | int | 此打印任务的秒数。 |
示例:
{ "job_id": "123", "expires_in": 600 }
5.1.3. 错误
/privet/printer/createjob API 可能会返回以下错误(如需了解详情,请参阅“错误”部分):错误 | 说明 |
---|---|
无效票 | 提交的打印票无效。 |
打印机处于忙碌状态 | 打印机繁忙,目前无法处理 /createjob。请在超时后重试。 |
打印机出错 | 打印机处于错误状态,需要用户互动才能修复。 说明应包含更详细的说明(例如“卡纸 1 中的卡纸”)。 |
无效令牌令牌 | X-Privet-Token 在请求中无效或为空。 |
如果设备未公开 /privet/printer/createjob,则必须返回 HTTP 404 错误。如果缺少 X-Privet-Token 标头,设备必须返回 HTTP 400 错误。
5.2. /privet/printer/submitdoc API
要通过本地网络实现打印(离线或发布到云打印),需要使用 /privet/printer/submitdoc API。这是一个 HTTP POST 请求。/privet/printer/submitdoc API 必须检查有效的“X-Privet-Token”标头。设备必须在“/privet/printer/submitdoc”网址上实现此 API:POST /privet/printer/submitdoc HTTP/1.1收到 /privet/printer/submitdoc API 调用后,打印机应开始打印。如果无法开始打印,则必须返回错误 print_strict 以及建议的超时时长,供客户端在重试前等待。
如果打印机无法将所有数据保留在其内部缓冲区中,则应使用 TCP 机制减慢数据传输过程,直到打印到文档的一部分,以便让缓冲区的一部分再次可用。(例如,打印机可能会在 TCP 层上设置 windowsize=0,这会导致客户端处于等待状态。)
向打印机提交文档可能需要大量时间。在打印过程中,客户端应该能够检查打印机的状态和作业(高级打印)。为此,打印机必须允许客户端在处理 /privet/printer/submitdoc API 调用时调用/privet/info 和 /privet/printer/jobstate API。建议所有客户端启动一个新线程来执行 /privet/printer/submitdoc API 调用,以便主线程可以使用 /privet/info 和 /privet/printer/jobstate API 检查打印机和作业状态。
注意:在本地打印作业完成或中止后,强烈建议您(并在此规范的未来版本中强制要求)将作业的最终状态报告给 /fitbit/submit 界面,以便进行结算和提供用户体验。必须提供参数“printerid”、“title”、“contentType”和“final_semantic_state”(采用 PrintJobState 格式),以及参数“tag”(重复参数)和“ticket”(工单格式,是工单格式)。请注意,提供的 PrintJobState 实际上必须是 final,也就是说,其类型必须为 DONE 或 ABORTED,并且在 ABORTED 的情况下必须提供原因(如需了解详情,请参阅 JobState)。另请注意,其规范未提及这个使用 /fitbit/submit 接口报告本地打印任务的情况,因为该部分旨在描述该接口的主要用途:提交打印作业,并在 "content" 参数中提供要打印的文档。
5.2.1. 输入
/privet/printer/submitdoc API 具有以下输入参数:名称 | 值 |
---|---|
作业 ID | (可选)打印任务 ID。对于简单打印的情况,可以省略(参见上文)。必须与打印机返回的标签一致。 |
用户名 | (可选)简单易懂的用户名。此设置并不完整,应该仅用于打印任务注释。如果作业重新发布到云打印服务,则应将此字符串附加到云打印作业。 |
客户名称 | (可选)发出请求的客户端应用的名称。仅用于显示目的。如果作业重新发布到云打印服务,则应将此字符串附加到云打印作业。 |
作业名称 | (可选)要记录的打印任务的名称。如果作业重新发布到云打印服务,则应将此字符串附加到云打印作业。 |
离线 | (可选)只能设为“离线=1”。在这种情况下,打印机应仅尝试离线打印(不得重新发布到云打印服务器)。 |
请求正文应包含有效的打印文档。&Content.Length 应包含请求的正确长度。"Content-Type”标头应设为文档 MIME 类型,并且与 CDD 中的某个类型匹配(除非 CDD 指定了“*/*”)。
强烈建议客户端在此请求中提供有效的用户名(或电子邮件地址)、客户端名称和作业名称。这些字段仅用于界面,以改善用户体验。
5.2.2. 返回
/privet/printer/submitdoc API 会返回以下数据:值名称 | 值类型 | 说明 |
---|---|---|
作业 ID | 字符串 | 新建的打印任务(简单打印)或请求中指定的 job_id(高级打印)的 ID。 |
expires_in | int | 此打印任务的秒数。 |
作业类型 | 字符串 | 所提交文件的内容类型。 |
职位大小 | 整型 64 位 | 打印数据的大小(以字节为单位)。 |
作业名称 | 字符串 | (可选)输入的名称与输入的内容相同(如有)。 |
示例:
{ "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 可能会返回以下错误(如需了解详情,请参阅“错误”部分):错误 | 说明 |
---|---|
打印作业无效 | 请求中指定的作业 ID 无效/已过期。请在超时后重试。 |
文档类型无效 | 打印机不支持文档 MIME 类型。 |
无效文档 | 提交的文件无效。 |
document_too_large | 文件超出了大小上限。 |
打印机处于忙碌状态 | 打印机繁忙,目前无法处理文档。请在超时后重试。 |
打印机出错 | 打印机处于错误状态,需要用户互动才能修复。 说明应包含更详细的说明(例如“卡纸 1 中的卡纸”)。 |
参数无效 | 请求中指定的参数无效。(应安全忽略未知参数,以便将来兼容) |
user_cancel | 用户明确取消了设备的打印流程。 |
服务器错误 | 未能将文档发布到云打印。 |
无效令牌令牌 | 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”打印机“打印机繁忙”错误进行处理,并将“超时”设置为 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 具有以下输入参数:名称 | 值 |
---|---|
作业 ID | 要返回其状态的打印任务 ID。 |
5.3.2. 返回
/privet/printer/jobstate API 会返回以下数据:值名称 | 值类型 | 说明 |
---|---|---|
作业 ID | 字符串 | 包含状态信息的打印任务 ID。 |
state | 字符串 | 草稿 - 已在设备上创建打印任务(尚未收到 /privet/printer/submitdoc 调用)。 已加入队列 - 已接收打印任务并将其加入队列,但打印尚未开始。 in_progress - 打印任务正在进行打印。 已停止 - 打印任务已暂停,但可以手动或自动重启。 done - 表示打印任务已完成。 已中止 - 打印作业失败。 |
广告内容描述 | 字符串 | (可选)简单易懂的打印任务状态说明。如果 state< 为 stop 或 aborted,则应添加其他信息。semantic_state 字段通常用于为客户端提供更优质、更有意义的说明。 |
expires_in | int | 此打印任务的秒数。 |
作业类型 | 字符串 | (可选)所提交文件的内容类型。 |
职位大小 | 整型 64 位 | (可选)打印数据的大小(以字节为单位)。 |
作业名称 | 字符串 | (可选)输入的名称与输入的内容相同(如有)。 |
server_job_id | 字符串 | (可选)从服务器返回的作业的 ID(如果作业已发布到云打印服务)。离线打印时省略。 |
语义状态 | JSON | (可选)作业的语义状态,采用 PrintJobState 格式。 |
示例(通过云打印进行打印):
{ "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 可能会返回以下错误(如需了解详情,请参阅“错误”部分):错误 | 说明 |
---|---|
打印作业无效 | 请求中指定的作业 ID 无效/已过期。 |
服务器错误 | 未能获取打印任务状态(适用于发布到云打印的打印任务)。 |
无效令牌令牌 | 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、本地打印)应停用。
- 注册需要与设备进行实际互动。
- 用户必须对设备进行实际操作(例如,按下按钮),以确认他们对设备的访问。
- 在用户执行上述操作后,打印机应发送 /fitbit/register 请求。在执行此操作之前,不应发送此请求(请参阅序列图 1)。
- 如果设备正在处理 /privet/register 请求(例如,等待上述操作),则必须拒绝所有其他 /privet/register 请求。在这种情况下,设备必须返回 device_忙碌错误。
- 任何未收到上述物理操作的 /register 请求都应在 60 秒内超时。在这种情况下,设备必须返回 Confirm_timeout 错误。
- 可选:建议但不强制要求,以下几点可改善用户体验:
- 打印机可能会闪烁指示灯或屏幕,以表明用户需要执行操作以确认注册。
- 打印机可能会在屏幕上声明“它正在注册用户 Google 云打印‘abc@def.com’- 按‘确定’以继续”,其中 abc@def.com 是 /register API 调用中的用户参数。这会让用户更清楚地知道:
- 他们的确认请求是
- 如果他/她没有触发请求,会发生什么情况。
- 除了通过打印机进行确认之外(例如“按下 OK 按钮”后,打印机可能也会向用户提供取消请求的按钮(例如,按“取消”以拒绝请求。这样一来,未触发注册请求的用户会在 60 秒超时之前取消该请求。在这种情况下,设备必须返回 user_cancel 错误。
- 所有权转让:
- 系统可能会从 Cloud 服务中明确删除该设备。
- 如果设备收到成功请求,但由于 /借用/打印机(用于 GCP)调用而没有显示设备说明,则必须恢复到默认(开箱即用)模式。
- 如果设备凭据不再工作(由于服务器发送的“凭据无效”响应),必须恢复到默认(开箱即用)模式。
- 本地恢复出厂设置必须清除设备的凭据,并将其设为默认状态。
- 可选:设备可能会提供一个菜单项,用于清除凭据并将其置于默认模式。
- 支持 XMPP 通知的设备必须具备 ping 服务器的功能。ping 超时必须可从服务器通过“local_settings”进行控制。
- 设备可能会每天 24 小时明确 ping 一次服务器(对于 GCP,除了 XMPP ping 以外,还会针对 GCP 使用 /借用/打印 API),以确保它们保持同步。建议在 24-32 小时内对检查窗口进行随机化处理。
- 可选:对于云打印设备,建议(而非强制要求)提供手动方式(按钮),以便用户从设备开始检查新的打印任务。部分打印机已启用此功能。
- (可选)企业打印机可能会完全停用本地发现功能。在这种情况下,设备必须更新服务器上的这些本地设置。新的本地设置必须为空(将“local_discovery”设置为“false”,表示可以通过 GCP 服务将其重新启用)。
6.1.2 默认注册图
![](https://developers.google.cn/cloud-print/images/privet_image06.png?authuser=9&hl=sv)
6.2. XSSI 和 XSRF 攻击与预防
本部分将介绍设备上可能会出现 XSSI 和 XSRF 攻击的方法,以及如何防范这些攻击(包括令牌生成技术)。如需了解详情,请参阅: http://chromebooksecurity.blogspot.com/2011/05/website-security-for-webmasters.html
通常,当网站使用 Cookie 身份验证机制时,可能会发生 XSSI 和 XSRF 攻击。虽然 Google 不会通过其云打印服务使用 Cookie,但此类攻击仍有可能。从设计上来说,本地网络访问权限会隐式信任请求。
6.2.1. XSSI
恶意网站可能会猜测与 Privet 兼容的设备的 IP 地址和端口号,并尝试使用 <script=> 标记内的 API 调用 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;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 - 设备专用密钥。每次重启时都必须更新设备密钥。
生成设备密钥的推荐方法:
- 每次重启时生成新的 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 令牌生成和验证序列图
![](https://developers.google.cn/cloud-print/images/privet_image03.png?authuser=9&hl=sv)
6.3. 工作流程图
本部分将介绍不同情况下的工作流程。6.3.1. 开箱即用的工作流
![](https://developers.google.cn/cloud-print/images/privet_image01.png?authuser=9&hl=sv)
6.3.2. 已注册打印机启动
![](https://developers.google.cn/cloud-print/images/privet_image02.png?authuser=9&hl=sv)
6.3.3 XMPP 通知处理工作流
![](https://developers.google.cn/cloud-print/images/privet_image05.png?authuser=9&hl=sv)
6.3.4. 检查打印机设置工作流
![](https://developers.google.cn/cloud-print/images/privet_image00.png?authuser=9&hl=sv)