Security Tutorials系列文章第九章:Creating and Manageing Roles

 本文英文原版及代碼下載:http://www.asp.net/learn/security/tutorial-09-cs.aspx


Security Tutorials系列文章第九章:Creating and Manageing Roles

導言:
 
    在前面的文章《User-Based Authorization》裏我們是針對某些具體的用戶運用URL authorization來對要訪問的頁面進行限制.不過這樣處理方式在這些場合下是難以想象的:有很多的用戶帳戶,且用戶的權限(privileges)經常改變.每當用戶獲得或失去某個授權時,管理員都要對URL authorization規則進行更改.

   通常的做法是將用戶劃分爲不同的組或角色,然後以role-by-role的原則執行授權規則.比如,很多的web applications都有一系列的頁面只面向“管理員”用戶,如果以前面的基於用戶的的授權規則的話,我們要添加相應的URL authorization規則,顯式或寫代碼來允許特定的用戶帳戶執行管理操作.不過如果新添加一個管理員或移除一個管理員,我們也要相應的添加或刪除他的“管理員”授權規則,這樣就需要重新更新配置文件以及web頁面了.不過,利用role的話情況就不同了,我們可以創建一個名爲Administrators的role,再將相應的用戶帳戶分配給該role.接下來,再添加相應的URL authorization規則,顯式或寫代碼允許名爲Administrators的role執行相關的管理員操作.這樣一來,不管是添加新的管理員或移除現有的管理員,都不需要更改配置文件、或修改代碼.

    ASP.NET提供了一個Roles framework以定義roles並分配給用戶帳戶.有了 Roles framework我們就可以創建和刪除role、將一個用戶帳戶添加到某個role或
從某個role裏刪除該用戶、獲取屬於某個role的所有用戶帳戶、探測某個用戶帳戶是否屬於某個role等.一旦配置好Roles framework後,我們就可以role-by-role的原則定義URL authorization規則、根據登錄用戶的role不同而提供或隱藏頁面功能.

  本文考察了配置Roles framework所必須的步驟,接下來,在文章《Assigning Roles to Users》裏考察如何向roles裏添加或刪除用戶帳戶.

再在文章《Role-Based Authorization tutorial》裏考察如何以role-by-role的原則對頁面的訪問設限,以及如何根據登錄用戶的roles不同而提供不同的功能.


第1步:Adding New ASP.NET Pages

   本文以及後面的2篇文章將考察與roles相關的內容,我們來爲這3篇文章添加頁面.

   首先創建一個名爲Roles的文件夾,再添加4個頁面,都要記得運用母版頁,如下:

.ManageRoles.aspx
.UsersAndRoles.aspx
.CreateUserWizardWithRoles.aspx
.RoleBasedAuthorization.aspx
此時,你的界面看起來和下面的差不多:


圖1

  每個頁面都應該有2個Content控件,對應母版頁的2個ContentPlaceHolders控件: MainContent 以及 LoginContent

<asp:Content
ID="Content1" ContentPlaceHolderID="MainContent"Runat="Server">
</asp:Content>

<asp:Content ID="Content2"
ContentPlaceHolderID="LoginContent"Runat="Server">
</asp:Content>

  記得LoginContent ContentPlaceHolder的默認界面顯示的是一個登錄或註銷的鏈接;而這個Content2 Content控件重寫了頁面的默認代碼.正如我們在前面的《An Overview of Forms Authentication》探討的那樣,這樣重寫再某些情況下是很有用處的.  
     不過對這4個頁面而言,我們想要顯示LoginContent ContentPlaceHolder的默認代碼,因此移除Content2 Content的聲明代碼.完成後,每個頁面的的聲明代碼裏就只包含一個Content控件了.

   最後更新Web.sitemap文件.在<siteMapNode>後添加如下的XML:

<siteMapNode title="Roles">
     <siteMapNode url="~/Roles/ManageRoles.aspx"
title="Manage Roles"/>      <siteMapNode
url="~/Roles/UsersAndRoles.aspx" title="Users and Roles" />
     <siteMapNode
url="~/Roles/CreateUserWizardWithRoles.aspx" title="Create Account (with Roles)"
/>      <siteMapNode
url="~/Roles/RoleBasedAuthorization.aspx" title="Role-Based Authorization" />
</siteMapNode>

在瀏覽器裏登錄網站,如圖2所示,左邊的導航欄就包括這幾個頁面了.


圖2


Step 2: Specifying and Configuring the Roles Framework Provider

  和Membership framework類似,Roles framework也是使用的provider model.如在文章《Security Basics and ASP.NET Support》裏探討的那樣, .NET Framework內置了3個Roles providers:也就是AuthorizationStoreRoleProvider, WindowsTokenRoleProvider,以及SqlRoleProvider. 本文專注的是SqlRoleProvider,它以Microsoft SQL Server作爲role store.

   .NET Framework包含一個Roles class,作爲Roles framework的API.而Roles class包含了一些static methods,比如: CreateRole, DeleteRole, GetAllRoles, AddUserToRole, IsUserInRole等.當調用這些方法時,Roles class將調用委託給配置好的provider進行處理.SqlRoleProvider要用到與role相關的表(比如:aspnet_Roles and aspnet_UsersInRoles)

   要在我們的應用程序裏使用SqlRoleProvider provider,我們需要指定使用哪個數據庫.而SqlRoleProvider要指定的數據庫包含某些數據庫表、視圖、存儲過程等.我們可以利用aspnet_regsql.exe工具來添加這些database objects,不然現在我們的數據庫以及擁有了SqlRoleProvider所必需的構架.在前面的文章《 Creating the Membership Schema in SQL Server》裏,我們創建了一個名爲SecurityTutorials.mdf的數據庫,並用aspnet_regsql.exe添加了application services,其中就包含了SqlRoleProvider所必需的database objects。因此,我們只要告訴Roles framework啓動role support,且SqlRoleProvider使用SecurityTutorials.mdf作爲role store.
  
 Roles framework是在Web.config文件裏的<roleManager>元素來配置的.默認時,沒有啓用role support,要啓用的話我們要將<roleManager>元素的enabled屬性設爲true,如下:
  <?xml version="1.0"?> <configuration>
     <system.web>
          ... Additional
configuration markup removed for brevity ...
          <roleManager
enabled="true" />      <system.web>
</configuration>

默認,所有的web applications都有一個類型爲SqlRoleProvider,名稱爲AspNetSqlRoleProvider的Roles provider.該默認的provider是在machine.config文件(位於%WINDIR%/Microsoft.Net/Framework/v2.0.50727/CONFIG)裏註冊的,如下:

<roleManager>
     <providers>
          <add
name="AspNetSqlRoleProvider"
               connectionStringName="LocalSqlServer"
               applicationName="/"
               type="System.Web.Security.SqlRoleProvider,
               System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
     </providers> </roleManager>


  provider的connectionStringName屬性指定了要使用的role store.該AspNetSqlRoleProvider provider將其設置爲LocalSqlServer——它也是在machine.config裏定義的,默認是指向App_Data文件夾裏的名爲aspnet.mdf的一個SQL Server 2005 Express Edition數據庫.

   因此,如果我們僅僅啓用Roles framework而沒有在Web.config文件裏指定任何的provider信息,那麼應用程序將使用默認的AspNetSqlRoleProvider,如果 ~/App_Data/aspnet.mdf數據庫不存在的話,ASP.NET runtime會自動的創建並添加必要的構架。不過我們不使用這些默認的配置,我們要使用我們以及創建好了的SecurityTutorials.mdf.爲此,我們可以通過如下的2種方式來配置:

1.在Web.config文件裏爲LocalSqlServer連接字符串名稱指定一個值.通過重寫Web.config裏的 LocalSqlServer連接字符串名稱,我們可使用默認註冊的Roles provider(AspNetSqlRoleProvider),且使用SecurityTutorials.mdf數據庫.對該方法的更多詳情,請參閱Scott Guthrie的博文:《Configuring ASP.NET 2.0 Application Services to Use SQL Server 2000 or SQL Server 2005》

2.添加一個新的類型爲SqlRoleProvider的provider,並設置其connectionStringName屬性指向SecurityTutorials.mdf數據庫.這是我推薦的方法,也是在文章《Creating the Membership Schema in SQL Server》裏使用的方法。

  在Web.config文件裏添加如下的Roles配置代碼,它註冊了一個名爲SecurityTutorialsSqlRoleProvider的新的provider.

<?xml version="1.0"?> <configuration>
     <connectionStrings>
          <add
name="SecurityTutorialsConnectionString"
               connectionString="..."/>
     </connectionStrings>
     <system.web>
          ... Additional
configuration markup removed for brevity ...
          <roleManager
enabled="true" defaultProvider="SecurityTutorialsSqlRoleProvider">
               <providers>
                    <add
name="SecurityTutorialsSqlRoleProvider"
                         type="System.Web.Security.SqlRoleProvider"
                         applicationName="SecurityTutorials"
                         connectionStringName="SecurityTutorialsConnectionString"
/>
               </providers>
          </roleManager>
     <system.web> </configuration>

 上述代碼定義了SecurityTutorialsSqlRoleProvider作爲默認的provider(通過<roleManager>元素的defaultProvider屬性).它也將SecurityTutorialsSqlRoleProvider的applicationName屬性設爲SecurityTutorials.有一點要指出,還可以在<add>元素裏包含一個commandTimeout屬性,用於指定數據庫的timeout值,單位爲秒,默認值爲30. 

 配置好後我們就可以在應用程序裏使用role功能了
注意:
    上述代碼演示了<roleManager>元素的enabled和defaultProvider的用法,在後面的《Role-Based Authorization》裏,我們將考察其他的設置.


Step 3: Examining the Roles API

  Roles class有13個static methods來執行基於role的操作.當我們在第4和第6步驟裏考察創建和刪除roles的時候,我們將用到CreateRole和DeleteRole方法,這些方法從系統添加或移除role.

   要列出屬於某個roles的所有用戶,要用到GetAllRoles method(第5步)。而RoleExists method返回一個布爾值,指出是否存在某個role.

  在下篇文章我們將看到如何將用戶和roles關聯起來.Roles class的AddUserToRole, AddUserToRoles, AddUsersToRole,以及AddUsersToRoles方法將一個或多個用戶添加到一個或多個roles裏.要將用戶從roles裏移除,我們要使用 RemoveUserFromRole, RemoveUserFromRoles, RemoveUsersFromRole, 或RemoveUsersFromRoles方法.

   在後面的文章《Role-Based Authorization》裏,我們將看到如何根據當前登錄用戶的role編程顯示或隱藏函數功能的方式.爲此我們將使用FindUsersInRole, GetRolesForUser, GetUsersInRole,或IsUserInRole方法.
注意:
    時刻謹記,當調用這些方法時,Roles class將把這些請求委託給我們配置的provider.就我們的程序而言,這就意味着調用將傳給SqlRoleProvider.它再執行一些相關的數據庫操作.比如Roles.CreateRole("Administrators")示例將會使SqlRoleProvider執行aspnet_Roles_CreateRole存儲過程,向aspnet_Roles表添加一個新的名爲“Administrators”的記錄.

下面我們將考察使用Roles class的CreateRole, GetAllRoles,DeleteRole方法來管理系統裏的roles.

Step 4: Creating New Roles

    很遺憾的是ASP.NET裏沒有一個CreateRoleWizard控件,爲了向系統添加一個新的roles,我們必須創建一個合適的界面並調用Roles API.可喜的是這樣做很容易辦到.
注意:
    雖然沒有CreateRoleWizard控件,但我們可以利用ASP.NET Web Site Administration Tool,該工具是設計來查看和管理你自己的應用程序配置的.不過我對它不太感冒。第一,它用起來有點不爽,需要設置很多東西,第二,該工具設計初衷是本地運行,這就意味着如果你不是在本地管理的話你就要構建立自己的role management web頁面.因此,在本文以及後面的文章我們都關注在一個web頁面裏構建必要的role管理工具而不使用ASP.NET Web Site Administration Tool.

   打開,Roles文件夾下的ManageRoles.aspx頁面,添加一個TextBox,id爲RoleName和一個 Button,id和Text分別爲CreateRoleButton和Create Role.此時你的聲明代碼和下面的差不多:
<b>Create a New Role: </b> <asp:TextBox
ID="RoleName" runat="server"></asp:TextBox> <br /> <asp:Button
ID="CreateRoleButton" runat="server" Text="Create Role" />

然後雙擊CreateRoleButton按鈕添加如下的代碼:
protected void CreateRoleButton_Click(object sender, EventArgs e)
 {     
string newRoleName = RoleName.Text.Trim();  
   if (!Roles.RoleExists(newRoleName))
          // Create the role
     Roles.CreateRole(newRoleName);
     RoleName.Text = string.Empty;
}

代碼首先將RoleName TextBox裏輸入的role名稱賦值給變量newRoleName,再用Roles類的RoleExists方法檢查系統裏是否已經存在該角色,如果不存在就用CreateRole方法創建該角色.最後清除輸入的RoleName.
注意:
     你可能很好奇,如果我們在RoleName TextBox裏不輸入內容將會怎麼樣呢?實際上如果傳給CreateRole的參數爲null或空,那麼將會拋出異常.同樣,如果你輸入的角色名裏包含逗號的話,也會拋出異常.因此我們要在頁面上添加一個驗證控件,我將這個驗證留給讀者當練習.

我們來創建一個名爲“Administrators”的角色。在ManageRoles.aspx頁面裏,在textbox裏鍵入“Administrators”(如圖3),再點擊“Create Role”按鈕.


圖3


如何?發生了頁面回傳,但沒有視覺效果提示我們已經成功創建角色了.我們將在第五步驟裏進行改進.現在在SecurityTutorials.mdf數據庫裏的aspnet_Roles表裏我們可以看到剛剛添加進的Administrators角色.

圖4

 

Step 5: Displaying the Roles in the System

讓我們對ManageRoles.aspx頁面進行擴充,將系統裏當前的角色羅列出來.爲此,在頁面添加一個GridView控件,其id爲RoleList,接下來,在後臺代碼裏添加一個名爲DisplayRolesInGrid的方法,代碼如下:
private void DisplayRolesInGrid()
{      RoleList.DataSource = Roles.GetAllRoles();    
       RoleList.DataBind();
}

Roles類的GetAllRoles方法將系統當前的所有用戶以字符串數組的形式返回,並綁定到GridView.爲了在頁面首次登錄時便進行綁定,我們需要在Page_Load事件處理器裏調用DisplayRolesInGrid方法.下面的代碼是在頁面初次登錄而不是頁面回傳的時候進行綁定.
protected void Page_Load(object sender, EventArgs e)
{      if (!Page.IsPostBack)
          DisplayRolesInGrid();
}

然後在瀏覽器裏進行測試,如圖5所示。你將看到一個標記爲“Item”的方格.該方格里僅僅有一行,就是我們在第四步驟裏添加的角色Administrators.


圖5


   當用GridView顯示數據的時候,我喜歡自己顯式的定義,而不喜歡使用GridView默認生成的界面.我們來對GridView的聲明代碼進行改動,以顯式的定義列.

 首先將GridView的AutoGenerateColumns屬性設置爲False,然後添加一個TemplateField,設其HeaderText屬性爲“Roles”, 再在ItemTemplate裏添加一個id爲RoleNameLabel的Label控件.將其Text屬性綁定爲Container.DataItem.

   這些屬性以及ItemTemplate包含的內容可以通過顯式聲明的方式或GridView的Fields對話框和Edit Templates界面來實現.要通過Fields對話框的話,點擊GridView的智能標籤,不要選中“Auto-generate fields” checkbox,以使AutoGenerateColumns屬性爲False,再往GridView添加一個TemplateField,設其HeaderText屬性爲“Role”.要定義ItemTemplate的內容,選擇Edit Templates”選項,拖一個Label控件到ItemTemplate,設其id爲RoleNameLabel,再設置其Text屬性綁定到Container.DataItem.

不管你是用的哪種方式,你的聲明代碼和下面的差不多:

<asp:GridView ID="RoleList" runat="server" AutoGenerateColumns="false">    
 <Columns>
          <asp:TemplateField HeaderText="Role">
               <ItemTemplate>
                    <asp:Label
runat="server" ID="RoleNameLabel" Text='<%# Container.DataItem %>' />
               </ItemTemplate>
          </asp:TemplateField>
     </Columns>
</asp:GridView>

注意:
    我們是通過綁定語法<%# Container.DataItem %>來將數組的內容顯示出來的.關於該語法的更深入的探討超出了本文的範疇,你可以參閱文章《Binding a Scalar Array to a Data Web Control》


   當前,RoleList GridView僅僅在頁面初次登錄時將角色信息羅列出來,我們需要在新添加角色時馬上對頁面進行刷新.爲此,對CreateRoleButton按鈕的Click事件處理器進行更新,當新角色添加時即調用DisplayRolesInGrid方法.

protected void CreateRoleButton_Click(object sender, EventArgs e)
 {    
   string newRoleName = RoleName.Text.Trim();
     if (!Roles.RoleExists(newRoleName))
     {
          // Create the role
          Roles.CreateRole(newRoleName);
          // Refresh the RoleList Grid
          DisplayRolesInGrid();
     }     
   RoleName.Text = string.Empty;
}

  這樣,當我們新添加角色信息時馬上就可以顯示出結果.來測試,來瀏覽器裏登錄ManageRoles.aspx頁面.添加一個名爲“Supervisors”的角色,點擊“Create Role”按鈕.頁面回傳,新添加的Administrators就顯示出來了.

 

圖6


Step 6: Deleting Roles

 顯示用戶可以添加新角色,並在ManageRoles.aspx裏顯示出來,讓我們再允許用戶刪除角色.而Roles.DeleteRole方法有2個重載:

.DeleteRole(roleName)——刪除角色roleName,不過如果該角色包含一個或更多的用戶,那麼將拋出一個異常.

.DeleteRole(roleName, throwOnPopulatedRole)——刪除角色roleName,如果throwOnPopulatedRole爲true,那麼當角色包含一個或多個用戶的話將拋出異常,如果throwOnPopulatedRole爲false,那麼不管角色是否包含用戶都刪除該角色.在內部,DeleteRole(roleName)方法實際調用的是DeleteRole(roleName, true).

  如果roleName爲null,或空字符串或roleName裏包含逗號,DeleteRole方法都將拋出異常.如系統裏沒有roleName這個角色,那麼DeleteRole方法將執行失敗而不會拋出異常.

    我們再對ManageRoles.aspx頁面進行擴充,以包含一個Delete按鈕,當點擊該按鈕時將刪除選中的角色.打開GridView的Fields對話框,添加一個Delete按鈕,將其DeleteText屬性設置爲“Delete Role”.


圖7


這樣你的GridView的聲明代碼和下面的差不多:

<asp:GridView ID="RoleList" runat="server" AutoGenerateColumns="False">
     <Columns>
          <asp:CommandField
DeleteText="Delete Role" ShowDeleteButton="True"/>
          <asp:TemplateField HeaderText="Role">
               <ItemTemplate>
                    <asp:Label
runat="server" ID="RoleNameLabel" Text='<%# Container.DataItem %>' />
               </ItemTemplate>
          </asp:TemplateField>
     </Columns>
 </asp:GridView>

再爲GridView的RowDeleting事件創建一個事件處理器,這是當點擊“Delete Role”按鈕時觸發的事件,如下:

protected void RoleList_RowDeleting(object sender, GridViewDeleteEventArgs e)
{      // Get the RoleNameLabel    
 
Label RoleNameLabel =
RoleList.Rows[e.RowIndex].FindControl("RoleNameLabel") as Label;
     // Delete the role
     Roles.DeleteRole(RoleNameLabel.Text, false);
     // Rebind the data to the RoleList grid
     DisplayRolesInGrid();
}

  當點擊某行的“Delete Role”暗示時,首先編程引用該行的id爲RoleNameLabel的控件,然後調用Roles.DeleteRole方法,將RoleNameLabel的Text值和false一起傳入,這樣不管該角色是否有用戶都將被刪除.最後刷新RoleList GridView,那麼剛纔刪除的角色就顯示不出來了.

注意:
    當點擊“Delete Role”按鈕時,在用戶刪除之前我們並沒有要求用戶確認.一種最簡單的辦法是“客戶端確認”.更多詳情請見《Adding Client-Side Confirmation When Deleting》.

結語:
    有了Roles framework,我們可以很容易的創建和管理roles. 本文我們考察瞭如何配置Roles framework以啓用SqlRoleProvider,它是使用一個Microsoft SQL Server來作爲role store.我們也創建了一個頁面來將系統現有的角色羅列出來,並允許創建新角色,刪除現有的角色.下一章我們將看如何將用戶分配給角色以及如何執行基於角色的授權.

祝編程愉快!

作者簡介:

  Scott Mitchell,著有七本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。Scott是個獨立的技術諮詢顧問,培訓師,作家,最近完成了將由Sams出版社出版的新作,《24小時內精通ASP.NET 2.0》。他的聯繫電郵爲[email protected],也可以通過他的博客http://ScottOnWriting.NET與他聯繫。
   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章