具有MVC站点的某些要求的细化权限

The*_*Sky 8 .net c# permissions asp.net-mvc

我不喜欢内置的会员提供商.我决定自己推出.我正试图想出一个在动作级别执行授权的好方法.下面是我的要求试图去了:

  1. 属性用法 - 我喜欢这个,因为它在调用堆栈中控制得非常高,并且是组织权限的好地方.
  2. 没有神奇的字符串 - 这就是我偏离当前角色提供者的原因.我不想留下那些不能轻易重命名的字符串.
  3. 权限应可组成一个其他权限.示例:ReadWrite拥有权限Read.就像或与enum一样.

注意:有些人认为这套要求过于宽泛(见评论).我不这么认为,我认为它们相当简单.

最大的showstopper是属性使用.只能有"常量表达式,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...
    }
}
Run Code Online (Sandbox Code Playgroud)

但它确实限制了构图.我相信你在想"你为什么不去enum路线?" 好吧,我想计划改变的事情,不想限制为32(int)或64(long)操作,并且必须稍后进行大规模重写(也在db中,这只是丑陋的).

此外,如果有一个比动作/控制器上的属性更好的替代方案,那么我全都倾听建议.

编辑:也是从这篇文章,我读到了关于这个BitArray课程.它似乎有点难看,特别是对于数据库中的任意存储.

Tho*_*yde 5

首先,我要感谢你吮吸我回答这个问题;)

这是一个很长的答案,只是一个起点.您必须弄清楚如何为用户分配角色以及如何在用户中重新创建角色AuthenticateRequest.

如果这不能回答你的问题,我希望这将是一个灵感.请享用!

装饰控制器动作

我开始在默认情况下装饰这两个动作HomeController:

    [AuthorizeRoles(Role.Read)]
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    [AuthorizeRoles(Role.Write)]
    public ActionResult About()
    {
        return View();
    }
Run Code Online (Sandbox Code Playgroud)

然后,应授予ReadWrite角色中的所有用户访问权限.我在这里选择使用枚举作为魔术字符串的类型安全占位符.这个枚举的作用只不过是占位符.没有复合枚举值,必须在其他地方维护.稍后会详细介绍.

public enum Role
{
    Read,
    Write,
    ReadWrite
}
Run Code Online (Sandbox Code Playgroud)

实现新的授权属性

由于字符串不见了,我需要一个新的authorize属性:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

所述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; }
}
Run Code Online (Sandbox Code Playgroud)

保持角色

CompositeRoleSet是注册和处理复合角色的地方.CreateDefault()是所有复合材料都注册的地方. 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

把它连接起来

我们需要经过身份验证的用户才能使用.我在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);
    }
Run Code Online (Sandbox Code Playgroud)

最后,我们需要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"); }
    }
}
Run Code Online (Sandbox Code Playgroud)