在 ASP.NET 框架中有没有更好的方法来实现基于角色的访问?

Is there a better way to implement role based access in ASP.NET framework?

基本上,过去几天我一直在尝试弄清楚如何将简单的管理员和成员角色添加到我正在为朋友开发的网站上。 (我正在使用 ASP.NET Framework 5.2.7.0)。我知道微软内置了一个很好的基于角色的访问功能,允许你在控制器的顶部放置类似 [Authorize Role=("Admin") 的东西;但是我根本无法让它工作,而且我发现的大部分资源都是针对 ASP.NET Core 的。

我已经尝试修改我的 web.config 文件以启用基于角色的访问(并希望将角色等迁移到我的数据库)。但是由于我一直无法弄清楚其中的任何一个,所以我尝试走一条更 hacky 的路线。 (**我不是高级程序员,我已经这样做了大约一年,但绝不是专业人士)。这就是我在尝试验证用户是否为管理员时基本上想到的(这也没有用)。

 [Authorize]
    public class AdminController : Controller
    {
        private LDSXpressContext db = new LDSXpressContext();

        public ActionResult AdminPortal()
        {
            IsAdmin();
            return View();
        }

        private ActionResult IsAdmin()
        {
            string name = User.Identity.Name;
            //The User.Identity.Name stores the user email when logged in
            var currentUserObject = db.accounts.Where(x => x.clientEmail == name);

            Account currentUser = new Account();
            foreach (var user in currentUserObject)
            {
                //I loop through the results, even though only one user should 
                //be stored in the var CurrentUserObject because it's the only 
                //way I know how to assign it to an object and get its values.
                currentUser = user;
            }

            if (currentUser.role == 2) //the number 2 indicates admin in my db
            {
                return null;
            }
            else
            {
           //Even when this is hit, it just goes back and returns the 
           //AdminPortal view
                return RedirectToAction("Index", "Home");
            }
        }
    }

现在我几乎可以肯定这不是一种非常安全的方法来检查登录用户是否是管理员,但我希望它至少可以工作。我的想法是当有人试图访问 AdminPortal 时,IsAdmin 方法将 运行 并检查用户是否是数据库中的管理员。如果他们是,则它 return 为 null 并显示 AdminPortal 视图,如果他们不是管理员,则他们将被重定向到主页上的索引视图。但是,AdminPortal 页面始终显示给任何用户,这似乎也不起作用。我什至进入了代码并在 return RedirectToAction("Index", "Home"); 操作上观察了它 运行,但随后它跳回到 AdminPortal 方法并且只是 return 的 AdminPortal 视图。所以我的问题是:

1) 如果有人碰巧在 ASP.NET Framework 中使用过基于角色的访问,我会喜欢一些关于如何设置它的提示

或者,

2) 如果所有其他方法都失败并且我需要使用我的 hacky 方法,为什么它会继续 return AdminView,即使用户不是管理员。

**注意:我知道我可以创建一个函数,如果用户是否是管理员,则 returns true 或 false,然后在 AdminPortal 控制器中有一个 if/else 语句将 return 用于 true,另一个用于 false,但是我不想在每个 ActionMethod 上都实现它,最好将它保持在一行,或者只是 [Authorize Role="Admin] 如果可能,在控制器上方。

非常感谢你们提供的任何帮助,几天来我一直在努力研究和解决这个问题,并决定联系并询问社区!

至少,您需要对您正在做的事情进行一些调整:

[Authorize]
public class AdminController : Controller
{
    public ActionResult AdminPortal()
    {
        if(IsAdmin())
        {
            return View();
        }

        return RedirectToAction("Index", "Home");
    }

    private bool IsAdmin()
    {
        bool isAdmin = false;

        using(LDSXpressContext db = new LDSXpressContext())
        {
            string name = User.Identity.Name;

            //The User.Identity.Name stores the user email when logged in

            // @see https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.singleordefault
            var currentUser = db.accounts.SingleOrDefault(x => x.clientEmail.Equals(name, StringComparison.OrdinalIgnoreCase));

            // If the email doesn't match a user, currentUser will be null
            if (currentUser != null) 
            {
                //the number 2 indicates admin in my db
                isAdmin = currentUser.role == 2;
            }
        }

        return isAdmin;
    }
}

首先,DbContext 实例最多只能在 HTTP 请求的生命周期内使用。将它从 class / controller 级别移到 using 块中,确保它被正确处理。

接下来,您的 IsAdmin 函数实际上只需要根据您的查找 return 一个 true/false 值,然后 AdminPortal 操作可以决定如何处理该结果。

由于电子邮件似乎是您 table 中的唯一字段,请使用 SingleOrDefault or FirstOrDefault LINQ 扩展来获取单个匹配记录。您使用哪一个取决于您,但如果它确实是一个唯一值,SingleOrDefault 更有意义(如果超过一行匹配,它将抛出异常)。使用带有 String.Equals 扩展方法的 StringComparison 标志使您的搜索 case-insensitive。有几个 culture-specific 版本,但我通常会在这里使用序号匹配。

实施某些版本的身份框架对于此处的答案来说有点太长了,但是可以实施 claims-based 身份验证方案而无需太多工作。不过,这可能需要一个单独的答案。