视图本身不能实现真正的行列级权限控制,真正起作用的是GRANT、RLS和SQL SECURITY的组合;必须配合权限回收、策略启用与安全定义才能生效。
不能靠视图本身实现真正的行列级权限控制,它只是封装层;真正起作用的是 GRANT、RLS(行级安全策略)和 SQL SECURITY 设置的组合。单独建一个带 WHERE 和 CASE 的视图,不配权限回收或策略启用,等于没设防。
这是最常见的失效场景:你建了 user_safe_view 隐藏 salary,但忘了收回用户对 users 表的权限。
REVOKE SELECT ON mydb.users FROM 'app_user'@'%'
GRANT SELECT ON mydb.user_safe_view TO 'app_user'@'%'
SQL SECURITY DEFINER(不是默认的 DEFINER,否则执行时按调用者权限检查,容易报 ERROR 1449)CASE WHEN role = 'HR' THEN salary ELSE NULL END 脱敏,确保 role 字段来自可信赖上下文(如关联权限表),而非前端传参CURRENT_USER 为什么查不到自己的数据?因为视图定义是静态的,CURRENT_USER 在 CREATE VIEW 时就被求值为定义者(比如 postgres),不是查询时的登录用户。
ALTER TABLE payroll ENABLE ROW LEVEL SECURITY
CREATE POLICY emp_salary_policy ON payroll FOR SELECT USING (employee_id = current_setting('app.user_id')::int)
CREATE VIEW hr_payroll_vw AS SELECT id, name, department FROM payroll
SET app.user_id = '123',再查视图——策略自动生效USER_NAME() 做行过滤危险在哪?USER_NAME() 返回数据库用户名,不是登录名;在 EXECUTE AS 或跨库场景下会错乱,甚至返回 NULL。
ORIGINAL_LOGIN()(稳定返回实际登录名)或 SESSION_CONTEXT(N'user_id')(需应用层提前 sp_set_session_context 设置)CREATE SECURITY POLICY SalesFilter ADD FILTER PREDICATE Security.fn_securitypredicate(sales_rep_id) ON dbo.orders
sa 或 dbo 直连——权限行为会完全不同最易被忽略的一点:视图定义里的函数(如 MD5(email)、SUBSTRING(phone, 1, 3))会让对应字段失去索引能力。如果这个字段还要用于 WHERE 过滤,性能会断崖式下跌——脱敏和查询效率得分开设计,别挤在同一个视图里。