Sql Server來龍去脈系列 必須知道的權限控制核心篇


    最近寫了《Sql Server來龍去脈系列  必須知道的權限控制基礎篇》,感覺反響比較大。這可能也說明了很多程序猿對數據庫權限控制方面比較感興趣,或者某些技術點了解的沒有很透徹。 有些人看了上篇感覺意猶未盡,介紹的都是基礎方面,不夠深入。那么本篇內容就比較符合大家的胃口,本篇包括了數據庫常用的權限控制,例如服務角色以及數據庫角色管理。

提幾個問題

    在介紹權限控制之前先提下面幾個問題,如果有回答不上來的問題,本篇內容你應該看。如果很清晰的回答出這些問題,那么本篇接下來的內容你可以直接忽略。

    1.登陸賬號和用戶有什么區別;

    2.服務角色和數據庫角色有什么區別;

    3.主體、用戶、架構之間的區別(Principal、User、Schema);

溫故而知新

    在《Sql Server來龍去脈系列  必須知道的權限控制基礎篇》中,我們介紹了登陸賬號的增刪改查、數據庫的增刪該查以及這些元數據對應的系統視圖和操作語法。但是,一個登陸賬號被創建后並不能創建或者操作數據庫,必須經過授予某些權限后才能操作管理數據庫。如何給登陸賬號授予正確的權限,就是本篇的主要內容。

讓人混淆的幾個概念

1.主體

    主體是可以請求SQL SERVER資源的實體。主體按照作用范圍可分為三類主體:Windows級別主體、服務器級別主體、數據庫級別主體。master數據庫中有兩張視圖sys.server_principals、sys.server_principals用於存儲主體數據。先來看看sys.server_principals視圖,執行以下語句:

select principal_id, name, type, type_desc from sys.server_principals ,視圖說明:https://msdn.microsoft.com/zh-cn/library/ms188786(v=sql.120).aspx

    執行結果如下:

clipboard

    查詢結果包含了Windows登錄賬號(WINDOWS_LOGINS)、SqlServer登錄賬號(SQL_LOGIN)、服務角色(SERVER_ROLE)。Windows級別主體就是我們的Windows登錄賬號,服務器級別主體就是Sql Server登錄賬號和服務角色。接下來我們再執行以下語句:

select principal_id, name, type, type_desc from sys.database_principals,視圖說明:https://msdn.microsoft.com/zh-cn/library/ms187328(v=sql.120).aspx

   執行結果如下:

clipboard[1]

    結果中包含Sql Server用戶(SQL_USER)、Windows用戶(WINDOWS_USER)、數據庫角色(DATABASE_ROLE),這三種數據都屬於數據庫級別主體。通過下圖我們可以很容易的看出主體具體包括哪些數據。

clipboard[2]

    需要注意的是,數據庫級別主體中包含有Sql Server用戶和Windows用戶。那么什么是用戶?用戶就是我們接下來的第二個概念。

2.數據庫用戶

    數據庫用戶是數據庫級別的主體,被用於訪問數據庫層面的對象。一個獨立的登錄賬號默認是不能夠鏈接數據庫的,必須和數據庫用戶建立對應關系才能根據用戶的權限訪問數據庫。但是,一個數據庫用戶也是不能獨立存在的,它也必須關聯一個登錄賬號。前面說過,用戶是訪問數據庫層面的對象。那么,數據庫、登錄賬號、用戶之間有什么關系?我們先看SQL Server Management Studio的一個操作:查看登錄賬號test2的賬號信息,選擇User Mapping頁簽。如下圖所示:

clipboard[3]

   分析上圖,如果想給一個登錄賬號設置數據庫權限,我們不能直接把登錄賬號和數據庫做關聯,數據庫只能和數據庫用戶做關聯,然后數據庫用戶和登錄賬號關聯。登錄賬號和用戶的存儲是有區別的,登陸賬號存儲在系統數據庫master中,而用戶是存儲在數據庫中的,並且都是存儲在系統視圖sys.sysusers中。例如,我們查詢上圖的用戶test。在系統數據庫master和用戶數據庫TestDb中查詢sys.sysusers(視圖說明:https://msdn.microsoft.com/zh-cn/library/ms179871(v=sql.120).aspx)視圖,對比如下:

clipboard[4]

    通過上圖我們明顯看出,創建的用戶test存儲在testDb數據庫中。並且對於同一個數據庫,一個用戶只能對應一個登陸賬號。不同的數據庫可以有相同名稱的用戶名。數據庫、用戶以及登錄賬號的關系可通過下面的關系圖表示:

clipboard[5]

    上圖中,數據庫DbAdmin中的存儲有登錄賬號1對1關系的用戶,而數據庫TestDb中也單獨存儲有登錄賬號1對1關系的用戶。但登錄賬號數據是唯一的並且保存在系統數據庫master中。之前在操作SQL Server Management Studio中登錄賬號屬性User Mapping頁簽數據時,我們能看到每個數據庫其實還對應了一個"Default Schema",Schema也叫做架構。架構就是本篇需要說明的第三個概念。

3.數據庫架構

   架構(Schema)相當於存儲數據庫對象的一個容器,它是SQL Server 2005之后版本引入的。你可以理解架構為一個命名空間。在SQL Server 2000中其實也有架構的概念,不同的是SQL Server 2000的架構和用戶是綁定的。例如 ,我新建一個用戶heavi,Sql Server自動分配了一個叫做heavi的架構,並且用戶heavi和架構heavi之間的關系不能更改。如果新建一張表Teacher,則為heavi.Teacher。但heavi離職時,Teacher這站表就很難維護。

    但Sql Server 2005之后,用戶和機構是分離的。我可以把Teacher表的架構heavi分配給其他用戶,方便管理。訪問一張表一般有三種方式:

select * from Teacher;
select  * from heavi.Teacher;
select * from TestDb.heavi.Teacher;

   第一種方式只能是當前登錄賬號對應的用戶默認Schema正好是heavi的情況才能正常使用。例如用戶heavi對應的登錄賬號為sa,那么只有sa賬號才能直接使用select * from Teacher語句。其他賬號必須加上架構名稱。

    數據庫包含的對象包括系統表、用戶表、視圖、存儲過程等。這些對象都被“包含”在架構下。例如下圖,數據庫表、視圖、存儲過程都掛在架構為dbo下。

clipboard[6]

服務器角色

查詢角色

   之前介紹了什么是主體,我們知道主體包含服務器級別主體。而服務器級別主體又包含有服務器角色。服務器角色的存在,方便操作員對服務器級別的管理。例如給登錄賬號授予sysadmin服務器角色后,該登錄賬號擁有Sql Server服務器的任何操作權限。Sql Server包括哪些服務器角色?從前面介紹的系統視圖sys.server_principals可以查詢所有的服務器角色。執行查詢語句:

select sp.name as Name, sp.principal_id as Id, sp.sid as Sid, sp.type as Type, sp.type_desc as Type_Desc from sys.server_principals sp where sp.type = 'R'

     查詢結果如下:

clipboard[7]

    查詢的結果集就是服務器上包含的所有服務器角色。具體每個服務器角色包含哪些權限可查看說明:https://msdn.microsoft.com/zh-cn/library/ms188659(v=sql.120).aspx

查詢授權角色

    在我的權限管理系統中,分別查詢出了所有的登錄賬號和服務器角色,如下圖所示:

clipboard[8]

    當我選擇登錄列表中的某個登錄賬號時,系統會從數據庫中查詢當前選中賬號授予了哪些服務器角色。由於服務器角色是固定不變的,微軟直接把賬號的服務器角色授權數據保存在系統視圖sys.syslogins中。在權限管理服務器執行的查詢語句如下:

select 
sid as Sid, name as Name, dbname as DbName,password as Password, language as Laguage, createdate as CreateDate,
sysadmin as IsSysAdmin, securityadmin as IsSecurityAdmin, serveradmin as IsServerAdmin,
setupadmin as IsSetupAdmin, processadmin as IsProcessAdmin, diskadmin as IsDiskAdmin,
dbcreator as IsDbCreator, bulkadmin as IsBulkAdmin, 1 as IsPublic 
from sys.syslogins where name = 'sa'

    查詢結果如下:

clipboard[9]

    結果中,1表示已授權、0表示未授權 。所以賬號sa授予了sysadmin和public服務器角色,其中public是默認角色,新創建的登錄賬號默認擁有public角色。

修改授權角色

    每個登錄賬號授予的服務器角色我們能夠查詢出來,有些時候我們也需要修改賬號的服務角色。例如新創建的dbAdmin需要管理用戶的數據庫權限,則必須給 dbAdmin授予securityadmin服務角色。像修改登陸賬號或者數據庫時我們都是使用DDL語句操作,修改服務角色也不例外。我們先看下修改服務角色的參考語句:

ALTER SERVER ROLE server_role_name 
{
    [ ADD MEMBER server_principal ]
  | [ DROP MEMBER server_principal ]
  | [ WITH NAME = new_server_role_name ]
} [ ; ]--說明:https://msdn.microsoft.com/zh-cn/library/ee677634(v=sql.120).aspx

server_role_name表示角色名稱,server_principal可以是登錄賬號或者用戶定義的服務器角色。接下來再看看權限管理系統中修改授權角色的代碼:

public void Update(string name, IList<string> roles)

        {
            string sql = string.Empty;
           foreach(var role in roles)
            {
                if (string.IsNullOrEmpty(role))
                {
                    throw new Exception("更新服務角色失敗,參數有誤。");
                }
                string[] arr = role.Split(':');
                bool value;
                if(arr.Length != 2 || string.IsNullOrEmpty(arr[0]) || !bool.TryParse(arr[1], out value))
                {
                    throw new Exception("更新服務角色失敗,參數不匹配");
                }
                if(arr[0] == "public")
                {
                    continue;
                }

                string opt = value ? "ADD" : "DROP";
                sql += string.Format(@"ALTER SERVER ROLE [{0}]  {1} MEMBER [{2}] ;", arr[0], opt, name);
            }
            DbHelper.Instance().ExecuteNonQuery(sql);
        }

     代碼Update方法的參數name表示登錄賬號,roles表示服務器角色和操作項數據,例如其中一個role為:sysadmin:ADD。每個角色都會遍歷執行一次。如刪除sa的sysadmin角色,執行語句為:ALTER SERVER ROLE [sysadmin] DROP MEMBER [sa]。需要注意的是,public是默認授予的服務角色,不能修改。

數據庫角色

查詢角色

  和服務器角色相似,主體也包含了數據庫級別主體,而數據庫級別主體又包含了數據庫角色。我們可以從系統視圖sys.database_principals查詢。執行查詢語句:

select sid as Sid, name as Name, principal_id as Id, type as Type, type_desc as Type_Desc from sys.database_principals where type = 'R'

    查詢結果如下:

clipboard[10]

   結果集中包含了所有的數據庫角色,具體每個數據庫角色包含哪些權限,可查看說明:https://msdn.microsoft.com/zh-cn/library/ms189121(v=sql.120).aspx

查詢架構

    架構的存儲和數據用戶存儲相似,都是存儲在數據庫中。我們可以通過系統視圖sys.schemas查詢數據庫中存在的架構數據。執行下面的語句:

USE [數據庫];
select sc.name as SchemaName,sc.schema_id as Id, ps.type as Type,ps.type_desc as Type_Desc, ps.principal_id as PrincipalId, ps.name as PriincipalName 
from sys.schemas as sc 
inner join sys.database_principals ps on sc.principal_id = ps.principal_id
where ps.type = 'S'

    在執行查詢之前必須要做數據庫切換,切換到當前需要查詢的數據庫。執行結果如下:

clipboard[11]

    Schema是和用戶關聯的,因為每個用戶都有一個默認的Schema。而用戶的主體數據存儲在database_principals中。系統視圖sys.schemas的詳細描述請查看:https://msdn.microsoft.com/zh-cn/library/ms176011(v=sql.120).aspx

查詢數據庫用戶和默認架構

    首先看下權限管理系統的數據庫權限管理界面,如下圖所示:

clipboard[12]

    整個界面包含了五個部分,分別是數據庫列表、登錄賬號、數據庫用戶、默認架構、數據庫角色。通過之前介紹的知識我們已經知道數據庫和登錄賬號怎樣查詢了。查詢數據庫授權角色的步驟是先選擇某個數據庫,然后從登錄賬號下拉列表中選中某個登錄賬號。選擇登錄賬號后,權限管理系統會根據選擇的數據庫和登錄賬號到數據庫查詢對應的數據庫用戶、默認架構以及授予了哪些數據庫角色。

    首先分析怎樣查詢數據庫用戶和默認架構,這些數據關聯了sys.sql_logins、sys.sysusers、sys.database_principals、sys.schemas等系統視圖。它們之間的關系如下圖所示:

clipboard[13]

    查詢當前數據庫中登錄賬號、數據庫用戶、架構數據的sql代碼如下:  

string sql = string.Format("USE {0};select lo.sid as Sid, lo.name as LoginName, su.uid as UserId, su.name as UserName, sc.schema_id as SchemaId, pr.default_schema_name as SchemaName from sys.sql_logins lo " +
                        "inner join sys.sysusers su on lo.sid = su.sid " +
                        "inner join sys.database_principals pr on lo.sid = pr.sid " +
                        "left join sys.schemas sc on pr.default_schema_name = sc.name " +
                        "where lo.name = '{1}';", dbName, loginName);

    例如,我當前查詢數據庫testDb下登錄賬號為test2的用戶和默認shcema數據,結果如下:

clipboard[14]

    查詢結果就包含了當前登錄賬號test2的數據庫用戶test以及用戶test默認的schema為t。

查詢授權角色

 

    在同一個數據庫中,不同的用戶有不同的數據庫角色。數據庫用戶和數據庫角色同屬於數據庫級別主體,都存儲在系統視圖sys.database_principals中。數據庫用戶類型為S,數據庫角色類型為R。如果用戶授予了某些角色,用戶和角色必定有關聯關系,這些關聯關系存儲在系統視圖sys.database_role_members。執行以下SQL語句:

select * from sys.database_role_members,視圖說明:https://msdn.microsoft.com/zh-cn/library/ms189780(v=sql.120).aspx

    查詢結果如下圖所示:

clipboard[15]

    sys.database_role_members視圖包含兩個字段,role_principal_id表示角色主體ID,member_pricinpal_id表示用戶主體ID。如果需要查詢指定數據庫下某個用戶授予的數據庫角色,可通過權限管理系統執行的SQL代碼查詢:

USE testDb;
select dbp1.principal_id as Id, dbp1.name as Name, dbp1.type as Type 
from sys.database_role_members rm 
inner join sys.database_principals dbp1 on rm.role_principal_id = dbp1.principal_id 
inner join sys.database_principals dbp2 on rm.member_principal_id = dbp2.principal_id 
where dbp2.name = 'test'

    在查詢前必須切換到需要查詢的數據庫,因為數據庫角色、用戶數據都存儲在數據庫中。例如,我們需要查詢testDb(dbName = 'testDb')數據庫中用戶為test(userName = 'test')的數據庫角色。查詢結果如下:

clipboard[16]

    查詢結果表明test用戶授予了db_owner和db_datareader兩個數據庫角色。而我們的權限管理系統查詢結果界面為:

clipboard[17]

    上面表示的整個流程為數據庫testDb下,登錄賬號test2對應的用戶為test,而用戶test默認的架構為t。並且用戶test擁有db_owner和db_datareader數據庫角色。public是每個用戶默認擁有的角色。那么查詢的數據到底是否正確,我們可以通過SQL Server Management Studio工具中的login Properties校驗。打開登錄賬號test2的login Properties窗口,結果如下圖所示:

clipboard[18]

    通過對比可以看出,testDb下登錄賬號test2的用戶、默認架構以及數據庫角色和我們權限管理系統查詢結果完全一致。

修改權限角色

    1.登錄賬號對應的用戶沒有改變,只改變了默認Schema或者角色

    我們可以查詢數據庫下某個賬號對應的用戶和默認架構,那么我們也可以修改對應的用戶和架構。我們應該記得,像操作系統級別元數據,我們一般都使用數據定義語言(DDL)。修改用戶的 DDL語言定義如下:

-- SQL Server Syntax
ALTER USER userName  
     WITH <set_item> [ ,...n ]
[;]
<set_item> ::= 
      NAME = newUserName 
    | DEFAULT_SCHEMA = { schemaName | NULL }
    | LOGIN = loginName
    | PASSWORD = 'password' [ OLD_PASSWORD = 'oldpassword' ]
    | DEFAULT_LANGUAGE = { NONE | <lcid> | <language name> | <language alias> }--說明:https://msdn.microsoft.com/zh-cn/library/ms176060(v=sql.120).aspx

    用戶不變情況下我們只用修改默認架構即可,SQL語句比較簡單,如下所示:

ALTER USER {0} WITH DEFAULT_SCHEMA = {1} --{0}表示用戶,{1}表示默認架構名稱

    默認架構修改完了 ,我們還得修改用戶對應的數據庫角色。修改用戶的數據庫角色和修改登錄賬號的服務器角色相似,都是遍歷所有的數據庫角色,按照每個角色的勾選情況刪除或者增加每個角色的用戶成員。數據庫角色增加或刪除成員的方式和服務器角色不同,是通過系統存儲過程操作,增加成員的存儲過程為sp_addrolemember(說明:https://msdn.microsoft.com/zh-cn/library/ms187750(v=sql.120).aspx),刪除成員的存儲過程為sp_droprolemember(說明:https://msdn.microsoft.com/zh-cn/library/ms188369(v=sql.120).aspx)。權限管理系統中操作數據庫角色成員的代碼如下:

private string CreateExecuteDbRoleSql(DatabaseUserMapping mapping)

        {
            string setDbRoleSql = string.Empty;
            if (mapping.RoleList != null && mapping.RoleList.Count > 0)
            {
                setDbRoleSql = string.Format("USE {0};", mapping.DbName);
                foreach (var role in mapping.RoleList)
                {
                    if(role.Name == "public")
                    {
                        continue;
                    }
                    var produreName = role.IsAuthorized ? "sp_addrolemember" : "sp_droprolemember";
                    setDbRoleSql += string.Format("EXEC {2} @rolename = '{0}',@membername = '{1}';", role.Name, mapping.UserName, produreName);
                }
            }
            return setDbRoleSql;
        }

    第一步操作還是使用USE {0}切換到當前數據庫,然后遍歷角色集合,如果當前用戶授予了角色(role.IsAuthorized為true),則執行sp_addrolemember。否則執行sp_droprolemember。

   2.登錄賬號對應的用戶已改變

    之前的內容我們有講到同一個數據庫下,登錄賬號和用戶是一一對應關系。也就是說同一個用戶不能關聯兩個用戶。在這樣的一個背景下,現在我們修改了登錄賬號對應的用戶,那么之前的用戶就被丟棄了。例如,登錄賬號Tom,對應用戶為tom。現在我把Tom對應的用戶修改為joly,之前的用戶tom就被丟棄了。所以,如果登錄賬號對應的用戶改變后,我們需要做一系列的操作。具體步驟如下:

    步驟1,刪除之前用戶對應的數據庫角色

    首先把之前用戶授予的數據庫角色查詢出來(在"查詢授權角色"已經講過怎樣查詢授權的數據庫角色)。然后逐個遍歷角色,執行刪除語句:

EXEC sp_droprolemember @rolename = '{0}' , @membername =  '{1}' --{0}表示角色名稱,{1}表示用戶名稱

    步驟2,刪除之前的用戶

    刪除用戶使用DDL的DROP操作,執行語句如下:

USE {0};DROP USER {1},--{0}表示數據,{1}表示用戶名,DROP說明:https://msdn.microsoft.com/zh-cn/library/ms189438(v=sql.120).aspx

   步驟3,創建新用戶

    創建用戶使用DDL的CREATE USER操作,執行語句如下:

CREATE USER {0} FOR LOGIN {1} WITH DEFAULT_SCHEMA = {2},CREATE說明:https://msdn.microsoft.com/zh-cn/library/ms173463(v=sql.120).aspx

    操作語句其中的{0}表示用戶名稱,{1}表示登錄賬號,{2}表示默認架構名稱。

    步驟4,給新用戶授予數據庫角色

    給新用戶授予數據庫角色方式和前面一樣,直接遍歷角色,然后調用存儲過程sp_addrolemember為數據庫角色增加用戶成員。

重點

    前面介紹的內容更偏重於怎樣授權,接下來舉個實際的例子來說明怎樣使用這些授權。首先使用我們的權限管理系統創建數據庫OperationDb,然后創建兩個登錄賬號opt1和opt2。剛創建的登錄賬號opt1和opt2是沒有任何權限的,所以需要給這兩個賬號授權。

    登錄賬號opt1授權如下:

clipboard[19]

    登錄賬號opt2授權如下:

clipboard[20]

    兩個賬號都只授予了db_accessadmin數據庫角色。那么現在我用opt1創建一張表OptTable1會是怎樣的結果?執行結果如下:

clipboard[21]

   執行結果說明opt1賬號沒有CREATE TABLE的權限,現在我們再為賬號opt1和opt2都授予DDL操作權限db_ddladmin角色。然后我們再通過opt1創建OptTable1表,通過opt2創建OptTable2表,都能創建成功。我們可通過數據庫管理工具查看到這兩張表,如下圖所示:

clipboard[22]

    分析上圖,OptTable1表前面標注了opt1,而OptTable2表前面標注了opt2。opt1表示的是登錄賬號opt1對應的用戶opt1的默認架構opt1,opt2表示登錄賬號opt2對應的用戶opt2的默認架構opt2。下面的圖片表示的比較直觀:

clipboard[23]

    表創建好了后,分別使用登錄賬號opt1和opt2查詢opt1.OptTable1、opt2.OptTable2表。opt1和opt2查詢結果對比如下:

clipboard[24]

    通過結果可看出在ddladmin角色權限下,賬號opt1沒有權限查看Schema為opt2下面的表數據,而opt2沒有權限查看Schema為opt1下面的表數據。之前說過架構相當於一個容器,而架構opt1和架構opt2相當於同級的兩個容器。整個層次關系如下圖所示:

clipboard[25]

    登錄賬號Opt1只能操作Schema為opt1下面的對象,而登錄賬號opt2只能操作Schema為opt2下面的對象。如果要想讓opt1同時擁有操作Schema為opt1和opt2下面的表,我們需要把權限提高到數據庫層面上。而數據庫角色db_datareader,可以訪問數據庫下面的所有Schema。所以分別為賬號opt1和opt2授予db_datareader后,現在可以訪問OperationDb下面的所有表數據了。

總結

   通過本篇的內容介紹,在回顧篇頭提出的幾個問題,現在應該能夠回答個一二。本篇先介紹了幾個常用到的概念,包括主體、數據庫用戶、數據庫架構。然后分別介紹了服務器角色和數據庫角色授權的查詢、修改、刪除功能。在”數據庫角色“中也介紹了用戶的增刪改查以及登錄賬號、用戶和Schema的關系。

 

   如果本篇內容對大家有幫助,請點擊頁面右下角的關注。如果覺得不好,也歡迎拍磚。你們的評價就是博主的動力!下篇內容,敬請期待!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM