在Silverlight+WCF中应用以角色为基础的安全模式(一)基础篇之角色为基础的安全模式简介...
??? 引言
最近一直在研究Silverlight+WCF應(yīng)用中的安全問題,如何設(shè)計一個安全,又符合Silverlight和WCF的規(guī)范的應(yīng)用呢?又可以將以前的角色為基礎(chǔ)的開發(fā)框架拿來主義呢?
我們知道WCF在安全方面提供了很多的綁定協(xié)議,可是Silverlight3+WCF的話,只有basicHttpBinding可以使用,這就使得我們的選擇不多,還有就是項目本身是一個互聯(lián)網(wǎng)應(yīng)用,還是使用比較通用的角色為基礎(chǔ)的權(quán)限系統(tǒng)比較好。
這個系列有兩篇文章,一篇講解.NET框架提供我們的角色為基礎(chǔ)的安全模式,以及如何根據(jù)我們的需求,自定義角色為基礎(chǔ)的安全模式;一篇講解在Silverlight+WCF應(yīng)用中,如何設(shè)計的一種角色為基礎(chǔ)的應(yīng)用方法。
文中的代碼下載:/Files/virusswb/RetrieveSecurity_src.zip
正文
?
.NET中的角色為基礎(chǔ)的安全
.NET 框架使得 你在應(yīng)用中實現(xiàn)以角色為基礎(chǔ)的安全模式非常容易。迫使安全有兩部分組成,認(rèn)證和授權(quán)。認(rèn)證就是驗證你的身份。應(yīng)用程序驗證你就是你所聲明的人。通常的做法是用戶輸入用戶名和密碼,應(yīng)用查找你輸入的用戶名,然后驗證你輸入的密碼是否匹配。更高級的做法是依賴生物認(rèn)證,例如:指紋或者是視網(wǎng)膜,又或者是一張綁定了個人PIN碼的認(rèn)證卡。如果認(rèn)證失敗,用戶將不被允許進(jìn)入系統(tǒng),除非系統(tǒng)允許匿名訪問,意味著如果系統(tǒng)確認(rèn)了你的身份,就授予你訪問權(quán)。授權(quán)就是確認(rèn)用戶是否能操作系統(tǒng)的某項功能。授權(quán)依賴于已知的用戶身份以及和用戶相關(guān)的安全信息,基于這些安全信息,系統(tǒng)就可以批準(zhǔn)或者拒絕用戶的請求。
.NET框架提供了通過Identity訪問用戶信息,通過principal訪問授權(quán)信息。Thread.CurrentPrincipal提供了當(dāng)前線程的principal信息,默認(rèn)情況下,它是一個非認(rèn)證的授權(quán)。框架提供了兩種不同的principal,一個是windows principal,一個是通用的授權(quán)generic principal。Windows principal工作在windows 操作系統(tǒng)上。所以,當(dāng)前運(yùn)行的線程會映射到一個windows帳戶上。如果你正在運(yùn)行一個windows form的應(yīng)用程序,它就是一個用戶。
有兩個辦法可以訪問windows principal
// set that a principal should be attached to the thread and
// it should be a windows principal
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
// get hold of the windows principal
WindowsPrincipal MyPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;
// get the windows identity
WindowsIdentity MyIdentity = MyPrincipal.Identity;
?
通過調(diào)用當(dāng)前程序域的SetPrincipalPolicy,你告訴框架當(dāng)前線程需要附加的principal,你需要在第一次訪問principal之前做這些設(shè)置。調(diào)用Thread.CurrentPrincipal返回和當(dāng)前線程綁定的principal。第一次這么做的時候,框架會查詢windows的帳戶信息創(chuàng)建一個windows身份和一個windows授權(quán)并且綁定到這個線程。從windows principal你可以訪問windows identity。另一個辦法是
// get an identity object for the windows user
WindowsIdentity Identity = WindowsIdentity.GetCurrent();
// get hold of the windows principal
WindowsPrincipal MyPrincipal = new WindowsPrincipal(Identity);
WindowsIdentity.GetCurrent()查詢windows帳戶信息,同時創(chuàng)建一個identity代表當(dāng)前用戶,那樣你可以用這個identity創(chuàng)建一個principal。這 樣做的缺點(diǎn)就是每次都需要查詢windows帳戶,然后創(chuàng)建一個identity和一個principal。第一種方法每次都會使用相同的identity和principal。通用的principal允許你創(chuàng)建不綁定任何windows帳戶的一個principal和identity。
// create the generic identity GenericIdentity
Identity = new GenericIdentity("Administrator");
// define the roles to associate with the generic principal
string[] Roles = new string[2] { "Manager", "Architect" };
// create the generic principal
GenericPrincipal MyPrincipal = new GenericPrincipal(Identity,Roles);
// bind the generic principal to the thread
Thread.CurrentPrincipal = MyPrincipal;
首先創(chuàng)建一個通用的identity,你需要提供identity的名稱,因為他不綁定任何windows帳戶,需要一個用戶名。然后定義你想要這個授權(quán)擁有的角色,最后創(chuàng)建一個principal,然后提供identity和角色列表。然后你可以將這個授權(quán)綁定到當(dāng)前線程。
創(chuàng)建自定義的授權(quán)principal和認(rèn)證identity
.NET框架允許通過實現(xiàn)IPrincipal和IIdentity接口,來自定義授權(quán)和認(rèn)證。本文下面的代碼,將展示如何創(chuàng)建一個數(shù)據(jù)庫驅(qū)動的認(rèn)證和授權(quán)。
授權(quán)過程在用戶表中檢查提供的用戶名和密碼。授權(quán)成功之后,讀取用戶信息和用戶的安全組信息,查看用戶屬于那些安全組。
這些信息對于自定義認(rèn)證和授權(quán)都是必要的。但是授權(quán)還可以更進(jìn)一步,還可以檢查個人權(quán)限信息,例如:用戶是否被允許查看預(yù)算等。這些信息都是從SecurityRightAssign表中讀取出來,讓我們先創(chuàng)建一個自定義身份。
public class UserIdentity : IIdentity
{
// the authentication type for us is always database
private static string AuthenticationTypeString = "Database";
// hash table with all the user info we have
private Hashtable UserInfo;
// create the user identity; all user information is in the hashtable passed along
private UserIdentity(Hashtable UserInfo)
{
????? this.UserInfo = UserInfo;
}
//create a user identity and return it to the caller
public static UserIdentity CreateUserIdentity(Hashtable UserInfo)
{
????? return new UserIdentity(UserInfo);
}
}
?
UserIdentity實現(xiàn)了IIdentity接口,需要我們事先三個屬性。類型的構(gòu)造函數(shù)被私有化,防止通過實例化來構(gòu)造對象。你需要通過靜態(tài)方法CreateUserIdentity,傳遞一個HashTable結(jié)構(gòu)的用戶類型,然后創(chuàng)建一個身份的實例。Name屬性返回這個身份的名稱。
// returns the name of the identity
public string Name
{
? get
? {
??? return
??? Convert.ToString(UserInfo[UserNameKey],CultureInfo.InvariantCulture).Trim();
? }
}
// returns if identity is authenticated or not
public bool IsAuthenticated
{
? get
? {
??? return true;
? }
}
// the type of authentication
public string AuthenticationType
{
? get
? {
??? return AuthenticationTypeString;
? }
}
IsAuthenticated屬性返回用戶是否被認(rèn)證通過,在上面的代碼中用戶總是被認(rèn)證功過,因為我們return true。如果你允許匿名訪問,你就可以為匿名用戶設(shè)置為false。最后一個屬性AuthenticationType返回的是驗證的類型,在我們的代碼中返回的是“Database”。WindowsIdentity返回的是NTLM,GenericIdentity返回的是空字符串或者是實例化GenericIdentity的時候傳遞的驗證類型。下面,我們里實現(xiàn)自定義的principal。
public class SecurityPrincipal : IPrincipal
{
// stores the list of security rights the user belongs too
private Hashtable SecurityGroups;
// stores the list of security rights the user has
private Hashtable SecurityRights;
// the user identity we create and associate with this principal
private UserIdentity TheUserIdentity;
// constructor: stores role and permission info and creates custom identity
private SecurityPrincipal(Hashtable SecurityGroups, Hashtable SecurityRights,
????????????????? Hashtable UserInfo)
{
????? this.SecurityGroups = SecurityGroups;
????? this.SecurityRights = SecurityRights;
????? // creates the IIdentity for the user and associates it with this IPrincipal
????? TheUserIdentity = UserIdentity.CreateUserIdentity(UserInfo);
}
// create the security principal and return it to the caller
public static SecurityPrincipal CreateSecurityPrincipal(Hashtable SecurityGroups,
????????????????? Hashtable SecurityRights, Hashtable UserInfo)
{
????? return new SecurityPrincipal(SecurityGroups,SecurityRights,UserInfo);
}
}
實現(xiàn)IPrincipal接口需要實現(xiàn)Identity屬性和IsInRole()方法,同樣的這個類型的構(gòu)造函數(shù)也是私有的,防止通過實例化來創(chuàng)建對象。你需要調(diào)用靜態(tài)方法CreateSecurityPrincipal,傳遞一個hashtable類型的用戶信息,一個用戶所屬的角色信息,還有就是用戶在系統(tǒng)中的特權(quán)。這個類型的構(gòu)造函數(shù)調(diào)用自定義的Identity方法的靜態(tài)函數(shù)CreateUserIdentity,將用戶信息傳遞給CreateUserIdentity方法,然后返回一個UserIdentity。CreateSecurityPrincipal方法返回一個自定義的principal實例。Identity屬性返回和這個principal相關(guān)聯(lián)的identity。
// returns the Identity object associated with the principal
public IIdentity Identity
{
????? get
????? {
??????????? return TheUserIdentity;
????? }
}
// checks if user belongs to role
public bool IsInRole(string Role)
{
????? return SecurityGroups.ContainsValue(Role);
}
// checks if user has permission
public bool HasPermission(string Permission)
{
????? return SecurityRights.ContainsValue(Permission);
}
IsInRole方法檢查用戶是否屬于角色,是通過檢查角色是否在hashtable類型的SecurityGroups中,然后返回true 或者false。我們自定義的principal還實現(xiàn)了一個方法HasPermission,它和IsInRole方法類似,但是檢查的是提供的權(quán)限是否在特權(quán)列表中,然后返回true或者false。
這些已經(jīng)實現(xiàn)了自定義的identity和principal,下面的代碼解釋了信息是如何從數(shù)據(jù)庫中讀取,最后要做的就是去使用它。
public static IPrincipal SetSecurityPrincipal(Hashtable SecurityGroups,
?????????????????????????????? Hashtable SecurityRights, Hashtable UserInfo)
{
// set that we want to use authentication within the current app-domain;
// this means a thread will have a IPrincipal associated which is then
// used by the .NET security classes when checking role based security
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
// we switch to the new security principal only if we didn't do so already;
// protects us from the client calling the method multiple times
if (!(Thread.CurrentPrincipal is SecurityPrincipal))
{
? // create a new instance of the security principal which we can do only
? // within a class member as we marked the constructor private
? SecurityPrincipal TheSecurityPrincipal = new SecurityPrincipal(SecurityGroups,
????????????????? SecurityRights,UserInfo);
? // get a reference to the current security principal so the caller
? //can keep hold of it
? IPrincipal CurrentSecurityPrincipal = Thread.CurrentPrincipal;
? // set the security principal for the executing thread to the newly created one
? Thread.CurrentPrincipal = TheSecurityPrincipal;
? // return the current security principal;
? return CurrentSecurityPrincipal;
}
// return null if we don't switch the security principal
else
????? return null;
}
為了使用,我們在SecurityPrincipal類型上提供了一個靜態(tài)方法SetSecurityPrincipal。首先調(diào)用AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
這樣做看起來是錯誤的,因為我們想要的不是一個windows principal,而是一個自定義的principal。這么做只是為了保證我們擁有一個綁定到當(dāng)前線程的principal,然后我們檢查綁定到當(dāng)前線程的principal是否是自定義的principal類型。如果是的話,我們什么都不需要做,因為我們已經(jīng)為當(dāng)前線程分配了我們自定義的principal,這確保了調(diào)用者在多線程的環(huán)境中調(diào)用不會產(chǎn)生負(fù)面的問題。只是在第一次我們會發(fā)現(xiàn)沒有綁定到自定義的principal,這時候我們創(chuàng)建自定義的principal,創(chuàng)建自定義的identity,并且綁定到當(dāng)前線程。如果調(diào)用者需要的話,我們返回當(dāng)前principal給他。
在使用Thread.CurrentPrincipal訪問用戶信息的時候,會檢查用戶的角色和特權(quán),這些都可以通過在principal上調(diào)用IsInRole或者是訪問identity來實現(xiàn)。如果你想檢查用戶的特權(quán),你可以使用從Thread.CurrentPrincipal中獲取的principal的HasPermission方法來實現(xiàn)。
public bool CheckSecurityPermission(string Permission)
{
????? // if the current IPrincipal is of the same type as our custom
????? // security principal then go and check the security right
????? if (Thread.CurrentPrincipal is SecurityPrincipal)
????? {
????????? SecurityPrincipal Principal = (SecurityPrincipal)
??????????????????????????????????????? Thread.CurrentPrincipal;
????????? // returns whether the user has the permission or not
????????? return Principal.HasPermission(Permission);
????? }
????? // if we have a standard IPrincipal in use then we can not check
????? // the permission and we always return false
????? else
????????? return false;
}
如果你正在創(chuàng)建一個新的應(yīng)用程序域線程,你不想為每一個線程設(shè)置自定義的principal,你可以為每一個新創(chuàng)建的線程創(chuàng)建一個默認(rèn)的principal。設(shè)置默認(rèn)principal一定要在第一次第一次訪問principal之前設(shè)置Thread.CurrentPrincipal。
// create the custom principal
SecurityPrincipal MyPrincipal = SecurityPrincipal.CreateSecurityPrincipal(
????????????????? AppDomain.CurrentDomain.SetThreadPrincipal);
// set the custom principal as the app domain policy
AppDomain.CurrentDomain.SetThreadPrincipal(MyPrincipal);
你設(shè)置默認(rèn)principal,只需要在應(yīng)用程序域設(shè)置一次。在應(yīng)用程序域設(shè)置多次會引發(fā)PolicyException異常。
?
示例代碼
?
示例代碼演示的是一個windows form程序,首先需要用戶登錄(在數(shù)據(jù)庫中已經(jīng)有兩個用戶,virus和swb,密碼和用戶名一致)。btnLogon_Click()事件和btnLogin按鈕關(guān)聯(lián),調(diào)用DataLayer.CheckUserNameAndPassword().用來驗證用戶,調(diào)用DataLayer.RetrieveUserInformation().來獲取用戶信息,最后通過調(diào)用DataLayer.RetrieveSecurityInformation().來獲取用戶所屬的角色和權(quán)限信息,在獲取了用戶信息、角色信息和權(quán)限信息之后,使用SecurityPrincipal.SetSecurityPrincipal()創(chuàng)建一個principal和identity,并且綁定到當(dāng)前線程。
從示例中看出用戶屬于是三個角色,全部的權(quán)限,和用戶信息,可以檢查用戶是否屬于某一個角色,是否具有某一個權(quán)限,CheckSecurityRoles() and CheckSecurityPermissions()返回用戶是否屬于一個角色,是否有一個權(quán)限。logoff 按鈕的 LogOff_Click()方法恢復(fù)原始的principal,并且返回登陸界面,允許另外一個用戶登錄,繼續(xù)前面的處理過程。
在示例文件夾中你會發(fā)現(xiàn)一個叫做RetrieveSecurity.bak的文件,它是數(shù)據(jù)庫的備份文件。恢復(fù)數(shù)據(jù)庫,配置app.config文件中的連接字符串。你可以在數(shù)據(jù)庫中添加用戶、角色和權(quán)限信息。示例展示了在.NET 的角色為基礎(chǔ)的安全模型之后,如何實現(xiàn)數(shù)據(jù)庫驅(qū)動的驗證和安全模型。
?
總結(jié)
大多數(shù)應(yīng)用都需要通過角色和權(quán)限來實現(xiàn)用戶驗證和安全模型。.NET框架使得這些變得容易,幾行代碼,就改變了windows賬號和安全組的影響。使用自定義的identity和principal可以很容易的擴(kuò)展角色為基礎(chǔ)的安全框架,示例代碼展示的就是如何實現(xiàn)數(shù)據(jù)庫驅(qū)動的角色權(quán)限系統(tǒng)。
參考文獻(xiàn)
【1】Role-Based Security? Microsoft
【2】Introduction to Role-Based Security in .NET? Klaus Salchner
【3】在Identity 增加自己的屬性 部門,并且使用access mdb文件實現(xiàn)角色驗證? iHqq
?
感謝上面這些機(jī)構(gòu)和作者的無私奉獻(xiàn)。
轉(zhuǎn)載于:https://www.cnblogs.com/virusswb/archive/2010/03/01/1675414.html
總結(jié)
以上是生活随笔為你收集整理的在Silverlight+WCF中应用以角色为基础的安全模式(一)基础篇之角色为基础的安全模式简介...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 赶走最令人不愉悦的一类BUG,你准备好了
- 下一篇: 与客户“调情”