使用哪种设计模式将允许的操作分配给用户类型?

时间:2011-11-09 22:35:16

标签: c# .net design-patterns access-control

我正在使用C#(.NET 4.0)开发一个系统,该系统将被各种用户使用。

每个用户类型只允许以特定方式与应用程序交互。例如,用户A应该只能执行操作1,2和3.用户B应该只能执行操作2,3和4.等等。

我决定实现一个名为“Role”的接口,该接口由各种类实现,例如“Administrator”,“Reader”等。用户可能有多个角色(定义为“角色”列表)。 / p>

我希望每个角色(“管理员”,“读者”等)都有一个允许的操作列表。我想在某个地方定义一个enum,它将包含应用程序中可能的所有操作。然后,每个“角色”都会有一个在该枚举中找到的项目列表。

我发现这个相当不优化,因为我必须始终确保在我的应用程序中实现新功能时保持此枚举最新。

您建议我使用哪种方法?有没有可以帮助我的设计模式?

4 个答案:

答案 0 :(得分:4)

我喜欢这个问题,这是一个很好的问题。每当我遇到这样的问题时,我总是开始考虑接口,操作以及它们如何协同工作。通常一两个工厂突然决定在最终解决方案中需要感受它的存在。那么让我们来看看你的问题所包含的一些接口。

首先,我们有一个角色。角色可以是任何东西,并且完全针对特定业务,如您的问题所述。所以,角色实际上可以很简单:

public interface IRole
{
    public string Name { get; }
}

(你可能会说,我保持这很简单)。这真的是一个角色。一个标题。您可能认为只是为了拥有一个具有name参数的接口,但是角色可能会在以后变得更加复杂。

好的,我们已经定义了一个角色的抽象概念。我想我们将会有一些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

现在,我们有一个角色的定义,一个具体的User类以及向特定用户添加和撤销角色的能力。是时候定义一些具体的角色了:

public class AdminRole : IRole
{
    public string Name { get { return "Admin"; } }
}

public class ReaderRole : IRole
{
    public string Name { get { return "Reader"; } }
}

我们唯一真正缺少的是可以执行的操作。所以,让我们定义一个新的界面:

public interface IAction
{
    string Name { get; }
    void Perform();
}

这就像动作一样简单。我们会做一个虚拟的:

public class InvoiceAction : IAction
{
     public string Name { get { return "Invoicing"; } }
     public void Perform()
     {
         /* Invoice somebody! */
     }

     public InvoiceAction(/* Some pertinent details*) { }
}

是的,我们已经有了基本的接口和具体的类。我们现在需要提出一种行为可以与角色相关联的方式。出于本示例的目的,我将使用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

好的,这让我们做了什么?它允许我们使用我们的RoleValidation类将操作与角色相关联....

RoleValidation rv = new RoleValidation();
rv.AssignActionToRole<Administrator>(new InvoiceAction());
rv.AssignActionToRole<Reader>(new OtherAction());

我意识到可能存在重复的操作,这留给读者一个练习,因为你可以创建一个动作工厂并在这里使用两个泛型,例如:

cv.AssignActionToRole<Administrator, InvoiceAction>();

基本上有一个类型的地图,而不是具体的实例。这将是我首选的做事方式。

现在它是在我们的用户类中实现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;
    }
}

我承认,我已经把这个答案放在一起而没有实际编译并构建一个工作示例,所以如果有错误我会道歉。这是您可以采取的一种方法,用于解决此问题并具有动态操作,分配给任意角色和具有多个角色的用户。如果我们认为角色可能来自彼此,那可能会变得更有趣,但这是一个不同的故事:)

答案 1 :(得分:2)

查看代理模式。这应该指导您如何处理这种情况:http://en.wikipedia.org/wiki/Proxy_pattern

  

保护代理控制对敏感主对象的访问。该   “surrogate”对象检查调用者是否具有访问权限   在转发请求之前需要。

http://sourcemaking.com/design_patterns/proxy

答案 2 :(得分:1)

也许像Aspect Oriented Programming这样的模式会有所帮助。如果您使用Unity之类的东西,这只是建立在代理模式上。也就是说,做这项工作的班级不应该知道谁有权限,这是标准的关注点分离。因此,继承自界面提供角色听起来就像是一个糟糕的设计。

我会调查角色提供程序来处理角色。一个好的设计将允许用户填写多个角色,无论是管理员,读者等等......

答案 3 :(得分:0)

州的设计模式怎么样?在您根据当前状态更改状态后,每种类型的用户都将生成自己的第一个菜单状态,如工厂。