【A11】服务器底层权限表笔记

从服务器职责出发,梳理权限表的定位、结构、使用方式。服务器是"权威账本 + 数据仓库 + 路由",纯响应式;读写与同步由客户端主导;版本号由时间式机制天然承载。


一、服务器职责

1.1 三件事

text 复制代码
服务器只做三件事:
  1. 维护权限表(grants):谁对什么有权限
  2. 存储数据(values):资源的字节 + 版本号
  3. 响应请求:按权限返回 / 按权限接收 / 移交仲裁

不解释数据、不执行业务、不做权限计算、不主动推送。

1.2 不做什么

不做 原因
不解释 R(字节内容) 只存 bytes
不执行业务逻辑 客户端负责
不计算权限(继承、组、角色) 中间环节暂不考虑,最终落到 grants
不主动下传 纯响应式,客户端请求才下发
不做同步决策 同步由客户端主导

服务器是"账本 + 仓库 + 路由",不是"业务处理器",也不是"主动推送者"。

1.3 与客户端的分工

text 复制代码
服务器:关心设备(pc_id),按表响应请求
客户端:关心登录人(user),按权限表过滤展示,主导读写与同步

服务器以"设备"为单位,纯响应
客户端以"用户"为单位,主动驱动

二、权限表:grants

2.1 结构

sql 复制代码
CREATE TABLE grants (
    resource  INTEGER NOT NULL,      -- 资源编号
    pc_id     INTEGER NOT NULL,      -- 设备编号
    level     TEXT    NOT NULL,      -- 'read' | 'own'
    PRIMARY KEY (resource, pc_id)
);

-- 一个资源只有一个写者
CREATE UNIQUE INDEX idx_one_own_per_resource
ON grants(resource) WHERE level = 'own';

-- 按设备查
CREATE INDEX idx_grants_pc ON grants(pc_id);

2.2 语义

text 复制代码
有记录        → 能读(客户端可请求数据)
level='own'  → 还能写(是数据源,隐含读权限)

读:有记录 = 有读权限(客户端可请求)
写:level='own' = 写权限(同时隐含读权限)

注意:有读权限 ≠ 服务器主动下传。有读权限只是"客户端可以请求"。

2.3 唯一性

text 复制代码
一个资源:
  - 多行(多个读者,level='read')
  - 一行 own(唯一写者,level='own')

由部分唯一索引保证。

三、数据表:values

3.1 结构

sql 复制代码
CREATE TABLE values (
    resource  INTEGER PRIMARY KEY,
    kind      INTEGER NOT NULL,      -- 资源类型
    bytes     BLOB    NOT NULL,      -- 数据字节
    version   INTEGER NOT NULL       -- 版本号(TimeId)
);

3.2 语义

text 复制代码
kind:资源类型(用于客户端反序列化)
bytes:数据(不透明,服务器不解释)
version:版本号(时间式机制,单调递增)

3.3 版本号的来源

text 复制代码
version 直接用 TimeId:
  - 由时间式同步机制天然承载
  - 每次写产生新 TimeId
  - 单调递增、全局唯一、带物理时间语义

不需要额外的 u64 版本字段。

四、客户端主导的读写与同步

4.1 客户端做什么

text 复制代码
客户端:
  - 决定何时读(拉取数据)
  - 决定何时写(编辑、上传)
  - 决定何时同步(上传本地变更、拉取更新)
  - 处理冲突(用 version 比对)

服务器:
  - 存数据(values)
  - 记权限(grants)
  - 响应请求(pull / push / transfer)
  - 不做决策

客户端是"驱动者",服务器是"服务者"。

4.2 写权限设备是数据源

text 复制代码
写权限设备 D 拥有 R:
  - D 是数据源
  - R 的数据在 D 本地产生
  - D 上传给服务器(备份 + 分发)
  - 平时不需要从服务器下传
  - 如果服务器下传给 D,反而可能覆盖更新的数据

4.3 下传的场景

text 复制代码
正常情况下传(写权限设备):
  - 无

异常下传(写权限设备):
  1. 设备移交后首次登录
     → 新 owner 需要 R 的当前数据
  2. 本地数据损坏 / 丢失
     → 从服务器恢复

正常下传(读权限设备):
  - 客户端请求时下发
  - 按需拉取

五、下发:pull(客户端请求触发)

5.1 流程

text 复制代码
设备 D 请求数据:
  1. 客户端发起请求(不是服务器主动)
  2. 查 grants WHERE pc_id = D
  3. 得到 D 有权限的资源
  4. 查 values 拿 bytes + version
  5. 返回

关键:客户端请求才下发,服务器不主动推。

5.2 SQL

sql 复制代码
-- D 有权限的资源(有记录 = 有读权限)
SELECT resource FROM grants WHERE pc_id = ?;

-- 拿数据
SELECT resource, kind, bytes, version FROM values
WHERE resource IN (SELECT resource FROM grants WHERE pc_id = ?);

5.3 增量拉取

text 复制代码
客户端请求:
  "给我 last_sync_version 之后的变化"
  带 last_sync_version(上次同步到的 TimeId)
  服务器返回:
    version > last_sync_version 的所有变更

按时间式版本增量拉取。


六、接收:push

6.1 流程

text 复制代码
设备 D 上传资源 R:
  1. 查 grants WHERE resource = R AND pc_id = D AND level = 'own'
  2. 存在 → 允许
  3. 不存在 → 拒绝
  4. 校验 base_version == values[R].version ?
     - 匹配 → 接受,version = 新 TimeId
     - 不匹配 → 拒绝,返回服务器最新
  5. 更新 values

6.2 SQL

sql 复制代码
-- 校验 D 是否是 R 的写者
SELECT 1 FROM grants
WHERE resource = ? AND pc_id = ? AND level = 'own';

-- 校验版本 + 更新
UPDATE values SET bytes = ?, version = ?
WHERE resource = ? AND version = ?;
-- 影响行数 0 → 版本冲突

写必须 level = 'own',且版本匹配。

6.3 冲突处理

text 复制代码
版本冲突时:
  客户端收到:
    - 服务器最新 version
    - 服务器最新 bytes

  客户端决定:
    - 合并(重新编辑,基于新版本)
    - 覆盖(放弃服务器,强制上传)
    - 放弃(放弃本地)

冲突处理在客户端。


七、移交

7.1 流程

text 复制代码
移交时需要服务器:
  1. A 请求移交 R 给 B
  2. 服务器查 grants:R 的 own 是不是 A?
  3. 是 → 更新 grants:R 的 own 改为 B
  4. 返回成功
  5. A 本地清除,B 本地获得

7.2 移交后的首次下传

text 复制代码
B 获得 R 的写权限后:
  - B 的设备上还没有 R 的数据
  - B 首次登录时,请求 R 的数据
  - 服务器下发 R 的最新 bytes + version
  - B 本地存储,开始作为新的数据源

移交后首次登录,下传一次。

7.3 对比

操作 权限来源 服务器参与 下传?
读(read 设备) 本地缓存 请求时 ✅ 请求触发
写(平时) 本地缓存 上传时 ❌ 不下传
写(移交后首次) 服务器 确认 + 下发 ✅ 一次性
写(本地损坏) 服务器 请求恢复 ✅ 请求触发
移交 服务器 ✅ ❌

八、时间式版本号

8.1 版本号的本质

text 复制代码
版本号 = TimeId(时间式同步机制)
  - 单调递增
  - 全局唯一
  - 带物理时间语义

每次写:
  values[R].version = 新 TimeId

不需要额外的 u64 版本字段。

8.2 用途

text 复制代码
1. 冲突检测:
   客户端 base_version vs 服务器 version

2. 增量拉取:
   version > last_sync_version

3. 审计:
   version 带时间,可追溯

8.3 统一

text 复制代码
一套 TimeId,覆盖:
  - 棒 ID
  - 版本号
  - 冲突检测
  - 增量同步

九、完整的服务器代码骨架

rust 复制代码
pub struct Server {
    grants: GrantsTable,
    values: ValuesTable,
}

impl Server {
    /// 拉取权限:客户端问"我能访问什么"
    fn pull_grants(&self, pc: PcId) -> Result<Vec<Grant>> {
        self.grants.find_by_pc(pc)
    }

    /// 拉取数据:客户端请求它有权访问的资源
    /// 客户端请求触发,服务器不主动推
    fn pull_values(
        &self,
        pc: PcId,
        resources: &[ResourceId],
        since: Option<TimeId>,
    ) -> Result<Vec<ServerResource>> {
        // 校验 pc 对这些资源有权限
        let allowed: Vec<_> = resources.iter()
            .filter(|r| self.grants.has_access(**r, pc).unwrap_or(false))
            .collect();

        // 按 since 过滤增量
        allowed.iter()
            .filter_map(|r| self.values.get(*r).ok())
            .filter(|v| since.map_or(true, |s| v.version > s))
            .collect()
    }

    /// 接收:pc 上传资源 R
    fn push(
        &self,
        pc: PcId,
        resource: ResourceId,
        base_version: TimeId,
        bytes: Vec<u8>,
    ) -> Result<TimeId> {
        // 1. 校验写权限
        let level = self.grants.level_of(resource, pc)?;
        if level != Level::Own {
            return Err(Error::NotOwner);
        }

        // 2. 校验版本
        let current = self.values.get(resource)?;
        if current.version != base_version {
            return Err(Error::VersionConflict {
                latest: current.version,
                latest_bytes: current.bytes,
            });
        }

        // 3. 生成新版本 + 存储
        let new_version = TimeId::next();
        self.values.update(resource, bytes, new_version)?;

        Ok(new_version)
    }

    /// 移交:把 R 的写权限从 A 转给 B
    fn transfer(&self, actor: PcId, resource: ResourceId, new_pc: PcId) -> Result<()> {
        let level = self.grants.level_of(resource, actor)?;
        if level != Level::Own {
            return Err(Error::NotOwner);
        }
        self.grants.update_owner(resource, new_pc)?;
        Ok(())
    }
}

四个方法:pull_grants / pull_values / push / transfer,全部响应式。


十、中间环节暂不考虑

10.1 暂不考虑

text 复制代码
暂不考虑:
  - 权限继承(子资源继承父资源)
  - 组授权(组可读 → 组成员可读)
  - 角色授权(角色可读 → 角色成员可读)
  - 动态权限(按条件计算)

10.2 grants 是"最终形态"

text 复制代码
不管权限怎么计算(继承、组、角色),
最终都落到 grants 表:
  (resource, pc_id, level)

服务器只认 grants。

10.3 先跑通

text 复制代码
第一步:
  - grants 表手动维护
  - pull_grants / pull_values / push / transfer
  - 时间式版本号
  - 跑通

第二步:
  - 加继承、组、角色
  - 自动生成 grants

十一、一句话

服务器是"权威账本 + 数据仓库 + 路由":维护 grants(权限)和 values(数据 + 版本),纯响应式。读写与同步由客户端主导;写权限设备是数据源,平时不下传;移交后首次登录、本地损坏时才下传。

版本号由时间式同步机制(TimeId)天然承载,一套机制覆盖棒 ID、版本、冲突检测、增量同步。grants 是权限的最终形态,中间环节暂不考虑。

相关推荐
先知后行。1 小时前
gdb、asan、Valgrind
linux·运维·服务器
yyuuuzz1 小时前
云服务器部署 web 项目的进程常驻记录
运维·服务器·前端·阿里云·云计算·aws
曹牧1 小时前
VS: Visual Studio 里把未包含的文件加进项目2
linux·运维·算法
微软技术分享2 小时前
NexShell 1.0|大模型驱动的智能 SSH 运维终端,永久免费,赋能 Linux 服务器管理
运维·服务器·ssh
噜~噜~噜~2 小时前
钢琴入门-基础乐理:第1讲到第3讲笔记
笔记·钢琴入门
坤坤子吖2 小时前
Linux 线程概念与控制——线程创建、退出与等待
linux·开发语言·c++·笔记·学习
qq_284274052 小时前
间歇运动机构与机械设计概论:棘轮与槽轮机构运动系数、零件失效与强度刚度准则、结构工艺性清根及标准件(含考点)
笔记·学习·平面·自动化·制造
Chenyu_3102 小时前
NUMA 基础:从 CPU、内存到 Node 的整体理解
linux·服务器·网络·缓存
于眠牧北3 小时前
雷电模拟器9/14搭配charles抓取自带浏览器接口9/14
经验分享·笔记·git