RLS 策略语法
约 1262 字大约 4 分钟
2025-08-11
概述
行级安全策略(Row Level Security, RLS)是 PostgreSQL 提供的数据库级别安全控制机制,允许对表中的特定行进行访问控制。
基本语法
启用 RLS
-- 为表启用行级安全策略
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
-- 禁用行级安全策略
ALTER TABLE table_name DISABLE ROW LEVEL SECURITY;创建策略
CREATE POLICY policy_name ON table_name
[ AS { PERMISSIVE | RESTRICTIVE } ]
[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
[ TO { role_name | PUBLIC | CURRENT_USER | CURRENT_ROLE } [, ...] ]
[ USING ( using_expression ) ]
[ WITH CHECK ( check_expression ) ];删除策略
DROP POLICY policy_name ON table_name;修改策略
ALTER POLICY policy_name ON table_name
[ TO { role_name | PUBLIC | CURRENT_USER | CURRENT_ROLE } [, ...] ]
[ USING ( using_expression ) ]
[ WITH CHECK ( check_expression ) ];策略类型
PERMISSIVE(宽松策略,默认)
多个 PERMISSIVE 策略使用 OR 逻辑组合,任一策略通过即允许访问。
-- 用户可以查看自己的记录
CREATE POLICY user_own_records ON users
FOR SELECT
USING (user_id = current_user_id());
-- 管理员可以查看所有记录
CREATE POLICY admin_all_records ON users
FOR SELECT
USING (is_admin(current_user_id()));
-- 结果: 用户能查看自己的记录 OR 管理员能查看所有记录RESTRICTIVE(限制策略)
所有 RESTRICTIVE 策略必须同时满足,使用 AND 逻辑组合。
-- 限制只能在工作时间访问
CREATE POLICY working_hours ON sensitive_data
AS RESTRICTIVE
FOR ALL
USING (EXTRACT(hour FROM now()) BETWEEN 9 AND 17);
-- 限制只能从安全网络访问
CREATE POLICY secure_network ON sensitive_data
AS RESTRICTIVE
FOR ALL
USING (inet_client_addr() << '192.168.1.0/24'::inet);
-- 结果: 必须在工作时间 AND 来自安全网络操作类型
SELECT 策略
控制查询权限:
-- 用户只能查看自己的订单
CREATE POLICY user_orders ON orders
FOR SELECT
USING (customer_id = auth.uid());INSERT 策略
控制插入权限:
-- 用户只能为自己创建订单
CREATE POLICY user_create_orders ON orders
FOR INSERT
WITH CHECK (customer_id = auth.uid());UPDATE 策略
控制更新权限:
-- 用户只能更新自己的订单
CREATE POLICY user_update_orders ON orders
FOR UPDATE
USING (customer_id = auth.uid())
WITH CHECK (customer_id = auth.uid());DELETE 策略
控制删除权限:
-- 用户只能删除自己的草稿订单
CREATE POLICY user_delete_drafts ON orders
FOR DELETE
USING (customer_id = auth.uid() AND status = 'draft');ALL 策略
应用于所有操作:
-- 应用于所有操作的策略
CREATE POLICY user_all_operations ON orders
FOR ALL
USING (customer_id = auth.uid())
WITH CHECK (customer_id = auth.uid());策略表达式
USING 表达式
定义行的可见性条件(适用于 SELECT、UPDATE、DELETE):
-- 基于用户 ID
USING (user_id = current_user_id())
-- 基于角色
USING (EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = current_user_id() AND role = 'admin'
))
-- 基于时间
USING (created_at > now() - interval '30 days')
-- 基于状态
USING (status = 'active' AND is_public = true)WITH CHECK 表达式
定义新行或修改行的验证条件(适用于 INSERT、UPDATE):
-- 确保插入的记录属于当前用户
WITH CHECK (owner_id = current_user_id())
-- 确保数据符合业务规则
WITH CHECK (price > 0 AND quantity > 0)
-- 确保状态转换合法
WITH CHECK (
CASE old_status
WHEN 'draft' THEN new_status IN ('submitted', 'cancelled')
WHEN 'submitted' THEN new_status IN ('approved', 'rejected')
ELSE false
END
)常用函数
认证函数
-- 当前用户 ID(Supabase)
auth.uid()
-- 当前用户角色
auth.role()
-- 当前数据库用户
current_user
-- 当前会话用户
session_user网络函数
-- 客户端 IP 地址
inet_client_addr()
-- 客户端端口
inet_client_port()
-- 服务器 IP 地址
inet_server_addr()时间函数
-- 当前时间
now()
-- 当前日期
current_date
-- 提取时间部分
EXTRACT(hour FROM now())
EXTRACT(dow FROM now()) -- 星期几 (0=Sunday)实际应用示例
多租户系统
-- 租户数据隔离
CREATE POLICY tenant_isolation ON documents
FOR ALL
USING (tenant_id = get_current_tenant_id())
WITH CHECK (tenant_id = get_current_tenant_id());基于角色的访问控制
-- 不同角色不同权限
CREATE POLICY role_based_access ON sensitive_data
FOR SELECT
USING (
CASE get_user_role()
WHEN 'admin' THEN true
WHEN 'manager' THEN department_id = get_user_department()
WHEN 'employee' THEN user_id = current_user_id()
ELSE false
END
);时间限制访问
-- 工作时间访问限制
CREATE POLICY business_hours ON payroll
FOR ALL
USING (
EXTRACT(dow FROM now()) BETWEEN 1 AND 5 AND -- 周一到周五
EXTRACT(hour FROM now()) BETWEEN 8 AND 18 -- 8点到18点
);数据分级保护
-- 基于数据敏感级别的访问控制
CREATE POLICY data_classification ON classified_docs
FOR SELECT
USING (
classification_level <= get_user_clearance_level()
);策略调试
查看策略信息
-- 查看表的所有策略
SELECT
schemaname,
tablename,
policyname,
cmd,
qual,
with_check
FROM pg_policies
WHERE tablename = 'your_table_name';测试策略效果
-- 设置测试用户上下文
SET LOCAL "request.jwt.claims" = '{"sub": "test-user-id"}';
-- 执行查询查看策略效果
SELECT * FROM protected_table;
-- 重置上下文
RESET ALL;策略性能分析
-- 分析策略对查询性能的影响
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM protected_table
WHERE some_condition;最佳实践
1. 索引优化
为 RLS 策略中使用的字段创建索引:
-- 为策略相关字段创建索引
CREATE INDEX idx_user_id ON table_name (user_id);
CREATE INDEX idx_tenant_id ON table_name (tenant_id);2. 函数优化
将复杂逻辑封装为函数:
-- 创建优化的策略函数
CREATE OR REPLACE FUNCTION can_access_record(record_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
-- 复杂的权限检查逻辑
RETURN true; -- 简化示例
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 在策略中使用函数
CREATE POLICY function_based ON records
FOR SELECT
USING (can_access_record(id));3. 策略组织
使用清晰的命名和组织策略:
-- 清晰的策略命名
CREATE POLICY "users_select_own_records" ON users FOR SELECT ...;
CREATE POLICY "users_update_own_profile" ON users FOR UPDATE ...;
CREATE POLICY "admins_manage_all_users" ON users FOR ALL ...;4. 安全考虑
- 避免在策略中暴露敏感信息
- 使用 SECURITY DEFINER 函数谨慎处理权限
- 定期审查和更新策略
- 测试策略的边界条件
版权所有
版权归属:Evoliant
许可证:MIT