处理MVC中的多个角色 - 基于操作的辅助功能

Rio*_*ams 11 c# asp.net dictionary roles asp.net-mvc-2

我目前有一个项目,我似乎遇到了关于角色的问题,并认为我会就如何最好地处理问题得到一些意见.

系统将需要可编辑,灵活的角色,不仅可以控制特定区域的访问,还可以控制系统功能的使用(添加用户,编辑用户,查看报告等)

系统当前允许用户拥有多个角色,每个角色都明确定义了访问/操作区域,例如:

  • 角色A可以访问区域1,2,3并且可以添加用户.
  • 角色B可以访问区域1,5,7并且可以修改用户.
  • 角色C可以访问区域4,6并且只能访问查看用户.

所以用户可以在角色A和C中,因此访问:1,2,3,4和6,并且可以添加和查看用户.

我的第一个解决方案是创建一个字典,将所有可能的访问/访问选项区域存储到字典中,如下所示:

Dictionary<string,bool>
Run Code Online (Sandbox Code Playgroud)

然后,当它被实例化时,它从数据库中提取所有属性,然后遍历角色以确定它们是否可访问.

所有这一切目前都运行得很好 - 但是项目非常Javascript/jQuery密集,因此很多这些选项都是由客户端函数调用的.我试图避免必须包含所有这些客户端功能:

<%if(AccessDictionary[key])
     //Enable or Disable Action
<%}%>
Run Code Online (Sandbox Code Playgroud)

所以基本上,我想知道以下事情:

  1. 用户登录后,存储此词典的最佳方法是什么?静态?在会议中?
  2. 什么是最好的存储方法,以便在视图中轻松访问字典?(因为我目前看不到包装我的客户端功能)

任何建议或想法将不胜感激!

Dar*_*rov 20

我会将此信息存储在身份验证cookie的用户数据部分中.所以当用户登录时:

public ActionResult Login(string username, string password)
{
    // TODO: validate username/password couple and 
    // if they are valid get the roles for the user

    var roles = "RoleA|RoleC";
    var ticket = new FormsAuthenticationTicket(
        1, 
        username,
        DateTime.Now, 
        DateTime.Now.AddMilliseconds(FormsAuthentication.Timeout.TotalMilliseconds), 
        false, 
        roles
    );
    var encryptedTicket = FormsAuthentication.Encrypt(ticket);
    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
    {
        // IIRC this property is only available in .NET 4.0,
        // so you might need a constant here to match the domain property
        // in the <forms> tag of the web.config
        Domain = FormsAuthentication.CookieDomain,
        HttpOnly = true,
        Secure = FormsAuthentication.RequireSSL,
    };
    Response.AppendCookie(authCookie);
    return RedirectToAction("SomeSecureAction");
}
Run Code Online (Sandbox Code Playgroud)

然后我将编写一个自定义authroize属性,该属性将负责读取和解析身份验证票证,并将通用用户存储在HttpContext.User属性及其相应的角色中:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext.User.Identity.IsAuthenticated)
        {
            var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
            if (authCookie != null)
            {
                var ticket = FormsAuthentication.Decrypt(authCookie.Value);
                var roles = ticket.UserData.Split('|');
                var identity = new GenericIdentity(ticket.Name);
                httpContext.User = new GenericPrincipal(identity, roles);
            }
        }
        return base.AuthorizeCore(httpContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,您可以使用此属性修饰控制器/操作以处理授权:

// Only users that have RoleA or RoleB can access this action
// Note that this works only with OR => that's how the base
// authorize attribute is implemented. If you need to handle AND
// you will need to completely short-circuit the base method call
// in your custom authroize attribute and simply handle this
// case manually
[MyAuthorize(Roles = "RoleA,RoleB")]
public ActionResult Foo()
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

为了检查用户是否处于给定角色,只需:

bool isInRole = User.IsInRole("RoleC");
Run Code Online (Sandbox Code Playgroud)

有了这些信息,您现在可以开始考虑如何组织视图模型.在这些视图模型我将包括布尔属性,如CanEdit,CanViewReport......这将是由控制器来填充.

现在,如果您需要在每个操作和视图中使用此映射,那么事情可能会变得重复和枯燥.这是全局自定义动作过滤器发挥作用的地方(它们实际上并不存在于ASP.NET MVC 2中,仅存在于ASP.NET MVC 3中,因此您可能需要使用此动作过滤器修饰的基本控制器,其模拟大致相同功能).你只需定义这样的全局动作过滤器,它在每个动作之后执行,并向ViewData注入一些常见的视图模型(神圣....,不能相信我发音那些词),从而使它可用于横向的所有视图其他行动方式.

最后,在视图中,您将检查这些布尔值属性,以便包含或不包含站点的不同区域.就javascript代码而言,如果它是不显眼的AJAX化网站区域,那么如果DOM中没有这些区域,那么这段代码将不会运行.如果您需要更精细的控制,您可以始终data-*在DOM元素上使用HTML5 属性,以便在用户授权上为外部javascript函数提供提示.