當 DEPARTMENT_ID = 60 時遮罩 SALARY、COMMISSION_PCT 欄位。
CREATE OR REPLACE VIEW masked_salary_view AS
SELECT first_name, last_name,
CASE department_id WHEN 60 THEN NULL ELSE salary END AS salary,
CASE department_id WHEN 60 THEN NULL ELSE commission_pct END AS commission_pct,
department_id
FROM employees;
透過 GRANT SELECT ON masked_salary_view TO hr_user 控制存取。
-- 1. 建立 Context 保存使用者部門
CREATE CONTEXT hr_context USING hr_pkg;
-- 2. 策略函數
CREATE OR REPLACE FUNCTION rls_dept (
obj_owner IN VARCHAR2,
obj_name IN VARCHAR2
) RETURN VARCHAR2 IS
v_deptno NUMBER;
v_pred VARCHAR2(200);
BEGIN
v_deptno := SYS_CONTEXT('HR_CONTEXT', 'DEPT');
IF v_deptno IS NULL THEN
v_pred := '1=2'; -- 全部遮罩
ELSE
v_pred := 'department_id = ' || v_deptno;
END IF;
RETURN v_pred;
END;
/
-- 3. 掛載策略
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'DEPT_MASK_POLICY',
function_schema => 'HR',
policy_function => 'RLS_DEPT',
statement_types => 'SELECT',
sec_relevant_cols => 'SALARY,COMMISSION_PCT', -- 僅遮罩特定欄位
sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS -- 10g: 顯示所有列但敏感欄位為 NULL
);
END;
/
| 維度 | 關聯視圖 | VPD |
|---|---|---|
| 實作複雜度 | 低 | 中高 |
| 動態條件 | 靜態,需建多個視圖 | 動態,單一策略函數支援任意邏輯 |
| 共用池效率 | 每視圖一份游標 | 單一游標,SYS_CONTEXT 為繫結變數 |
| 應用程式修改 | 需改查詢視圖名 | 零修改,透明攔截所有存取 |
| 多應用共存 | 難以統一 | 原生支援,所有連線自動受保護 |
| 欄位級遮罩 | 需在視圖列出所有欄位 | SEC_RELEVANT_COLS 精確指定 |
VPD 修飾的是 基礎表,無論透過何種應用、報表工具、Ad-hoc 查詢存取 EMPLOYEES,策略自動生效。視圖僅保護透過該視圖的存取,繞過視圖即可看到原始資料。
SYS_CONTEXT 而非查詢表格取得條件值。DBMS_RLS.ADD_POLICY(..., policy_type => DBMS_RLS.CONTEXT_SENSITIVE) 快取策略結果。