c# - 对 MVC 站点具有特定要求的粒度权限

标签 c# .net asp.net-mvc permissions

我不喜欢内置的成员(member)提供程序。我决定自己动手。我正在尝试想出一种在操作级别执行授权的好方法。以下是我尝试满足的要求:

  1. 属性用法 - 我喜欢这个,因为它在调用堆栈中控制在一个非常高的级别,是组织权限的好地方。
  2. 没有神奇的字符串 - 这就是我远离当前角色提供者的原因。我不想留下无法轻易重命名的字符串。
  3. 权限应该可以由一个其他权限组成。示例: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/

相关文章:

asp.net-mvc - 如何构建 VB.NET Windows 窗体应用程序

c# - .Net MVC3 自定义模型绑定(bind)器 - 初始加载模型

.net - 如何在单个设置中合并 exe 和 msi 文件?

.net - 与Server 2008 R2上的Internet Explorer ComObject交互时出现问题

c# - 等待自定义函数

css - VB - ASP.NET MVC 3 Razor 呈现额外的空白

asp.net-mvc - 这称为什么类型的架构?

c# - 我可以从 xaml 中过滤集合吗?

c# - 在 Telerik Radgrid 中,如何根据组标题的数量对结果进行分页?

c# - Lambda 检查列表是否包含所有属性的某些属性值