Supabase行级安全策略 #
一、RLS概述 #
1.1 什么是RLS #
text
Row Level Security (行级安全)
├── PostgreSQL内置功能
├── 行级别的访问控制
├── 基于策略过滤数据
├── 每个用户只能看到授权的数据
└── 安全性在数据库层实现
1.2 RLS工作原理 #
text
查询请求
│
▼
PostgreSQL
│
├── 检查RLS是否启用
│
├── 应用匹配的策略
│
├── 过滤数据
│
▼
返回结果
二、启用RLS #
2.1 启用表RLS #
sql
-- 启用RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 禁用RLS
ALTER TABLE posts DISABLE ROW LEVEL SECURITY;
-- 查看RLS状态
SELECT tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';
2.2 强制RLS #
sql
-- 强制表所有者也受RLS限制
ALTER TABLE posts FORCE ROW LEVEL SECURITY;
-- 取消强制
ALTER TABLE posts NO FORCE ROW LEVEL SECURITY;
三、创建策略 #
3.1 基础策略 #
sql
-- 允许所有人读取
CREATE POLICY "Public read access"
ON posts FOR SELECT
USING (true);
-- 允许认证用户插入
CREATE POLICY "Authenticated users can insert"
ON posts FOR INSERT
WITH CHECK (auth.role() = 'authenticated');
-- 用户只能更新自己的数据
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
USING (auth.uid() = author_id);
-- 用户只能删除自己的数据
CREATE POLICY "Users can delete own posts"
ON posts FOR DELETE
USING (auth.uid() = author_id);
3.2 策略类型 #
sql
-- SELECT: 读取策略
CREATE POLICY "..." ON table FOR SELECT USING (condition);
-- INSERT: 插入策略
CREATE POLICY "..." ON table FOR INSERT WITH CHECK (condition);
-- UPDATE: 更新策略
CREATE POLICY "..." ON table FOR UPDATE
USING (condition) -- 哪些行可以更新
WITH CHECK (condition); -- 更新后的值必须满足
-- DELETE: 删除策略
CREATE POLICY "..." ON table FOR DELETE USING (condition);
-- ALL: 所有操作
CREATE POLICY "..." ON table FOR ALL USING (condition);
3.3 组合策略 #
sql
-- 用户可以管理自己的文章
CREATE POLICY "Users can manage own posts"
ON posts FOR ALL
USING (auth.uid() = author_id)
WITH CHECK (auth.uid() = author_id);
四、常用策略模式 #
4.1 公开读取 #
sql
-- 所有人可读
CREATE POLICY "Public read"
ON products FOR SELECT
USING (true);
4.2 认证用户访问 #
sql
-- 认证用户可读
CREATE POLICY "Authenticated read"
ON posts FOR SELECT
USING (auth.role() = 'authenticated');
-- 匿名用户可读
CREATE POLICY "Anonymous read"
ON posts FOR SELECT
USING (auth.role() = 'anon');
4.3 用户数据隔离 #
sql
-- 用户只能访问自己的数据
CREATE POLICY "User isolation"
ON profiles FOR ALL
USING (auth.uid() = id);
4.4 基于角色控制 #
sql
-- 管理员完全访问
CREATE POLICY "Admin full access"
ON posts FOR ALL
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid() AND role = 'admin'
)
);
-- 普通用户只能访问自己的
CREATE POLICY "User own data"
ON posts FOR ALL
USING (
auth.uid() = author_id
OR EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid() AND role = 'admin'
)
);
4.5 基于状态控制 #
sql
-- 已发布的内容公开可见
CREATE POLICY "Published content visible"
ON posts FOR SELECT
USING (
published = true
OR auth.uid() = author_id
);
4.6 基于组织控制 #
sql
-- 用户只能访问自己组织的数据
CREATE POLICY "Organization isolation"
ON documents FOR ALL
USING (
organization_id IN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid()
)
);
五、辅助函数 #
5.1 内置函数 #
sql
-- 获取当前用户ID
auth.uid()
-- 获取当前用户角色
auth.role()
-- 获取JWT声明
auth.jwt()
-- 获取JWT中的特定字段
auth.jwt() ->> 'email'
5.2 自定义函数 #
sql
-- 检查用户是否是管理员
CREATE OR REPLACE FUNCTION is_admin()
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid() AND role = 'admin'
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 使用
CREATE POLICY "Admin access"
ON posts FOR ALL
USING (is_admin());
sql
-- 获取用户组织ID
CREATE OR REPLACE FUNCTION user_organization()
RETURNS UUID AS $$
BEGIN
RETURN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid()
LIMIT 1
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 使用
CREATE POLICY "Organization members"
ON documents FOR ALL
USING (organization_id = user_organization());
六、管理策略 #
6.1 查看策略 #
sql
-- 查看表的所有策略
SELECT
policyname,
cmd,
qual,
with_check
FROM pg_policies
WHERE tablename = 'posts';
6.2 删除策略 #
sql
-- 删除单个策略
DROP POLICY "Policy name" ON posts;
-- 删除所有策略
DROP POLICY "Public read" ON posts;
DROP POLICY "User isolation" ON posts;
6.3 修改策略 #
sql
-- 先删除再创建
DROP POLICY "Old policy" ON posts;
CREATE POLICY "New policy" ON posts FOR SELECT USING (true);
七、调试RLS #
7.1 测试策略 #
sql
-- 以特定用户身份测试
SET ROLE authenticated;
SET request.jwt.claims = '{"sub": "user-uuid"}';
SELECT * FROM posts;
-- 重置
RESET ROLE;
7.2 查看执行计划 #
sql
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM posts;
八、最佳实践 #
8.1 策略设计原则 #
text
RLS设计原则
├── 最小权限原则
├── 默认拒绝
├── 策略命名清晰
├── 避免过于复杂的策略
└── 定期审计策略
8.2 性能优化 #
sql
-- 为策略条件创建索引
CREATE INDEX idx_posts_author ON posts(author_id);
-- 为角色检查创建索引
CREATE INDEX idx_user_roles_user ON user_roles(user_id);
8.3 常见陷阱 #
sql
-- 错误:忘记启用RLS
-- 表创建后默认没有启用RLS
-- 错误:策略过于宽松
CREATE POLICY "Too permissive"
ON posts FOR ALL
USING (true); -- 所有操作都允许
-- 错误:策略冲突
-- 多个策略是OR关系,可能导致意外访问
九、完整示例 #
9.1 博客系统 #
sql
-- 启用RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
-- 文章策略
-- 已发布文章所有人可见
CREATE POLICY "Published posts visible"
ON posts FOR SELECT
USING (published = true OR auth.uid() = author_id);
-- 认证用户可创建文章
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = author_id);
-- 作者可更新自己的文章
CREATE POLICY "Authors can update own posts"
ON posts FOR UPDATE
USING (auth.uid() = author_id)
WITH CHECK (auth.uid() = author_id);
-- 作者可删除自己的文章
CREATE POLICY "Authors can delete own posts"
ON posts FOR DELETE
USING (auth.uid() = author_id);
-- 评论策略
-- 文章评论所有人可见
CREATE POLICY "Comments visible"
ON comments FOR SELECT
USING (true);
-- 认证用户可评论
CREATE POLICY "Users can comment"
ON comments FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- 用户可删除自己的评论
CREATE POLICY "Users can delete own comments"
ON comments FOR DELETE
USING (auth.uid() = user_id);
十、总结 #
RLS要点:
| 操作 | SQL |
|---|---|
| 启用 | ALTER TABLE … ENABLE ROW LEVEL SECURITY |
| 创建策略 | CREATE POLICY … |
| 删除策略 | DROP POLICY … |
| 查看策略 | SELECT * FROM pg_policies |
下一步,让我们学习数据库函数!
最后更新:2026-03-28