在SQL Server數據庫登陸賬戶、數據庫用戶權限管理中,經常會授予權限、回收權限,有時還會拒絕權限。GRANT、REVOKE是我們常用的,但有時會遇到使用DENY的情形。從英文單詞的字面意思來看,GRANT是賦予權限,REVOKE是收回我們已經授予的權限;而DENY是禁掉某個權限。這三者內部到底有什麼聯繫、區別?什麼情景下使用GRANT?什麼情景下使用REVOKE?什麼情景下使用DENY?單獨從文字描述上,我們很難理解。接下來,本文將通過試驗的方式,逐步揭示兩者的祕密。
本打算將結論放在各試驗後面揭示,寫試驗案例時發現寫的又臭又長,最終還是決定將結論提前,讓我們能找到讀閱讀的主線:
-
GRANT賦予主體各類權限。
-
用戶成爲角色的成員可以繼承角色的權限。
-
REVOKE、DENY同時可以收回GRANT賦予的權限,但兩者影響不同,REVOKE僅僅收回了權限,DENY撤銷GRANT賦予的權限後並拒絕該權限,具體影響見下面的6、8結論。
-
REVOKE可以撤銷DENY拒絕的權限。
-
賦予(GRANT)數據庫SELECT權限,則獲得數據庫下所有子對象(如數據庫的表、視圖及所有列,包括新建的表和列)的SELECT權限,某些單獨拒絕(DENY)SELECT的子對象除外。
-
拒絕(DENY)數據庫的SELECT權限,則拒絕父對象下所有子對象(如數據庫的表、表的列,包括新建的表和列)的SELECT權限,即使單獨賦予子對象的SELECT權限。
-
賦予表SELECT權限,則對錶的所有列(包括新建列)有SELECT權限,單獨拒絕SELECT的列除外。
-
拒絕表的SELECT權限,則拒絕表的所有列(包括新建列)SELECT權限,單獨賦予SELECT的列除外。
值得注意的是,在EXEC AS user上下文中,無論是賦予表SELECT權限,還是拒絕表SELECT權限,對新增列都無SELECT權限(這是在SQL Server 2008(SP2)版本試驗中發現)其他版本表現的有所不同,所以如果有使用這樣情景,需要重點測試,以免出現不必要的影響。
應用場景:根據最小化用戶權限原則,對重要信息表,我們可以拒絕所有用戶的SELECT權限,而僅僅賦予特定用戶查看特定列的SELECT權限,最大化避免信息泄漏。
到此本文的核心結論您已經瞭然,當然文字描述相對較爲抽象,如果您有時間,可以看完此文。當然我建議您耐心看完後文,並親自動手實踐,一則可以加深我們對三者區別的理解;二則對於新的內容,我們可以使用試驗方式去學習,這是一個很好的學習方式,避免人云亦云,做到真正的瞭然於胸。最後,本文僅僅針對表的SELECT權限進行說明,有興趣的同學可以參照本文的測試過程,設計INSERT、DELETE、UPDATE、EXEC等權限的樣例,進行試驗學習。
測試的環境
數據庫的版本爲 SQL Server 2008(SP2)企業版:
SELECT @@VERSION
Microsoft SQL Server 2008 (SP2) - 10.0.4000.0 (X64) Sep 16 2010 19:43:16 Copyright (c) 1988-2008 Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
測試準備:創建一個登陸名Jack,在test數據庫下創建用戶Jack,關聯到登陸名Jack;然後在test 數據庫下創建表test、test1,並在Jack用戶的上下文下執行查詢,腳本如下
USE master
GO
IF EXISTS(SELECT name FROM sys.database_principals WHERE name='Jack')
DROP LOGIN Jack
CREATE LOGIN Jack WITH PASSWORD='Password'
,CHECK_POLICY=OFF,CHECK_EXPIRATION=OFF
USE test
GO
IF EXISTS(SELECT name FROM sys.sysusers WHERE name='Jack')
DROP USER Jack
CREATE USER Jack FOR LOGIN Jack
EXEC sys.sp_helprotect NULL,Jack
USE test
CREATE TABLE [dbo].[test](
[a] [int] IDENTITY(1,1) NOT NULL,
[b] [varchar](30) NULL,
[d] [datetime] NULL
)
CREATE TABLE [dbo].[test1](
[e] [int] IDENTITY(1,1) NOT NULL,
[f] [varchar](30) NULL,
[g] [datetime] NULL
)
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
消息229,級別14,狀態5,第1 行
拒絕了對對象'test' (數據庫'test',架構'dbo')的SELECT 權限。
因爲test數據庫下新創建的Jack用戶,沒有查詢權限,所以報瞭如上錯誤。
GRANT、REVOKE和DENY三者之間的關係
GRANT賦予用戶各種權利,REVOKE、DENY收回GRANT權限。
賦予Jack用戶SELECT權限,並使用sp_helprotect查看Jack的權限,腳本如下:
--賦予Jack用戶SELECT權限
GRANT SELECT TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
從結果可以看到,數據庫用戶Jack擁有SELECT權限(剛剛賦予的),同時還有CONNECT連接權限(創建數據庫用戶時默認賦予的)。
此時在Jack 上下文中執行對test表的查詢是OK的:
試驗1:使用REVOKE收回SELECT權限,再次查看Jack擁有的權限
--試驗1:
--收回Jack SELECT 權限
REVOKE SELECT FROM Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
從結果可以看到Jack的SELECT權限沒有了。
試驗2:重新賦予Jack 用戶SELECT權限,使用DENY 拒絕掉其SELECT權限,並查看Jack的權限:
--試驗2:
--重新賦予Jack用戶SELECT 權限
GRANT SELECT TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
--拒絕Jack 的SELECT 權限
DENY SELECT TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
比較REVOKE和DENY 的結果,可以看出REVOKE後,Jack擁有的SELECT權限直接沒有了;而DENY的結果是將GRANT 改爲了DENY。此時我們再次在Jack用戶的上下文中執行test表的查詢,結果如下:
這個錯誤和上面沒有SELECT權限相同。同樣說明了REVOKE和DENY均有收回GRANT賦予的權限的功能。那麼是不是說明DENY是多餘的呢?我們繼續下面的試驗:
--使用REVOKE 收回DENY 拒絕的權限
REVOKE SELECT FROM Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
從結果可以看到,REVOKE不僅可以收回GRANT賦予的權限,同樣可以收回 DENY 拒絕的權限;同時DENY可以收回GRANT賦予的權限。這雖然反應了REVOKE和DENY的一點不同,但還不能體現其真正內在的區別。下面我繼續設計稍顯複雜的試驗,繼續深入。
新增加列對權限的繼承
GRANT SELECT TO Jack
EXEC sp_helprotect NULL,Jack
--增加列c
ALTER TABLE test ADD c int
EXEC AS USER='Jack'
GO
SELECT c FROM test
REVERT
GO
--刪除增加的列
ALTER TABLE test DROP column c
DENY SELECT TO Jack
EXEC sp_helprotect NULL,Jack
ALTER TABLE test ADD c int
EXEC AS USER='Jack'
GO
SELECT c FROM test
REVERT
GO
結論:賦予/拒絕數據庫的SELECT的權限,則會賦予/拒絕新表或者表的新列的SELECT權限。
--刪除增加的列
ALTER TABLE test DROP column c
GRANT SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL,Jack
--增加列c
ALTER TABLE test ADD c int
EXEC AS USER='Jack'
GO
SELECT c FROM test
REVERT
GO
--刪除增加的列
ALTER TABLE test DROP column c
DENY SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL,Jack
ALTER TABLE test ADD c int
EXEC AS USER='Jack'
GO
SELECT c FROM test
REVERT
GO
結論:賦予/拒絕表的SELECT的權限,則會賦予/拒絕表的新列的SELECT權限。
賦予/拒絕數據庫或表SELECT權限
試驗3:賦予Jack用戶整個數據庫的SELECT權限,拒絕Jack用戶對錶test 的SELECT權限,查看Jack的權限,並在用戶Jack上下文中查詢表test。
--試驗3:
GRANT SELECT TO Jack
DENY SELECT ON OBJECT::test TO Jack
EXEC sys.sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
結論:賦予用戶(Jack)父級對象(本試驗指數據庫)SELECT權限,則用戶具有除單獨拒絕的子對象(這裏拒絕了test表的SELECT權限),對其餘子對象均有SELECT權限。
--恢復Jack權限
REVOKE SELECT TO Jack
REVOKE SELECT ON OBJECT::test TO Jack
EXEC sys.sp_helprotect NULL ,Jack
試驗4:反之拒絕了Jack對數據庫的SELECT權限,賦予Jack 對數據庫下表test 的SELECT權限。
--試驗4:
DENY SELECT TO Jack
GRANT SELECT ON OBJECT::test TO Jack
EXEC sys.sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
從試驗3、4獲得這樣的一個結論:拒絕用戶數據庫SELECT權限,則會拒絕用戶對數據庫所有對象的SELECT權限,即使再單獨賦予該對象SELECT權限。而賦予用戶數據庫SELECT權限,僅僅拒絕用戶對數據庫中的某些對象的SELECT權限,則用戶對除拒絕對象外的其他對象仍然有SELECT權限。
恢復Jack的權限
--恢復Jack權限
REVOKE SELECT TO Jack
REVOKE SELECT ON OBJECT::test TO Jack
賦予/拒絕數據庫或表列的SELECT權限
試驗5:賦予Jack用戶SELECT權限,拒絕Jack用戶對錶test的字段a、d的SELECT權限,然後在Jack用戶的上下文中,分別查詢test全表、test的字段b,試驗腳本如下。
--試驗5:
--賦予Jack用戶SELECT權限
GRANT SELECT TO Jack
--禁止用戶Jack 查看錶test字段a、d
DENY SELECT ON OBJECT::dbo.test(a,d) TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT b FROM test
REVERT
GO
顯然,對test的全表查詢報錯,在消息下可以看到的錯誤如下:
這樣即使通過GRANT給予了用戶Jack數據庫的查詢權限,但拒絕了Jack用戶對錶test 的字段a、d的SELECT權限,Jack 用戶只能查詢test表的b字段,而不能查詢test表的a、d字段。
恢復Jack權限:
REVOKE SELECT FROM Jack
REVOKE SELECT ON OBJECT::test(a,d) FROM Jack
試驗6:反過來,拒絕Jack的SELECT權限,賦予Jack用戶對錶test 的字段a、d的SELECT權限
--試驗6:
--賦予Jack用戶SELECT權限
DENY SELECT TO Jack
--禁止用戶Jack 查看錶test字段a、d
GRANT SELECT ON OBJECT::dbo.test(a,d) TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT a,d FROM test
REVERT
這樣即使Jack用戶對錶test的字段a、d有SELECT權限,但是由於對Jack拒絕了SELECT權限,所以無論是對test表的a、d字段,還是對test表都沒有了SELECT權限。即拒絕父對象(這裏只數據庫)的SELECT權限,則會拒絕父對象的所有子對象的SELECT權限,即使另外賦予該子對象(這裏只表test的字段a、d列)SELECT權限。
恢復Jack權限:
REVOKE SELECT FROM Jack
REVOKE SELECT ON OBJECT::test(a,d) FROM Jack
賦予/拒絕表或表列的SELECT權限
試驗7:拒絕用戶Jack對錶test 的SELECT 權限,賦予用戶Jack對錶test字段a、d的SELECT權限:
--試驗7:
--賦予Jack用戶SELECT權限
DENY SELECT ON OBJECT::dbo.test TO Jack
--禁止用戶Jack 查看錶test字段a、d
GRANT SELECT ON OBJECT::dbo.test(a,d) TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT a,d FROM test
REVERT
GO
拒絕父對象(這裏指表test)的SELECT權限,則拒絕除另外賦予了SELECT的子對象(這裏指test表的列a、d)之外的所有子對象,包括對象新增的子對象(新增字段),下面的試驗8剛好說明了這一點。
試驗8:在試驗7的基礎上爲表test增加一列c,然後在Jack上下文中查看c列。
--試驗8:
ALTER TABLE test ADD c int
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT c FROM test
REVERT
GO
用戶Jack使用DENY拒絕表的SELECT權限,表新增的字段的SELECT權限對用戶Jack也是拒絕的。
從試驗7、8這兩個試驗我們可以得到DENY的一個應用場景,對於特定的信息(字段)如涉及用戶隱私的信息(姓名、身份證號、手機號、銀行卡號、密碼、虛擬幣餘額等等),一般用戶可以賦予表的查詢權限,而拒絕其查詢、更新特定隱私信息的權限,從而使用戶權限最小化。
恢復用戶Jack 權限,並恢復test表結構:
REVOKE SELECT ON OBJECT::test FROM Jack
REVOKE SELECT ON OBJECT::test(a,d) FROM Jack
--刪除增加的列
ALTER TABLE test DROP column c
試驗9:賦予用戶Jack對錶test的SELECT權限,禁止用戶Jack查看錶test的字段a、d列:
--試驗9:
--賦予Jack用戶SELECT權限
GRANT SELECT ON OBJECT::dbo.test TO Jack
--禁止用戶Jack 查看錶test字段a、d
DENY SELECT ON OBJECT::dbo.test(a,d) TO Jack
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT b FROM test
REVERT
GO
賦予表test的SELECT權限,拒絕對錶的字段a、d的SELECT權限,用戶對錶test的其他字段(如試驗中的b列)仍有SELECT權限。
試驗10:在試驗9的基礎上,爲表test新增字段c,檢驗其是否具有SELECT權限
--試驗10:
--表test 增加字段c
ALTER TABLE test ADD c int
--查看Jack用戶擁有的權限
EXEC sys.sp_helprotect NULL,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
SELECT c FROM test
REVERT
GO
由於使用EXEC AS user='Jack' 上下文的問題,使用GRANT 爲用戶Jack賦予表test 的SELECT權限,用戶Jack獲得了新增列c的SELECT權限。但此時用戶並沒有列c的SELECT權限(試驗環境SQL Server 2008(SP2) 這應該是SQL Server的一個Bug。SQL Server 2016(RTM-GDR)版本只要對列DENY了SELECT權限,對所有表都沒有了SELECT權限;SQL Server 2016(SP1)又恢復了SQL Server 2008(SP2)情景 )。當我使用Jack用戶直接登錄服務器,連接數據庫test,查詢新列c是有SELECT權限的。對於使用 EXEC AS user 的情景,此時需要對新列單獨賦予SELECT權限:
GRANT SELECT ON OBJECT::test(c) TO Jack
試驗7、8、9、10表明,根據最小化權限原則,如果想要用戶查看錶儘可能少的信息,我們可以拒絕表的SELECT權限,再賦予用戶特定字段的SELECT權限。以避免新增加的字段被不必要的用戶看到,造成信息泄漏。
所以要慎用DENY,原因爲SQL Server 中使用EXEC AS user上下文的情景和直接登錄時,對新列的權限界定不一致。
恢復用戶Jack 權限,並恢復test表結構:
REVOKE SELECT ON OBJECT::test FROM Jack
REVOKE SELECT ON OBJECT::test(a,d) FROM Jack
--刪除增加的列
ALTER TABLE test DROP column c
數據庫角色成員繼承角色權限的試驗
用戶除了可以直接對其賦予、拒絕權限外,還可以通過成爲角色的成員,繼承其權限,接下來我將試驗用戶直接獲得的權限和從角色中繼承的權限或者不同角色權限之間的影響。
首先在數據庫test中創建兩個角色,GrantRole和DenyRole,並查看其具有的權限:
--創建數據庫角色
USE test
GO
CREATE ROLE GrantRole
CREATE ROLE DenyRole
--查看數據庫角色權限
EXEC sys.sp_helprotect NULL ,GrantRole
EXEC sys.sp_helprotect NULL ,DenyRole
可以看到,數據庫角色剛創建的時候是沒有任何權限的,所以報沒有要報告的匹配行錯誤。
分別對角色/用戶賦予或拒絕相同對象的權限測試
試驗11:賦予角色對數據庫的SELECT權限,將Jack加入爲其成員,拒絕Jack對數據庫的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗11:
GRANT SELECT TO GrantRole
EXEC sys.sp_addrolemember GrantRole,Jack
DENY SELECT TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
試驗12:拒絕角色GrantRole對數據庫的SELECT權限,賦予用戶Jack對數據庫的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗12:
DENY SELECT TO GrantRole
GRANT SELECT TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論:用戶權限和用戶從數據庫角色中繼承的權限互斥時(GRANT<<=>>DENY),用戶最終獲得的是拒絕權限
恢復角色RrantRole和用戶Jack的權限:
REVOKE SELECT FROM GrantRole
REVOKE SELECT FROM Jack
試驗13:賦予角色GrantRole對數據庫的SELECT權限,拒絕用戶Jack對數據庫的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗13:
GRANT SELECT ON OBJECT::test TO GrantRole
DENY SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論同試驗12.
試驗14:拒絕角色GrantRole對數據庫對象表test的SELECT權限,賦予用戶Jack對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗14:
DENY SELECT ON OBJECT::test TO GrantRole
GRANT SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論同試驗12.
恢復角色GrantRole、用戶Jack的權限
REVOKE SELECT ON OBJECT::test TO GrantRole
REVOKE SELECT ON OBJECT::test TO Jack
試驗15:拒絕角色GrantRole對數據庫對象表test的a、d列SELECT權限,賦予用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗15:
DENY SELECT ON OBJECT::test(a,d) TO GrantRole
GRANT SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論同試驗12.
試驗16:賦予角色GrantRole對數據庫對象表test的a、d列SELECT權限,拒絕用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗16:
GRANT SELECT ON OBJECT::test(a,d) TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
試驗11~16說明,賦予或拒絕角色或用戶對相同對象的權限時,拒絕權限具有較高優先級
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT ON OBJECT::test(a,d) TO GrantRole
REVOKE SELECT ON OBJECT::test(a,d) TO Jack
用戶從不同角色中繼承權限的相互影響
試驗17:賦予角色GrantRole對數據庫SELECT權限,拒絕角色DenyRole對數據庫SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗17:
EXEC sys.sp_addrolemember DenyRole,Jack
GRANT SELECT TO GrantRole
DENY SELECT TO DenyRole
EXEC sys.sp_helprotect NULL,GrantRole
EXEC sys.sp_helprotect NULL,DenyRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論:從不同角色繼承對相同對象的互斥權限(GRANT<<==>>DENY),用戶最終獲得的是拒絕權限。
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT TO GrantRole
REVOKE SELECT TO DenyRole
試驗18:賦予角色GrantRole對數據庫對象表test的SELECT權限,拒絕角色DenyRole對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗18:
GRANT SELECT ON OBJECT::test TO GrantRole
DENY SELECT ON OBJECT::test TO DenyRole
EXEC sys.sp_helprotect NULL,GrantRole
EXEC sys.sp_helprotect NULL,DenyRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
結論同試驗17.
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT ON OBJECT::test TO GrantRole
REVOKE SELECT ON OBJECT::test TO DenyRole
試驗19:賦予角色GrantRole對數據庫對象表test的a、d列SELECT權限,拒絕角色DenyRole對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗19:
GRANT SELECT ON OBJECT::test(a,d) TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO DenyRole
EXEC sys.sp_helprotect NULL,GrantRole
EXEC sys.sp_helprotect NULL,DenyRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
GO
REVERT
GO
試驗17~19說明,Jack用戶從兩個角色中繼承相反同對象的權限,仍然是拒絕權限佔較高優先級,同試驗11~16結論相同。
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT ON OBJECT::test(a,d) TO GrantRole
REVOKE SELECT ON OBJECT::test(a,d) TO DenyRole
同時先刪除DenyRole的成Jack,避免測試時其他因素影響:
EXEC sys.sp_droprolemember DenyRole,Jack
分別對角色/用戶賦予或拒絕父子對象的權限測試
試驗20:賦予角色GrantRole對數據庫SELECT權限,拒絕用戶Jack對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗20:
GRANT SELECT TO GrantRole
DENY SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
因爲對角色GrantRole賦予了數據庫的SELECT權限,對用戶Jack僅拒絕了表test的SELECT權限,所以Jack用戶仍然具有查看數據庫下除test表以外的對象的權限。
試驗21:拒絕角色GrantRole對數據庫SELECT權限,賦予用戶Jack對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗21:
DENY SELECT TO GrantRole
GRANT SELECT ON OBJECT::test TO Jack
EXEC sp_helprotect NULL ,Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
拒絕父級對象(這裏是數據庫)的SELECT權限、則拒絕所有子級(這裏是數據庫中的表test)對象的SELECT權限,即使單獨爲子級對象賦予了SELECT權限
恢復角色GrantRole、用戶Jack權限:
拒絕父級對象(這裏是數據庫)的SELECT權限、則拒絕所有子級(這裏是數據庫中的表test)對象的SELECT權限,即使單獨爲子級對象賦予了SELECT權限
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT TO GrantRole
REVOKE SELECT ON OBJECT::test TO Jack
試驗22:拒絕角色GrantRole對數據庫SELECT權限,賦予用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗22:
DENY SELECT TO GrantRole
GRANT SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT a,d FROM test
SELECT b FROM test
GO
REVERT
GO
拒絕父級對象(這裏是數據庫)的SELECT權限、則拒絕所有子級(這裏是數據庫中表test的字段a、d)對象的SELECT權限,即使單獨爲子級對象賦予了SELECT權限
試驗23:賦予角色GrantRole對數據庫SELECT權限,拒絕用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗23:
GRANT SELECT TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT a,d FROM test
SELECT b FROM test
GO
REVERT
GO
賦予父級對象(這裏指數據庫)SELECT權限,則僅僅拒絕子級對象中被拒絕的子級對象(這裏拒絕了子級對象表test的字段a、d列),對其他子級對象仍然有SELECT權限。
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT TO GrantRole
REVOKE SELECT ON OBJECT::test(a,d) TO Jack
試驗24:賦予角色GrantRole對數據庫對象表test的SELECT權限,拒絕用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗24:
GRANT SELECT ON OBJECT::test TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT a,d FROM test
SELECT b FROM test
GO
REVERT
GO
賦予父級對象(這裏指表test)SELECT權限,則僅僅拒絕子級對象中被拒絕的子級對象(這裏拒絕了子級對象表test的字段a、d列),對其他子級對象仍然有SELECT權限。
試驗25:在試驗24的基礎上增加字段e列,檢驗賦予權限後,新增加的對權限的繼承情況
--試驗25:
ALTER TABLE test ADD e int
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT e FROM test
GO
REVERT
GO
角色GrantRole對錶test有SELECT權限,並且對於新增的列(在賦予權限後創建的列)也有SELECT權限,但此時和10一樣,對新增的列拒絕了SELECT權限。
解決方案爲重新單獨爲其賦予權限。
GRANT SELECT ON OBJECT::test(e) TO GrantRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT e FROM test
GO
REVERT
GO
從結果來看,賦予角色GrantRole權限並沒有單獨增加對test表字段e的SELECT權限信息,而我們此時已經有了對test表字段e的SELECT權限(如圖中的結果)。
刪除e列,再重新創建e列,重新在Jack上下問中執行對e列的查詢
ALTER TABLE test DROP column e
ALTER TABLE test ADD e int
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT e FROM test
GO
REVERT
GO
發現又報上面的錯誤了。這也許算是SQL Server中EXEC AS user的一個Bug吧!!!
同樣在使用賬戶登錄服務器,連接數據庫test時,查詢新列e,有SELECT權限。
恢復test表結構:
ALTER TABLE test DROP COLUMN c,e
試驗26:拒絕角色GrantRole對數據庫對象表test的SELECT權限,賦予用戶Jack對數據庫對象表test的a、d列SELECT權限,檢驗在用戶Jack上下文中執行對錶test的查詢情況
--試驗26:
DENY SELECT ON OBJECT::test TO GrantRole
GRANT SELECT ON OBJECT::test(a,d) TO Jack
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT a,d FROM test
SELECT b FROM test
GO
REVERT
GO
拒絕父級對象(這裏指test表)的SELECT權限,則拒絕其所有子級對象(這裏列出test表字段a、d)的SELECT權限,即使再單獨賦予子級對象SELECT權限。
恢復角色GrantRole、用戶Jack權限:
REVOKE SELECT ON OBJECT::test TO GrantRole
REVOKE SELECT ON OBJECT::test(a,d) TO Jack
試驗從不同角色中繼承對不同對象權限的相互影響
試驗27:賦予角色GrantRole對數據庫的SELECT權限,拒絕角色DenyRole對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test、test1的查詢情況
--試驗27:
EXEC sp_addrolemember DenyRole,Jack
GRANT SELECT TO GrantRole
DENY SELECT ON OBJECT::test TO DenyRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,DenyRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
繼承角色對數據庫test的SELECT權限,同時繼承了拒絕表test的SELECT權限,在用戶有對數據庫的所有對象有SELECT權限,除拒絕的對象外。
試驗28:拒絕角色GrantRole對數據庫的SELECT權限,賦予角色DenyRole對數據庫對象表test的SELECT權限,檢驗在用戶Jack上下文中執行對錶test、test1的查詢情況
--試驗28:
DENY SELECT TO GrantRole
GRANT SELECT ON OBJECT::test TO DenyRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,DenyRole
EXEC AS USER='Jack'
GO
SELECT * FROM test
SELECT * FROM test1
GO
REVERT
GO
用戶從角色中繼承了拒絕數據庫test的SELECT權限,並繼承角色對錶test的SELECT權限,可以看到拒絕了數據庫的SELECT權限,則拒絕了數據庫所有對象的SELECT權限,即使單獨給這個對象賦予SELECT權限。
REVOKE SELECT TO GrantRole
REVOKE SELECT ON OBJECT::test TO DenyRole
試驗29:賦予角色GrantRole對數據庫的SELECT權限,拒絕角色DenyRole對數據庫對象表test的列a、d的SELECT權限,檢驗在用戶Jack上下文中執行對錶test各列的查詢情況
--試驗29:
GRANT SELECT TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO DenyRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,DenyRole
EXEC AS USER='Jack'
GO
SELECT b FROM test
SELECT a,d FROM test
GO
REVERT
GO
用戶繼承了角色對數據庫test的SELECT權限,同時繼承了拒絕表test的a、d列的SELECT權限。用戶對數據庫有SELECT權限,在用戶對除數據庫中拒絕的對象以外的所有對象有SELECT權限。
REVOKE SELECT TO GrantRole
REVOKE SELECT ON OBJECT::test(a,d) TO DenyRole
試驗30:拒絕角色GrantRole對錶test的SELECT權限,賦予角色DenyRole對數據庫對象表test的列a、d的SELECT權限,檢驗在用戶Jack上下文中執行對錶test各列的查詢情況
--試驗30:
DENY SELECT ON OBJECT::test TO GrantRole
GRANT SELECT ON OBJECT::test(a,d) TO DenyRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,DenyRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT b FROM test
SELECT a,d FROM test
GO
REVERT
GO
用戶繼承了角色拒絕表test的SELECT權限,同時繼承了賦予角色對錶test的a、d列的SELECT權限。用戶拒絕表test的SELECT權限,在用戶對拒絕表中所有列的SELECT權限(包括新增列),即使再單獨賦予用戶對某些列的SELECT權限。
試驗31:賦予角色GrantRole對數據庫的SELECT權限,拒絕角色DenyRole對數據庫對象表test的列a、d的SELECT權限,檢驗在用戶Jack上下文中執行對錶test各列的查詢情況
--試驗31:
GRANT SELECT ON OBJECT::test TO GrantRole
DENY SELECT ON OBJECT::test(a,d) TO DenyRole
EXEC sp_helprotect NULL ,GrantRole
EXEC sp_helprotect NULL ,DenyRole
EXEC sp_helprotect NULL ,Jack
EXEC AS USER='Jack'
GO
SELECT b FROM test
SELECT a,d FROM test
GO
用戶繼承了賦予角色對錶test的SELECT權限,同時繼承了拒絕角色對錶test的a、d列的SELECT權限。用戶對錶test有SELECT權限,則用戶對錶中的所有列有SELECT權限,除單獨拒絕的某些列外。