Sql Server前因後果系列 必須知道的權限控制核心篇

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

提幾個問題

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

    1.登錄帳號和用戶有什麼區別;數據庫

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

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

溫故而知新

    在《Sql Server前因後果系列  必須知道的權限控制基礎篇》中,咱們介紹了登錄帳號的增刪改查、數據庫的增刪該查以及這些元數據對應的系統視圖和操做語法。可是,一個登錄帳號被建立後並不能建立或者操做數據庫,必須通過授予某些權限後才能操做管理數據庫。如何給登錄帳號授予正確的權限,就是本篇的主要內容。app

讓人混淆的幾個概念

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

    執行結果以下:ui

clipboard

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

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.OptTable一、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的關係。

 

   若是本篇內容對你們有幫助,請點擊頁面右下角的關注。若是以爲很差,也歡迎拍磚。大家的評價就是博主的動力!下篇內容,敬請期待!

相關文章
相關標籤/搜索