跳到主要内容

访问控制

最后更新:2026 年 8 月 26 日

访问控制决定的是谁有资格碰每一张 Table。它回答的问题和认证不同:登录确立的是_调用方是谁_,访问控制确立的是_这个调用方可以做什么_。这套模型有意做得简单 —— 按 Table 划分 Read 和 Write,按 Function 划分 Execute,面向三类调用方 —— 再配合按 Player 划分的行归属。

信息

默认情况下,游戏 client 能读不能写,而一张新建的 Table 对 client 是完全关闭的。你要按调用方、按 Table 有意识地逐一放开。如果 client 的调用因权限不足被拒,说了算的是运行时 —— 客户端一侧的检查可以提前拒绝,但它从不授予权限。


三种 subject​

每一次运行时调用都会解析出唯一一个 subject,依据是它本就携带的凭据。

Subject是谁
client你的游戏客户端 —— 公开的 pk_... key,无论有没有已登录的 Player
server你自己的服务器 —— 保密的 sk_... key(game server、脚本、CI)
backend你的服务端 Functions —— 云函数那一层

Backoffice(操作者)那一层是独立的,从不受这些 Table 设置的限制。


两层叠加​

访问权限按固定顺序判定:先看权限,再看行作用域。

  1. Table 权限 —— 这个 subject 究竟能不能读或写这张 Table?不能,则调用以 403 被拒,后面的一概不看。
  2. 行作用域 —— 对于处在 owned by a player 的 Table 上的 client,哪些行可见?Player 看得到自己的行;别人的行不可见。
调用 + 凭据→ subject权限Read / Write 开关否 → 403 forbidden行作用域owned Table 上的 client仅限自己的行他人的行404 —— 不可见

Table 权限​

每张 Table 都为每个 subject 各带一个 Read 开关和一个 Write 开关。Write 涵盖所有修改 —— 创建、更新和删除一视同仁,没有单独的删除权限。

SubjectRead 默认Write 默认
clientoffoff
serveronon
backendonon

新建的 Table 在两个方向上都是对 client 关闭的,而对 server 和 backend 开放。这就是为什么 client 对一张新 Table 调用 Update() 会被拒,直到你放开 client.write —— 这道写入边界是安全默认值,不是 bug。

注意

在一张并非 owned by a player 的 Table 上放开 client.write,等于让任何 client 都能写它的任何一行 —— 也就是共享状态、世界状态那种场景。只有 owned by a player 的 Table 才会把 client 的写入限制在调用方自己身上(见下)。放开无限制的 client 写入,请出于明确的判断。


client 的行归属​

在标记为 owned by player 的 Table 上,Table 权限依然决定 client 能不能 碰它;行归属接着决定 能碰哪些行。

  • Read owner —— client 只看得到自己的行。
  • Read public —— client 看得到所有行(归属不限制读取)。
  • Write —— client 创建时,会把当前行动的那个 Player 盖章为归属者;更新和删除只在调用方自己的行上才成功。
  • server 和 backend 从不受限 —— 它们读写任意行。

归属于其他 Player 的行被当作不可见,而不是“禁止访问”:读取、更新或删除它都返回 404 —— 与一行根本不存在时完全相同的结果。这是有意为之,好让写入路径永远不会泄露读取路径藏起来的行。

备注

既然他人的行和不存在的行在设计上就无从区分,就不要在 client 代码里依赖这个差别 —— 两者都只会返回一个普通的 not-found。


Function 的 Execute​

每个 Function 都为各个 subject —— client、server、backend —— 各带一个 Execute 开关,默认全部开启。没有 Execute 的 subject 会以 403 被拒。

Execute 与 Function 的 Auth 要求(none 或 player)是两回事:Auth 管的是_匿名 client 能不能调用_,Execute 管的是_这个 subject 是否被允许_。没有已登录 Player 的 client 去调用一个 player auth 的 Function,无论 Execute 如何都会以 401 被拒。


调用被拒时​

情形结果
subject 在该 Table 上没有 Read403 table_read_forbidden
subject 在该 Table 上没有 Write403 table_write_forbidden
subject 在该 Function 上没有 Execute403 function_execute_forbidden
匿名 client 读取按 owner 划分作用域的 Table403 owner_read_requires_player
匿名 client 写入 owned 的 Table422 acting_player_required
client 在没有 Player 的情况下调用 player auth 的 Function401 player_auth_required
client 碰了归属于其他 Player 的行404 not_found(不可见)
client 读取其他 Player 的账号403 player_read_forbidden
提示

如果 client 的写入意外被拒,先看这张 Table 的 client.write 开关,再看它是不是 owned by a player、以及你是否正以那一行归属者的身份行动。这两点合起来,几乎能解释数据面上的每一个 403 / 404。


下一步​