撤销Postgres的SELECT权限后RLS失效,请求排查问题
问题原因及解决方法
核心问题1:RLS策略的USING与WITH CHECK未区分
你创建的workspace_only策略用了for all,但仅提供了using子句——这个子句仅控制读取/修改时的行可见性(比如SELECT能看到哪些行、DELETE/UPDATE能操作哪些行),但对于INSERT操作,必须添加with check子句验证新插入的行是否符合规则,否则INSERT会被RLS拦截。
核心问题2:权限撤销与RLS策略冲突
你执行了REVOKE SELECT ON services_connected FROM anon;,同时策略包含for all(覆盖SELECT),导致:
- SELECT操作直接被权限拒绝(即使RLS允许也无效)
- 其他操作(INSERT/UPDATE/DELETE)虽可能保留权限,但RLS的
using子句需要调用者能看到对应行才能操作,而anon无SELECT权限,无法验证行可见性,最终操作被拒。
核心问题3:函数执行上下文权限不足
你的disconnect_service函数默认是SECURITY INVOKER(以调用者anon的权限执行),但anon缺少services_connected和users_list表的DELETE权限,同时RLS策略未明确允许anon执行DELETE操作。
修复步骤
1. 调整基础权限
先确保anon拥有非SELECT操作的权限:
GRANT INSERT, UPDATE, DELETE ON services_connected TO anon; GRANT INSERT, UPDATE, DELETE ON users_list TO anon;
2. 拆分RLS策略,精准控制操作
删除原有的for all策略,分别为不同操作创建针对性策略:
-- 删除原有策略 DROP POLICY "workspace_only" ON public.services_connected; -- 允许anon操作自己工作区的行(UPDATE/DELETE) CREATE POLICY "allow_workspace_delete_update" ON public.services_connected FOR UPDATE, DELETE TO anon USING ((workspace_id = ((current_setting('request.jwt.claims'::text, true))::json ->> 'workspace_id'::text)::bigint)); -- 允许anon插入自己工作区的行(INSERT) CREATE POLICY "allow_workspace_insert" ON public.services_connected FOR INSERT TO anon WITH CHECK ((workspace_id = ((current_setting('request.jwt.claims'::text, true))::json ->> 'workspace_id'::text)::bigint));
3. 优化函数权限上下文(可选更安全方案)
如果不想给anon直接操作表的权限,可将函数设为SECURITY DEFINER(以函数拥有者权限执行),仅开放函数调用权限:
DROP FUNCTION IF EXISTS public.disconnect_service; CREATE OR REPLACE FUNCTION public.disconnect_service(service_name_value TEXT) RETURNS VOID AS $$ DECLARE workspace_id_value BIGINT := ((current_setting('request.jwt.claims'::text, TRUE))::JSON ->> 'workspace_id')::BIGINT; BEGIN DELETE from services_connected where workspace_id = workspace_id_value and service_name = service_name_value; DELETE FROM users_list WHERE workspace_id = workspace_id_value and service_name = service_name_value; END; $$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public; -- 仅允许anon调用该函数 GRANT EXECUTE ON FUNCTION public.disconnect_service(TEXT) TO anon;
内容的提问来源于stack exchange,提问作者Alwaysblue
相关产品推荐
相关产品推荐

