在 MVC 中处理多个角色 - 基于操作的可访问性
Posted
技术标签:
【中文标题】在 MVC 中处理多个角色 - 基于操作的可访问性【英文标题】:Handling Multiple Roles in MVC - Action-based Accessibility 【发布时间】:2011-07-25 11:11:49 【问题描述】:我目前有一个项目,我似乎遇到了有关角色的问题,并认为我会就如何最好地处理该问题获得一些意见。
系统将需要可编辑、灵活的角色,这些角色不仅可以控制特定区域的访问,还可以控制系统功能的使用(添加用户、编辑用户、查看报告等)
系统目前允许用户拥有多个角色,每个角色都有明确定义的访问/操作区域,例如:
角色 A 可以访问区域 1、2、3 并可以添加用户。 角色 B 可以访问区域 1、5、7 并可以修改用户。 角色 C 可以访问区域 4,6 并且只能查看用户。因此用户可以处于角色 A 和 C,因此可以访问:1、2、3、4 和 6,并且可以添加和查看用户。
我的第一个解决方案是创建一个字典,将所有可能的访问/访问选项区域存储到字典中,如下所示:
Dictionary<string,bool>
然后当它被实例化时,它会从数据库中提取所有属性,然后遍历角色以确定它们是否可访问。
目前所有这些都可以正常工作 - 但是该项目是 javascript/jQuery 密集型项目,因此其中许多选项由客户端函数调用。我试图避免将所有这些客户端功能包装起来:
<%if(AccessDictionary[key])
//Enable or Disable Action
<%%>
所以基本上,我想知道以下几点:
-
用户登录后,存储此词典的最佳方式是什么?静态的?会议中?
为了便于在视图中访问字典,最好的存储方法是什么? (目前我认为没有办法包装我的客户端函数)
任何建议或想法将不胜感激!
【问题讨论】:
这不能通过 web.config 中基于位置的授权安全设置来控制吗? 【参考方案1】:我会将此信息存储在身份验证 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");
然后我将编写一个自定义的 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);
接下来你可以用这个属性来装饰你的控制器/动作来处理授权:
// 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()
...
为了简单地检查用户是否处于给定角色中:
bool isInRole = User.IsInRole("RoleC");
有了这些信息,您现在就可以开始思考如何组织您的视图模型了。在这些视图模型中,我将包含布尔属性,例如 CanEdit
、CanViewReport
、...,这些属性将由控制器填充。
现在,如果您在每个操作和视图中都需要这种映射,事情可能会变得重复和无聊。这就是全局自定义操作过滤器发挥作用的地方(它们实际上并不存在于 ASP.NET MVC 2 中,仅存在于 ASP.NET MVC 3 中,因此您可能需要一个装饰有此操作过滤器的基本控制器,它或多或少地模拟相同功能)。您只需定义这样的全局动作过滤器,该过滤器在每个动作之后执行并将一些通用视图模型注入 ViewData(天啊......,不敢相信我正在发音这些词),从而使其可用于横向的所有视图其他动作方式。
最后,您将在视图中检查这些布尔值属性,以包括或不包括网站的不同区域。就 javascript 代码而言,如果它是不显眼的 AJAXifying 站点区域,那么如果这些区域不存在于 DOM 中,则此代码将不会运行。如果您需要更细粒度的控制,您始终可以在您的 DOM 元素上使用 html5 data-*
属性,以提示您的外部 javascript 函数对用户的授权。
【讨论】:
感谢您像往常一样非常深入的回答达林。我会考虑整合这样的东西!另外 - 我会给你另一个 +1 的“神圣....” ViewData 提及。 :) 如果您要将敏感信息(如用户角色)存储在 cookie 中,请确保您拥有所有最新的 .NET 补丁。 FormsAuthentication.Encypt() 曾经存在漏洞。 另外,我真的很喜欢 data-* 的想法。User.IsInRole
在视图文件中总是返回 false。以上是关于在 MVC 中处理多个角色 - 基于操作的可访问性的主要内容,如果未能解决你的问题,请参考以下文章