You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Postgres/Supabase含关注计数器系统的安全策略设计咨询

Postgres/Supabase关注计数器场景的最优安全方案

核心结论:不要拆分计数器到独立表,也不要直接配置列级写权限,采用「Security Definer封装事务函数+收敛表直接写权限」的方案是兼顾安全、性能、维护成本的最优解。


方案选型说明

为什么不选拆表方案

  • 计数器本身就是用户维度的强关联属性,拆表属于无业务价值的过度设计,后续所有用户信息查询都要额外做一次表关联,平白增加查询开销
  • 拆表后依然要解决计数器的修改权限问题,并没有从根源降低安全设计的复杂度,反而多了一份表间一致性维护成本

为什么不直接开放列级安全策略

列级RLS仅能约束「哪些角色可以修改对应列」,无法限制修改逻辑:一旦给普通用户开放follower_counter列的直接写权限,你没法通过策略限制用户只能对计数器做+1/-1的合法操作,恶意用户可以直接把任意用户的粉丝数改成任意数值,数据完全不可信。


具体实现步骤

1. 封装原子操作的安全函数

把插入关注关系、更新计数器的逻辑全部封装到单个带内置校验的函数中,函数配置为SECURITY DEFINER——执行时使用函数创建者(表所有者)的权限运行,不需要给调用用户开放两张表的直接写权限,从根源避免越权。
函数不要信任前端传入的关注者ID,直接从Supabase内置的认证上下文取当前登录用户ID作为操作主体,完全杜绝身份伪造。
参考实现代码:

-- 关注用户函数
CREATE OR REPLACE FUNCTION follow_user(target_following_id uuid)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
  current_user_id uuid := auth.uid();
BEGIN
  -- 基础校验:未登录拦截
  IF current_user_id IS NULL THEN
    RAISE EXCEPTION '请先登录后再操作';
  END IF;
  -- 校验:禁止关注自己
  IF current_user_id = target_following_id THEN
    RAISE EXCEPTION '无法关注自己';
  END IF;
  -- 校验:禁止重复关注
  IF EXISTS (
    SELECT 1 FROM following_relationship
    WHERE follower = current_user_id AND following = target_following_id
  ) THEN
    RAISE EXCEPTION '你已关注该用户';
  END IF;
  -- 同事务执行两个操作,原子性保证要么全成功要么全回滚
  INSERT INTO following_relationship(follower, following)
  VALUES (current_user_id, target_following_id);

  UPDATE users
  SET follower_counter = follower_counter + 1
  WHERE uuid = target_following_id;
END;
$$;

-- 给登录用户开放函数执行权限
GRANT EXECUTE ON FUNCTION follow_user(uuid) TO authenticated;

取关逻辑按照同样的模式封装即可,内部校验当前用户是对应关注关系的发起者,删除关系后给对应粉丝计数器-1。

2. 表级RLS配置

  • 给users表开启RLS:配置策略仅允许用户修改自身行的name等个人字段,收回authenticated角色对follower_counter列的直接修改权限,该字段的所有变更只能通过上面封装的关注/取关函数执行
  • 给following_relationship表开启RLS:允许用户查询公开的关注关系、删除自己发起的关注记录,收回authenticated角色对该表的直接插入权限,所有关注关系的创建只能走封装函数
  • 不需要额外给事务传递权限类参数,所有身份校验在函数内部通过auth.uid()完成,仅需要前端传入被关注用户的ID一个业务参数即可。

3. 函数权限注意点

  • 创建SECURITY DEFINER函数时要使用表所有者账号(通常是postgres角色)创建,不要用普通用户账号
  • 函数内部一定要做全链路的参数合法性校验,不要省略重复关注、自我关注这类拦截逻辑,避免产生脏数据
  • 不要给函数设置过高的权限,函数内仅保留操作对应两张表的必要权限即可

内容的提问来源于stack exchange,提问作者tobias hassebrock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:36:38