Hus*_*lil 5 .net c# design-patterns access-control
我正在开发一个C#(.NET 4.0)系统,它将被各种用户使用.
每个用户类型只允许以特定方式与应用程序交互.例如,用户A应该只能执行操作1,2和3.用户B应该只能执行操作2,3和4等.
我决定实现一个名为"Role"的接口,它由各种类实现,例如"Administrator","Reader"等.用户可能有多个角色(定义为"角色"列表).
我希望每个角色("管理员","读者"等)都有一个允许的操作列表.我想在某个地方定义一个enum,它将包含应用程序中可能的所有操作.然后,每个"角色"将具有在该枚举中找到的项目列表.
我觉得这个没有经过优化,因为我必须始终确保在我的应用程序中实现新功能时保持这个enum的最新状态.
你会建议我使用什么方法?有没有可以帮助我的设计模式?
我喜欢这个问题,这是一个很好的问题。每当我遇到这样的问题时,我总是开始考虑接口、动作以及它们如何协同工作。通常,一两个工厂会突然决定在最终解决方案中需要感受到它的存在。那么让我们来看看您的问题包含的一些接口。
首先,我们有一个角色。正如您的问题所述,角色可以是任何内容,并且完全特定于业务。所以,这个角色实际上可以很简单:
public interface IRole
{
public string Name { get; }
}
Run Code Online (Sandbox Code Playgroud)
(正如你可能会说的,我保持这个非常简单)。这就是一个角色,真的。标题。您可能认为仅仅拥有一个具有名称参数的接口是过分的,但角色在稍后可能会变得更加复杂。
好的,我们已经定义了一个角色的抽象概念。我想我们会有某种User
被赋予一系列角色的类。这是一个简单的:
public class User
{
private List<IRole> roles_ = new List<IRole>();
/* .. usual suspects */
public void AddRole(IRole role)
{
Debug.Assert(role != null);
Debug.Assert(!HasRole(role));
roles_.Add(role);
}
public void RevokeRole(IRole role)
{
Debug.Assert(role != null);
roles_.Remove(role);
}
public bool HasRole(IRole role)
{
Debug.Assert(role);
return roles_.Contains(role);
}
public bool CanPerform(IAction action) /* to come */
} // eo class User
Run Code Online (Sandbox Code Playgroud)
现在,我们有一个角色的定义、一个具体的 User 类以及向特定用户添加和撤销角色的能力。是时候定义一些具体的角色了:
public class AdminRole : IRole
{
public string Name { get { return "Admin"; } }
}
public class ReaderRole : IRole
{
public string Name { get { return "Reader"; } }
}
Run Code Online (Sandbox Code Playgroud)
我们唯一真正缺少的是可以执行的操作。所以,让我们定义一个新的接口:
public interface IAction
{
string Name { get; }
void Perform();
}
Run Code Online (Sandbox Code Playgroud)
这与操作一样简单。我们将制作一个虚拟的:
public class InvoiceAction : IAction
{
public string Name { get { return "Invoicing"; } }
public void Perform()
{
/* Invoice somebody! */
}
public InvoiceAction(/* Some pertinent details*) { }
}
Run Code Online (Sandbox Code Playgroud)
是的,我们已经准备好了基本的接口和具体的类。我们现在需要想出一种方法可以将动作与角色相关联。出于本示例的目的,我将使用一个RoleValidation
类。你可能会想到一个比这更好的名字。
public sealed class RoleValidation
{
private Dictionary<System.Type, List<IAction>> roleMap_ = new Dictionary<System.Type, List<IAction>>(); // this is our mapping of roles to actions
// ctor ignored for brevity
public void AssignActionToRole<ROLE>(IAction action)
{
Debug.Assert(action != null);
if(roleMap_.ContainsKey(typeof(ROLE)))
roleMap_[typeof(ROLE)].Add(action);
else
{
List<IAction> actions = new List<IAction>();
actions.Add(action);
roleMap_[typeof(ROLE)] = actions;
}
}
// Nice generic function for ease of use
public List<IAction> GetActions<ROLE>() where ROLE : IRole
{
foreach(KeyValuePair<System.Type, IAction> pair in roleMap_)
{
if(typeof(ROLE).IsAssignableFrom(pair.Key.GetType()))
return pair.Value;
}
return null;
}
// for everyone else, non-explicit
public List<IAction> GetActions(System.Type type)
{
foreach(KeyValuePair<System.Type, IAction> pair in roleMap_)
{
if(type.IsAssignableFrom(pair.Key.GetType()))
return pair.Value;
}
return null;
}
} // eo class RoleValidation
Run Code Online (Sandbox Code Playgroud)
好的,这允许我们做什么?它允许我们使用 RoleValidation 类将动作与角色相关联......
RoleValidation rv = new RoleValidation();
rv.AssignActionToRole<Administrator>(new InvoiceAction());
rv.AssignActionToRole<Reader>(new OtherAction());
Run Code Online (Sandbox Code Playgroud)
我意识到可能存在重复的动作,这留给读者作为练习,因为您可能会创建一个动作工厂并在这里使用两个泛型,例如:
cv.AssignActionToRole<Administrator, InvoiceAction>();
Run Code Online (Sandbox Code Playgroud)
并且基本上有一个类型的映射,而不是具体的实例。那将是我首选的做事方式。
现在是CanPerform
在我们的用户类中实现该函数的类型:
public class User
{
/* you know the rest */
public bool CanPerform(IAction action)
{
/* code assumes RoleValidation is available via some method or whatever */
foreach(IRole role in roles_)
{
if(RoleValidation.Instance.GetActions(typeof(role)).Contains(action))
return true;
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
我承认,我在没有实际编译和构建工作示例的情况下将这个答案放在一起,所以如果有错误,我深表歉意。这是您可以用来解决此问题并具有动态操作的一种方法,分配给任意角色和具有多个角色的用户。如果我们考虑角色可能从另一个角色衍生出来,它会变得更有趣,但那是另一回事了:)