我不喜欢内置的成员(member)提供程序。我决定自己动手。我正在尝试想出一种在操作级别执行授权的好方法。以下是我尝试满足的要求:
- 属性用法 - 我喜欢这个,因为它在调用堆栈中控制在一个非常高的级别,是组织权限的好地方。
- 没有神奇的字符串 - 这就是我远离当前角色提供者的原因。我不想留下无法轻易重命名的字符串。
- 权限应该可以由一个其他权限组成。示例:
ReadWrite
具有Read
的权限。就像使用枚举或运算一样。
注意:有些人认为这组要求过于宽泛(请参阅评论)。我不这么认为,我认为他们相当直截了当。
最大的障碍是属性的使用。只能有“属性参数类型的常量表达式、typeof表达式或数组创建表达式”。
我在想也许有这样的东西来使操作具有静态访问权限。在属性内部,它会将 int “转换”为实际的 Permission 或其他东西......:
public static class Operations
{
public static class SectionA
{
public const int Read = 1;
public const int ReadWrite = 2;
}
public static class SectionB
{
// ... and so on...
}
}
但这确实限制了构图。我确定您在想“为什么不走枚举路线?”好吧,我想计划改变事情,不想限制为 32(int)或 64(long)操作,并且以后必须进行大量重写(也在丑陋的数据库中)。
另外,如果有比 Action / Controller 上的属性更好的选择,那么我会竭诚倾听建议。
编辑:同样来自 this post ,我读过有关 BitArray
类的内容。它看起来有点难看,尤其是数据库中的任意存储。
最佳答案
首先,我必须感谢你吸引我来回答这个问题;)
这是一个很长的答案,而且只是一个起点。您必须弄清楚如何为用户分配角色以及如何在 AuthenticateRequest
中重新创建它们。
如果这不能回答您的问题,我希望它能给您带来启发。享受吧!
装饰 Controller Action
我开始装饰默认的HomeController
中的两个action:
[AuthorizeRoles(Role.Read)]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
[AuthorizeRoles(Role.Write)]
public ActionResult About()
{
return View();
}
然后应授予 ReadWrite 角色中的所有用户访问权限。我在这里选择使用枚举作为魔术字符串的类型安全占位符。这个枚举的作用就是作为一个占位符。没有必须在其他地方维护的复合枚举值。稍后会详细介绍。
public enum Role
{
Read,
Write,
ReadWrite
}
实现新的授权属性
由于字符串不见了,我需要一个新的授权属性:
public class AuthorizeRolesAttribute : AuthorizeAttribute
{
private readonly RoleSet authorizedRoles;
public AuthorizeRolesAttribute(params Role[] roles)
{
authorizedRoles = new RoleSet(roles);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return authorizedRoles.Includes(httpContext.User);
}
}
RoleSet
包装了一组枚举值并验证 IPrincipal
是否是其中一个成员:
public class RoleSet
{
public RoleSet(IEnumerable<Role> roles)
{
Names = roles.Select(role => role.ToString());
}
public bool Includes(IPrincipal user)
{
return Names.Any(user.IsInRole);
}
public bool Includes(string role)
{
return Names.Contains(role);
}
public IEnumerable<string> Names { get; private set; }
}
维护角色
CompositeRoleSet
是注册和处理复合角色的地方。 CreateDefault()
是所有复合 Material 注册的地方。
Resolve()
将获取角色列表(枚举值)并将复合角色转换为单个角色。
public class CompositeRoleSet
{
public static CompositeRoleSet CreateDefault()
{
var set = new CompositeRoleSet();
set.Register(Role.ReadWrite, Role.Read, Role.Write);
return set;
}
private readonly Dictionary<Role, Role[]> compositeRoles = new Dictionary<Role, Role[]>();
private void Register(Role composite, params Role[] contains)
{
compositeRoles.Add(composite, contains);
}
public RoleSet Resolve(params Role[] roles)
{
return new RoleSet(roles.SelectMany(Resolve));
}
private IEnumerable<Role> Resolve(Role role)
{
Role[] roles;
if (compositeRoles.TryGetValue(role, out roles) == false)
{
roles = new[] {role};
}
return roles;
}
}
连接起来
我们需要经过身份验证的用户才能继续工作。我在 global.asax 中作弊并硬编码了一个:
public MvcApplication()
{
AuthenticateRequest += OnAuthenticateRequest;
}
private void OnAuthenticateRequest(object sender, EventArgs eventArgs)
{
var allRoles = CompositeRoleSet.CreateDefault();
var roles = allRoles.Resolve(Role.ReadWrite);
Context.User = new ApplicationUser(roles);
}
最后,我们需要一个理解所有这些的 IPrincipal
:
public class ApplicationUser : IPrincipal
{
private readonly RoleSet roles;
public ApplicationUser(RoleSet roles)
{
this.roles = roles;
}
public bool IsInRole(string role)
{
return roles.Includes(role);
}
public IIdentity Identity
{
get { return new GenericIdentity("User"); }
}
}
关于c# - 对 MVC 站点具有特定要求的粒度权限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4359661/