存儲過程執行時權限檢查機制直接決定「能不能查別人的表、能不能用 Role 權限」。本文通過實測案例,徹底講透定義者權限、調用者權限的編譯期/運行期差異、Role 在 PL/SQL 中失效的根因、動態 SQL 推遲綁定繞過靜態權限檢查的原理與最佳實踐。
| 維度 | 定義者權限 | 調用者權限 |
|---|---|---|
| 聲明語法 | 默認(無 AUTHID) | CREATE PROCEDURE ... AUTHID CURRENT_USER |
| 編譯期權限檢查 | 檢查**定義者**的直接授權(不含 Role) | **不檢查**對象權限(僅語法檢查) |
| 運行期權限檢查 | 仍用定義者權限 | 檢查**調用者**的直接授權 + **啟用的 Role** |
| Role 生效 | **永不生效**(編譯/運行均忽略) | **運行期生效**(調用者已啟用的 Role) |
| 適用場景 | 核心業務邏輯、封裝敏感表、統一權限入口 | 工具類過程、動態構建 SQL、多租戶共享代碼 |
-- SYS 創建用戶並授權
GRANT CONNECT, RESOURCE TO u1 IDENTIFIED BY u1;
GRANT DBA TO u2 IDENTIFIED BY u2; -- u2 有 DBA Role
-- U1 創建表
CONN u1/u1
CREATE TABLE t AS SELECT * FROM user_objects;
-- U2 嘗試創建過程刪除 U1 表
CONN u2/u2
CREATE OR REPLACE PROCEDURE p_test AS
BEGIN
DELETE FROM u1.t; -- 靜態 SQL,編譯期檢查 U2 對 u1.t 的直接授權
COMMIT;
END;
/
-- 警告: Procedure created with compilation errors.
-- ORA-00942: table or view does not exist
-- 原因:U2 雖有 DBA Role,但定義者權限過程編譯期**不認 Role**,且無直接授權。
-- 解決:U1 直接授權
CONN u1/u1
GRANT ALL ON t TO u2;
-- U2 重新創建過程 → 成功
CONN u2/u2
CREATE OR REPLACE PROCEDURE p_test AS
BEGIN DELETE FROM u1.t; COMMIT; END;
/
-- Procedure created.
-- 回收直接授權
CONN u1/u1
REVOKE ALL ON t FROM u2;
-- U2 創建調用者權限過程(靜態 SQL)
CONN u2/u2
CREATE OR REPLACE PROCEDURE p_test AUTHID CURRENT_USER AS
BEGIN
DELETE FROM u1.t; -- 編譯期不檢查,但運行期調用者 U2 仍無直接授權
COMMIT;
END;
/
-- 警告: ORA-00942 at line 5 (運行期報錯)
-- 關鍵:改用動態 SQL,將權限檢查推遲到 EXECUTE IMMEDIATE 瞬間
CREATE OR REPLACE PROCEDURE p_test AUTHID CURRENT_USER AS
BEGIN
EXECUTE IMMEDIATE 'DELETE FROM u1.t'; -- 運行期動態解析,調用者 U2 的 DBA Role 生效
COMMIT;
END;
/
-- Procedure created.
EXEC p_test; -- 成功執行
SELECT COUNT(*) FROM u1.t; -- 0 行,數據已刪除
SET ROLE、密碼 Role、安全應用 Role),編譯期不可知。GRANT obj_priv ON obj TO user)。AUTHID CURRENT_USER 聲明運行期以調用者身份執行。EXECUTE IMMEDIATE / DBMS_SQL 在**運行期**動態解析、綁定、執行,此時調用者的 Session 已啟用 Role,權限檢查自然包含 Role。DBMS_SQL + AUTHID DEFINER 但以 SYS 身份運行(極度危險,禁用)。SELECT_CATALOG_ROLE 查字典)。DBMS_ASSERT.ENQUOTE_LITERAL、ENQUOTE_NAME、綁定變量(USING 子句),**嚴禁字符串拼接用戶輸入**。DBA_TAB_PRIVS、DBA_ROLE_PRIVS,清理過期/過度授權。BEQUEATH CURRENT_USER 視圖特性,類似調用者權限,可用於視圖層權限傳遞。IF 過程訪問固定表、需封裝邏輯、統一權限控制
→ 定義者權限 + 直接授權 (GRANT ON table TO proc_owner)
ELSE IF 過程需動態構建表名/列名/WHERE、跨 Schema 通用
→ 調用者權限 + 動態 SQL (EXECUTE IMMEDIATE USING ...)
ELSE IF 過程需調用者 Role 權限(如查字典、監控視圖)
→ 調用者權限 + 動態 SQL
ELSE
→ 定義者權限(默認安全選項)
徹底理解「編譯期靜態綁定 vs 運行期動態綁定、Role 只在運行期 Session 生效」這兩大鐵律,即可在複雜權限矩陣中游刃有餘地設計安全、可維護的存儲過程體系。