Web Google 表格访问
Google表格访问方式取决于你携带的凭据。在已部署的网站上有两条并存的入口:用 Google 账号登录(OAuth),或者选择一个由你自己的浏览器直接使用的服务账号密钥文件。Unity / 本地路径则保留它那个只留在本机的服务账号密钥,不受影响。
在已部署的站点上——OAuth,可读可写
在已部署的站点上,一位已登录的用户可以通过自己的 OAuth 令牌,同时读取和写入 Google表格。
- 最小权限。 登录时只会请求你的基本资料,以及按文件授予的工作表访问权限(
drive.file):应用只能读取和写入你已经明确为它打开过的那些工作表文件。不会有任何面向整个账号的权限——它无法列出或浏览你 Drive 中的其余内容,一个你没有为它打开过的文件则始终保持不可见。 - 同一套核心计划。 Web 端的一次写入,走的是与其他每一条路径完全相同的核心计划,以及同一套共享的乐观锁校验——实时工作表会在发送前的最后一刻被重新检查,因此一个在你导入之后被第三方更改过的单元格会被跳过,绝不会被覆盖。唯一不同的只是传输层:在这条路径上,它携带的是 OAuth bearer 令牌。
- 工作表自己的 ACL 是最终的裁决者。 谁可以写入不是由 SheetForge 决定的,而是由 Google 决定的。来自 Google 的一个
403会被重新映射为一条如实的权限提示,而不是一次部分写入——因此一次拒绝读起来就是一次拒绝。
这个 OAuth 令牌保存在会话 cookie 中(一个 JWT),由服务器端的操作使用。唯一的例外是接下来要说明的 Google 文件选择器:它是一个运行在页面里的 Google 组件,因此打开它会把你自己这次会话的一个短时效访问令牌交给页面。
从选择器开始——你永远不需要 ID
已登录的标签页会以从 Google 选择一个电子表格开头:Google 自己的文件选择器会在页面上打开,你选中那个电子表格后,它的 ID 就会落入面板中,导入随即自行运行。选择这个动作本身,也正是在 drive.file 之下把该文件的访问权限授予给应用,因此一个手势就同时完成了两件事。当前电子表格这一行会显示面板当前指向的是什么。
手动输入 ID 依然可行——它被移到了一个高级:直接填写电子表格 ID的折叠项之下,此前保存过的 ID 会像以前一样继续正常工作。这里如实标出了其中的一个限制:Google 只会打开你至少选择过一次的文件,因此一个从未被选择过的文件的 ID 会被拒绝,直到你去选择它为止(下文的**允许文件访问…**一行正是用来处理这种情况的)。
为应用打开一个文件——只需一次
在 drive.file 之下,权限是逐个文件增长的。如果应用碰到一张它还没有访问权限的工作表——无论是在高级选项下输入的 ID,还是切换账号之后保存下来的 ID——就会在它碰到权限错误的那一刻,打开Google 自己的文件选择器,并且已经预先定位到了那张工作表。只需选择一次这个文件,你已经开始的那次导入或推送就会自行完成。从那以后,这张工作表的行为就会和它一直以来完全一样。
- 不需要提前做任何准备。 你不必提前授予访问权限;选择器会恰好在需要的那一刻出现,并且已经预设好了对应的文件,选择完成后,你原本请求的那个操作会自动重试。
- 随时可以重新授权。 Google 面板里始终保留着一条**允许文件访问…**选项,可以随时打开同一个选择器——比如在切换到另一个 Google 账号之后。
- 一条如实的边界。 这个选择器是运行在页面里的一个 Google 组件,因此在使用它期间,页面会持有你这次会话的一个短时效访问令牌(最长有效期一小时),它是从一个要求你登录的同源端点获取来的。这与下文的密钥文件路径交给页面的信任属于同一种类型——守护它的,是应用严格的 Content Security Policy(内容安全策略)。这个令牌不会被保存,也不会被记录到日志中。
密钥文件标签页故意保留了它的 ID 输入框:服务账号没有可供选择的已登录浏览会话,因此一个 ID——配合把该工作表共享给这个账号——在那里依然是唯一如实可行的进入方式。
在已部署的站点上——服务账号密钥文件,由你的浏览器签名
Google 面板的第二个标签页可以接收一个服务账号 JSON 密钥文件。这把密钥所做的一切都发生在你的浏览器中:
- 密钥永远不会抵达服务器。 浏览器本身会对令牌请求进行签名,并直接调用 Google 的 API,因此密钥、已签名的请求、访问令牌以及你的工作表数据,都不会接触到 SheetForge 的服务器。服务器侧的服务账号路由在已部署的站点上依然保持关闭(
501),和以前一样——这条路径只是新增了一种能力,而不会因此打开任何一道口子。 - 记住这把密钥的方式,对它实际保存了什么如实相告。 如果你选择记住这把密钥,密钥文件的文本会被立即丢弃;真正被保留下来的——就在这台设备上、在浏览器的存储中——是一把签名密钥:页面上的脚本只能用它来签名,却永远无法把它读取回来。但任何能打开这个浏览器配置文件的人,仍然可以把它恢复出来,所以请把这台设备当作保存着密钥文件本身一样对待。按下忘记密钥会立即删除它——在所有已打开的标签页中都会生效——应用同时还会请求 Google 撤销当前的访问令牌。
- 计划相同,校验也相同。 读取和写入都会经过与其他路径相同的核心计划,以及相同的发送前校验。
Unity / 本地路径
Unity 与本地开发服务器所使用的服务账号密钥,是从一个仅限本机的路径读取的,永远不会被上传或打包——这正是Google表格设置中所描述的那把密钥;请把它放在 Assets/ 之外,也放在仓库之外。仓库中不包含任何凭据。
下拉菜单会随每次推送刷新
从 Web 应用推送之后,工作表的数据验证下拉菜单会被自动重写——所有标签页在同一批次中一起处理,引用列的下拉菜单会被写成一个覆盖目标工作表键列的区域范围,因此它会随着记录的增加而扩大。一条指向不存在的工作表标签页的规则会被跳过并报告。这与编辑器的 Google 推送所写入的内容一致。
不同路径各自能做什么
| 运行位置 | Google 凭据 | 读取 | 写入 |
|---|---|---|---|
| Unity / 本地 | 服务账号密钥,从仅限本机的路径读取 | 可以 | 可以——外科手术式的精准单元格写入、结构重写、推送 |
| 已部署的站点 | 已登录用户的 OAuth 令牌(drive.file——为该应用打开过的文件) | 可以 | 可以——同一套核心计划 + 乐观锁校验 |
| 已部署的站点 | 一个你自己选择的服务账号密钥文件,由你的浏览器直接使用 | 可以 | 可以——同一套核心计划与校验;请求直接从你的浏览器发往 Google |
| 已部署的站点 | 服务器侧的服务账号路由 | —— | 被关闭(501) |
无论运行在哪里,写入行为都是完全一致的,因为每一条路径都共享同一套核心计划和发送前校验。唯一变化的只有传输层——一个由浏览器签名的服务账号令牌、一个按用户区分的 OAuth bearer 令牌,或者本地服务器的密钥。
相关页面
- Google表格设置——为 Unity 路径创建服务账号与 JSON 密钥
- 数据源、导出与推送——Web 写入路径所共享的推送安全链
- SheetForge Web——这项访问能力所在的浏览器应用