对 MVC 站点具有特定要求的细化权限 [英] Granular permissions with certain requirements for an MVC site

查看:15
本文介绍了对 MVC 站点具有特定要求的细化权限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不喜欢内置的会员服务提供商.我决定自己动手.我正在尝试提出一种在操作级别执行授权的好方法.以下是我努力要达到的要求:

I don't like the built in membership providers. I've decided to roll my own. I'm trying to come up with a good method for performing authorization at the action level. Here are the requirements that I'm trying to go by:

  1. 属性使用 - 我喜欢这个,因为它在调用堆栈中的一个非常高的级别进行控制,并且是组织权限的好地方.
  2. 没有魔法字符串 - 这就是我偏离当前角色提供者的原因.我不想留下无法轻松重命名的字符串.
  3. 权限应该可以由一个其他权限组成.示例:ReadWrite 具有Read 的权限.就像使用枚举进行 or'ing 一样.
  1. Attribute usage - I like this since it controls at a very high level in the call stack and is a nice place to organize permissions.
  2. No magic strings - This is a reason why I'm straying away from the current role providers. I don't want to leave strings lying around that can't be easily renamed.
  3. Permissions should can be composed of one other permission. Example: ReadWrite has permission for Read. Just like or'ing with an enum.

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

NOTE: Some think this set of requirements is too broad (see comments). I don't think so, I think they're fairly straightforward.

最大的障碍是属性的使用.只能有一个属性参数类型的常量表达式、typeof 表达式或数组创建表达式".

The biggest showstopper is attribute usage. There can only be "constant expressions, typeof expressions or array creation expression of an attribute parameter type".

我在想也许有这样的东西来使操作具有静态访问权限.在属性内部,它会将整数转换"为实际的权限或其他内容...:

I was thinking of perhaps having something like this to make operations have static access. Inside of the attribute, it would "convert" the int to the actual Permission or something...:

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(整数)或 64(长)操作,并且以后必须进行大量重写(也在丑陋的数据库中).

But it really limits composition. I'm sure you're thinking "why don't you go the enum route?" well I want to plan for things to change and don't want to limit to 32 (int) or 64 (long) operations and have to do a massive rewrite later (also in the db that's just ugly).

此外,如果有比动作/控制器的属性更好的选择,那么我非常愿意听取建议.

Also, if there is a better alternative than attributes on actions/controllers, then I'm all ears for suggestions.

同样来自这篇文章,我已经阅读了 BitArray 类.这看起来有点难看,尤其是在数据库中任意存储的情况下.

Also from this post, I've read about the BitArray class. It seems kind of ugly, especially with the arbitrary storage in the database.

推荐答案

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

First of all, I have to thank you for sucking me into answering this ;)

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

This is a long answer, and is only a starting point. You have to figure out how to assign roles to users and how to recreate them in the AuthenticateRequest.

如果这不能回答你的问题,我希望它会是一个灵感.享受!

If this does not answer your question, I hope it will be an inspiration. Enjoy!

装饰控制器动作

我开始装饰默认的HomeController中的两个动作:

I started to decorate the two actions in the default HomeController:

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

        return View();
    }

    [AuthorizeRoles(Role.Write)]
    public ActionResult About()
    {
        return View();
    }

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

All users in the ReadWrite role should then be granted access. I opted here to use an enum as a type safe placeholder for the magic strings. The role of this enum is nothing else than being a placeholder. There are no composite enum values, that has to be maintained somewhere else. More on that later.

public enum Role
{
    Read,
    Write,
    ReadWrite
}

实现新的授权属性

由于字符串没有了,我需要一个新的授权属性:

Since the strings are gone, I need a new authorize attribute:

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 是否是其中一个的成员:

The RoleSet wraps a set of enum values and verifies if an IPrincipal is a member of one of them:

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() 是注册所有组合的地方.Resolve() 将获取角色列表(枚举值)并将组合转换为它们的单个对应项.

The CompositeRoleSet is where composite roles are registered and handled. CreateDefault() is where all composites are registered. Resolve() will take a list of roles (enum values) and convert the composites to their single counterparts.

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 中作弊并硬编码了一个:

We need an authenticated user to work on. I cheated and hard-coded one in 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 来理解这一切:

Finally, we need an IPrincipal which understand all this:

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"); }
    }
}

这篇关于对 MVC 站点具有特定要求的细化权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆