| Download the code for this article: RollSec.exe (161KB) |
| SUMMARY Role-based security allows administrators to assign access permissions to users based on the roles they play rather than on their individual identities. These privileges can be used to control access to objects and methods, and are easier to identify and maintain than user-based security. The .NET Framework provides two role-based security models, which are exposed as two namespaces: System.Enterprise-Services and System.Security.Permissions. |
Role-based Security Models using System.Security.Permissions;
public class BankAccount
{
[PrincipalPermission(SecurityAction.Demand,Role="Teller")]
public long OpenAccount(){...}
[PrincipalPermission(SecurityAction.Demand,Role="Customer")]
[PrincipalPermission(SecurityAction.Demand,Role="Teller")]
public long GetBalance(){...}
/* Rest of the implementation */
}
using System.EnterpriseServices;
public class BankAccount : ServicedComponent
{
[SecurityRole("Teller")]
public long OpenAccount(){...}
[SecurityRole("Customer")]
[SecurityRole("Teller")]
public long GetBalance(){...}
/* Rest of the implementation */
}
[assembly: SecurityRole("Teller")]
[assembly: SecurityRole("Customer")]
Then use the Component Services Explorer to add users (or user groups) to these roles. Figure 1 shows the Bank App application with the Teller and Customer roles.Figure 1 Teller and Customer Roles Comparing the Two Models Solution Architecture public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
IPrincipal principal = Thread.CurrentPrincipal; public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
static void Main()
{
UnifiedPrincipal.SetModel(SecurityRoleModel.EnterpriseServices);
/* Rest of Main() */
}
But even if you develop a class library, you can call SetModel in your component's code. As you will see later on, there is no harm in calling SetModel multiple times. In addition, neither the client nor the server assemblies need to be part of an Enterprise Services application or contain serviced components.Applications and Assemblies [assembly: ApplicationName("MyApp")]
If no name is provided, .NET uses the assembly name for the application name.static public void SetModel(SecurityRoleModel model)
{
Assembly callingAssembly = Assembly.GetCallingAssembly();
string appName = GetAppNameFromAssembly(callingAssembly);
SetModel(appName,model);
}
Installing a Custom Security Principalstatic public void SetModel(string appName,SecurityRoleModel model)
{
UnifiedPrincipal principal = new UnifiedPrincipal(model);
principal.AppName = appName;
}
AppDomain currentDomain = Thread.GetDomain(); currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);You get your current app domain using the Thread class's GetDomain static property. Next, the constructor checks the constructor argument梩he security model. If it's SecurityRoleModel.WindowsGroups, then there is nothing to unify, and the default .NET behavior is just fine, so the constructor simply exits. Thread.CurrentPrincipal = this; if(m_DefaultPrincipal is UnifiedPrincipal == false)
{
currentDomain.SetThreadPrincipal(this);
}
Role Membership Lookupprotected bool IsInWindowsGroup(string group)
{
return m_DefaultPrincipal.IsInRole(group);
}
and this is why the constructor saved the default principal.Figure 6 Navigating the Component Services Catalog Wrap-up |
|
For related articles see: Security in .NET: Enforce Code Access Rights with the Common Language Runtime Security Briefs: Managed Security Context in ASP.NET http://www.discuss.develop.com/dotnet.html For background information see: Version 1 Security Changes for the Microsoft .NET Framework |
|
Juval Lowy is a seasoned software architect providing consulting and training on .NET design and .NET migration. This article contains adaptations from his book COM and .NET Component Services (O'Reilly, 2001). Contact Juval at http://www.idesign.net. |
Figure 2 UnifiedPrincipal Public Members public enum SecurityRoleModel
{
WindowsGroups,
EnterpriseServices,
Either,
Both
}
public class UnifiedPrincipal : IPrincipal
{
static public void SetModel(){}
static public void SetModel(string appName){}
static public void SetModel(SecurityRoleModel model){}
static public void SetModel(string appName,SecurityRoleModel model){}
//IPrincipal methods:
public IIdentity Identity { get; }
public bool IsInRole(string role);
/* Rest of class definition: protected methods and members */
}
Figure 3 The GetAppNameFromAssembly Method public static string GetAppNameFromAssembly(Assembly assembly)
{
Type AttributeType = typeof(ApplicationNameAttribute);
object[] objArray = assembly.GetCustomAttributes(AttributeType,true);
//One ApplicationName attribute is allowed at most
Debug.Assert(objArray.Length == 1 || objArray.Length == 0);
if(objArray.Length == 0)
{
//In the absence of ApplicationName attribute, assembly name is
//used
AssemblyName assemblyName = assembly.GetName();
return assemblyName.Name;
}
ApplicationNameAttribute appNameAttribute;
appNameAttribute = (ApplicationNameAttribute)objArray[0];
return appNameAttribute.Value;
}
Figure 4 Installing the Custom Principal public class UnifiedPrincipal : IPrincipal
{
protected string m_AppName;
protected IIdentity m_Identity;
protected IPrincipal m_DefaultPrincipal;
protected SecurityRoleModel m_Model;
protected UnifiedPrincipal(SecurityRoleModel model)
{
AppDomain currentDomain = Thread.GetDomain();
currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
if(model == SecurityRoleModel.WindowsGroups)
{
return;// Don't do anything, default is fine
}
m_Model = model;
m_AppName = "";
//Save old principal
m_DefaultPrincipal = Thread.CurrentPrincipal;
//use current identity
m_Identity = Thread.CurrentPrincipal.Identity;
//Make us the principal for this thread
Thread.CurrentPrincipal = this;
//Make sure all future threads in this app domain use this
//principal but because default principal cannot be set twice:
if(m_DefaultPrincipal is UnifiedPrincipal == false)
{
currentDomain.SetThreadPrincipal(this);
}
}
/* Rest of the class definition */
}
Figure 5 Implementing IsInRole public bool IsInRole(string role)
{
switch(m_Model)
{
case SecurityRoleModel.Either:
{
return IsInWindowsGroup(role) || IsInEnterpriseServicesRole(role);
}
case SecurityRoleModel.EnterpriseServices:
{
return IsInEnterpriseServicesRole(role);
}
case SecurityRoleModel.Both:
{
return IsInWindowsGroup(role) && IsInEnterpriseServicesRole(role);
}
default:
{
Debug.Assert(false);
return false;
}
}
}
Figure 7 Implementing IsInEnterpriseServicesRole using COMAdmin;
public class UnifiedPrincipal : IPrincipal
{
protected string m_AppName;
protected IIdentity m_Identity;
protected SecurityRoleModel m_Model;
protected bool IsInEnterpriseServicesRole(string role)
{
bool inRole = false;
string userName = m_Identity.Name;
//Find application
ICOMAdminCatalog catalog;
ICatalogCollection applicationCollection;
ICatalogObject application = null;
int applicationCount;
int appIndex = 0;
catalog = (ICOMAdminCatalog)new COMAdminCatalog();
applicationCollection =
(ICatalogCollection)catalog.GetCollection("Applications");
//Read the information from the catalog
applicationCollection.Populate();
applicationCount = applicationCollection.Count;
string tempName ="";
while(tempName != m_AppName && appIndex < applicationCount)
{
//Get the current application
application= (ICatalogObject)applicationCollection.get_Item
(appIndex++);
tempName = application.Name.ToString();
}
object appKey = application.Key;
//Get Roles collection
ICatalogCollection roleCollection;
roleCollection =
(ICatalogCollection)applicationCollection.GetCollection("Roles",appKey);
roleCollection.Populate();
int roleIndex = 0;
while(inRole == false && roleIndex <roleCollection.Count)
{
//Get individual role
ICatalogObject roleObj;
roleObj = (ICatalogObject)roleCollection.get_Item(roleIndex);
if(roleObj.Name.ToString() != role)
{
roleIndex++;
continue;
}
//Role name match. get users collection, and check each user
object roleKey = roleObj.Key;
ICatalogCollection userCollection;
userCollection = (ICatalogCollection)roleCollection.GetCollection
("UsersInRole",roleKey);
userCollection.Populate();
int userIndex = 0;
while(inRole == false && userIndex <userCollection.Count)
{
//Get individual user object
ICatalogObject user;
user = (ICatalogObject)userCollection.get_Item(userIndex);
//for each user, get users name, and compare
if (userName == user.Name.ToString())
{
inRole = true;
break;
}
//User in a role can actually be a user group. Check membership
//by using generic principal, that considers user group as
//"role"
inRole = IsInWindowsGroup(user.Name.ToString());
userIndex++;
}
roleIndex++;
}
return inRole;
}
/* Rest of the class definition */
}
|
博客围绕.NET相关内容,涉及应用安全、对象构造等信息技术要点。涵盖了如应用程序的安全保障,以及对象构造函数等方面知识,为.NET开发提供相关参考。

10万+

被折叠的 条评论
为什么被折叠?



