65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET MVC 和用户组进行自定义授权

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2020年5月4日

CPOL

4分钟阅读

viewsIcon

20057

关于 MVC 中基于用户组的自定义授权的概述。

引言

本文介绍了如何使用 ASP.NET MVC 和用户角色实现自定义授权。它可防止未经授权的用户访问他们不应该访问的页面。

用于演示的示例 MVC 应用程序

在此演示应用程序中,我使用的是 Visual Studio 2019。我将向您展示用户如何被阻止访问他们不应该访问的页面。我们正在使用Windows 身份验证,并且只有属于特定用户组(Admin/NormalUser)的用户才能授权访问相应的页面。例如,属于Admin组的用户只能访问Admin 页面,而属于 NormalUser组的用户只能访问NormalUser 页面

步骤 1:在系统中创建新的用户角色

创建一个管理员组并映射用户

启动“计算机管理”窗口。创建一个新的用户组“Admin”,并将 Windows 用户映射到创建的组,如下图所示

创建一个用户组并映射用户

启动“计算机管理”窗口。创建一个新的用户组“User”,并将 Windows 用户映射到创建的组,如下图所示

步骤 2:创建项目

在 Visual Studio 中,创建一个新的 ASP.NET MVC Web Application (C#) 项目,如下图所示

步骤 3:在 Web.config 中配置用户组

如下所示,在config文件中配置新创建的组

 <appSettings>   
    <add key="AdminUserRoles" value="Admin" />
    <add key="UserRoles" value="NormalUser" />
  </appSettings>

步骤 4:添加枚举类

在项目下添加 Enum 类“Enums.cs”,并添加 enum 常量“UserRoles”,如下所示

namespace CustomAuthorizationWithMVC
{
    public static class Enums
    {
        public enum UserGroups
        {
            /// <summary>
            /// No roles assigned
            /// </summary>
            None = 0,

            /// <summary>
            /// Admin role.
            /// </summary>
            Admin = 1,

            /// <summary>
            /// User role.
            /// </summary>
            NormalUser = 2,

            /// <summary>
            /// Both Administrator and  Normal user roles.
            /// </summary>
            All = 3
        }
    }
}

步骤 5:添加登录视图模型

以下视图模型类可帮助用户存储已登录用户的详细信息。

using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Models
{
    public class LogInViewModel
    {
        /// <summary>
        /// Gets or sets the LoggedInUser once Authenticated.
        /// </summary>
        public string LoggedInUser { get; set; }

        /// <summary>
        /// Gets or sets the Admin flag true/false.
        /// </summary>
        public bool IsAdmin { get; set; }

        /// <summary>
        /// Gets or sets the User Role Type.
        /// </summary>
        public UserGroups UserGroup { get; set; }
    }
}

步骤 6:添加授权过滤器

在项目下添加类文件“UserAuthorizeAttribute.cs”,并在类文件中添加以下代码。此类继承自“AuthorizeAttribute”并重写“OnAuthorization”方法,该方法将已登录的组/角色与web.Config中配置的角色进行验证。如果用户未加入任一组/角色,则用户将被重定向到登录页面,而无法访问请求的页面。

namespace UI.UserAuthorize
{
    using System;
    using System.Configuration;
    using System.Diagnostics;
    using System.Web.Mvc;
    using System.Web.Routing;
    using static CustomAuthorizationWithMVC.Enums;
    using AuthorizeUser.Common;

    public sealed class UserAuthorizeAttribute : AuthorizeAttribute
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UserAuthorizeAttribute" /> class.
        /// </summary>
        /// <param name="allowedGroupTypes">allowed group types</param>
        public UserAuthorizeAttribute(params UserGroups[] userRoleTypes)
        {
            this.AllowedUserRoleTypes = userRoleTypes;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the admin user roles from configuration file.
        /// </summary>
        private string AdminUserRoles { get; } = 
                ConfigurationManager.AppSettings["AdminUserRoles"];

        /// <summary>
        /// Gets the user roles from configuration file.
        /// </summary>
        private string UserRoles { get; } = ConfigurationManager.AppSettings["UserRoles"];

        /// <summary>
        /// Gets or sets the allowed role types retrieved from controller or action method.
        /// </summary>
        private UserGroups[] AllowedUserRoleTypes { get; set; }
    
        #endregion

        #region Public Methods

        /// <summary>
        /// Method to do the authorization for user.
        /// </summary>
        /// <param name="filterContext">authorization context.</param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext != null)
            {
                var user = filterContext.RequestContext.HttpContext.User;

                base.OnAuthorization(filterContext);
               
                ////If user does not have any access, redirect him to login page.
                if (!AuthorizeUser.IsAdmin(AllowedUserRoleTypes, AdminUserRoles, user) 
                    && !AuthorizeUser.IsUser(AllowedUserRoleTypes, UserRoles, user))
                {                    
                    filterContext.Result = new RedirectToRouteResult(new
                        RouteValueDictionary(new { controller = "LogIn", action = "LogIn" }));
                }               
            }
        }
        #endregion        
    }
}

步骤 7:添加授权帮助类

此类的成员由授权过滤器类“UserAuthorizeAttribute”使用,以检查已登录用户是否属于配置的 Admin 组或 NormalUser 组。

namespace AuthorizeUser.Common
{
    using System.Linq;
    using System.Security.Principal;
    using System.Text.RegularExpressions;
    using static CustomAuthorizationWithMVC.Enums;

    /// <summary>
    /// Class to check user group
    /// </summary>
    public static class AuthorizeUser
    {
        /// <summary>
        /// Method to check whether user is admin user
        /// </summary>
        /// <param name="allowedAuditUserGroupTypes">allowed audit user group types</param>
        /// <param name="admUserGroups">admin user groups</param>
        /// <param name="user">user</param>
        /// <returns>true or false</returns>
        public static bool IsAdmin(UserGroups[] allowedAuditUserGroupTypes,
               string auditAdminUserGroups, IPrincipal user)
        {
            bool isAdmin = false;
            var adminUserGroups = 
                Regex.Replace(auditAdminUserGroups, @"\s", string.Empty).Split(',');

            //If allowed group is configured for Administrator.
            if (allowedAuditUserGroupTypes.Any
               (allowedGroupType => allowedGroupType == UserGroups.Admin))
            {
                isAdmin = adminUserGroups.Any(admGrp => user.IsInRole(admGrp));
            }
            return isAdmin;
        }

        /// <summary>
        ///  Method to check whether user is audit user
        /// </summary>
        /// <param name="allowedAuditUserGroupTypes">allowed audit user group types</param>
        /// <param name="admUserGroups">admin user groups</param>
        /// <param name="user">user</param>
        /// <returns>true or false</returns>
        public static bool IsUser(UserGroups[] allowedAuditUserGroupTypes, 
             string auditUserGroups, IPrincipal user)
        {
            bool isUser = false;
            var userGroups = Regex.Replace(auditUserGroups, @"\s", string.Empty).Split(',');
            ////If allowed group is configured for Normal user.
            if (allowedAuditUserGroupTypes.Any
               (allowedGroupType => allowedGroupType == UserGroups.NormalUser))
            {
                isUser = userGroups.Any(usrGrp => user.IsInRole(usrGrp));
            }
            return isUser;
        }
    }
}

步骤 8:添加 MVC 控制器

在项目中的Controllers文件夹下添加以下控制器类

  • LogInController.cs
  • AdminController.cs
  • UserController.cs

LogInController.cs

以下控制器类可帮助用户导航到登录视图,并根据用户角色启用/禁用登录按钮。管理员登录按钮的功能是导航到管理员页面,而普通用户登录按钮用于将用户导航到普通用户页面。

using CustomAuthorizationWithMVC.Models;
using System.Configuration;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    public class LogInController : Controller
    {        
        /// <summary>
        /// Gets the admin user groups from config file.
        /// </summary>
        private string AdminUserRoles { get; } = 
                ConfigurationManager.AppSettings["AdminUserRoles"];

        /// <summary>
        /// Gets the user groups from config file.
        /// </summary>
        private string UserRoles { get; } = ConfigurationManager.AppSettings["UserRoles"];

        // GET: LogIn
        public ActionResult LogIn()
        {
            // Check the user is enrolled into any of the Admin user roles.
            bool isAdmin = Regex.Replace(this.AdminUserRoles, @"\s", string.Empty).Split(',')
                            .Any(admRole => User.IsInRole(admRole));

            // Check the user is enrolled into any of the normal user roles.
            bool isUser = Regex.Replace(this.UserRoles, @"\s", string.Empty).Split(',')
                            .Any(usrRole => User.IsInRole(usrRole));

            LogInViewModel logInViewModel = new LogInViewModel()
            {
                LoggedInUser = User.Identity.Name,                
                UserGroup = this.GetUserRole(isAdmin, isUser)
            };
            return View(logInViewModel);
        }

        public ActionResult AdminView()
        {
            return this.RedirectToAction("RenderAdminView", "Admin");           
        }

        public ActionResult UserView()
        {
            return this.RedirectToAction("RenderUserView", "User");
        }

        private bool IsUserInGroup(string groupName)
        {
            return User.IsInRole(groupName);
        }
        private UserGroups GetUserRole(bool isAdmin, bool isUser)
        {
            if (isAdmin && isUser)
            {
                return Enums.UserGroups.All;
            }

            if (isAdmin)
            {
                return Enums.UserGroups.Admin;
            }

            if (isUser)
            {
                return Enums.UserGroups.NormalUser;
            }

            return Enums.UserGroups.None;
        }
    }
}

AdminController.cs

以下控制器类可帮助用户导航到管理员页面,前提是他/她已加入管理员角色。通过在“AdminController”类之前(如下面的代码中粗体突出显示)添加授权过滤器“UserAuthorize”注释,将根据web.cofig中配置的管理员角色验证已登录用户。如果验证通过,它将执行“RenderAdminView”方法并导航到管理员页面。否则,它将重定向到登录页面。

using System.Web.Mvc;
using UI.UserAuthorize;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    [UserAuthorize(UserGroups.Admin)]
    public class AdminController : Controller
    {
        
       public ActionResult RenderAdminView()
       {
            return View("Admin");
       }
    }
}

UserController.cs

以下控制器类可帮助用户导航到用户页面,前提是他/她已加入 NormalUser 角色。通过在“UserController”类之前(如下面的代码中粗体突出显示)添加授权过滤器“UserAuthorize”注释,将根据web.config中配置的用户角色验证已登录用户。如果验证通过,它将执行“RenderUserView”方法并导航到用户页面。否则,它将重定向到登录页面。

using System.Web.Mvc;
using UI.UserAuthorize;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    [UserAuthorize(UserGroups.NormalUser)]
    public class UserController : Controller
    {
        public ActionResult RenderUserView()
        {
            return View("User");
        }
    }
}

步骤 9:添加 MVC 视图

在项目中的Views文件夹下添加以下视图

  • LogIn.cshtml
  • Admin.cshtml
  • User.cshtml

LogIn.cshtml

在此视图中,我们收到“LogInViewModel”作为模型,并根据 enum 常量验证已登录用户组。如果已登录用户组是“Admin”,则启用“Login as Administrator”按钮;如果已登录用户组是“NormalUser”,则启用“Login as NormalUser”按钮。

@model CustomAuthorizationWithMVC.Models.LogInViewModel
@{
    ViewBag.Title = "LogIn";
}

<h2>LogIn Page.</h2>
<div>
    @if (Model.UserGroup == Enums.UserGroups.Admin || Model.UserGroup == Enums.UserGroups.All)
    {
        <button id="btnAdministrator" 
        onclick="location.href = '@Url.Action("AdminView", "LogIn")'"
                class="btn btn-primary">Login as Administrator</button>
    }
    @if (Model.UserGroup == Enums.UserGroups.NormalUser || 
         Model.UserGroup == Enums.UserGroups.All)
    {
        <button id="btnAuditUser" 
        onclick="location.href = '@Url.Action("UserView", "LogIn")'" 
                class="btn btn-primary">Login as NormalUser</button>
    }
    @if (Model.UserGroup == Enums.UserGroups.None)
    {
        <button id="btnClose" onclick="window.close();" 
        class="btn btn-primary">Close</button>
    }
</div>

Admin.cshtml

此视图将显示消息“Admin Page”。当管理员导航到此视图时

@{
    ViewBag.Title = "Admin";
}

<h2>Admin Page.</h2>

User.cshtml

此视图将显示消息“User Page”。当用户导航到此视图时

@{
    ViewBag.Title = "Admin";
}

<h2>NormalUser Page.</h2>

步骤 10:运行应用程序

我只加入了“NormalUser”组。因此,当我启动登录页面时,我只能看到“Login as Normal User”按钮,如下图所示

单击登录按钮后,即可导航到普通用户页面

作为普通组的用户,我仍然可能尝试通过在浏览器中输入 URL 来访问管理员页面。在这种情况下,已登录用户将由授权过滤器验证,并将我重定向回登录页面,因为我是管理员页面的未经授权用户,如下图所示

历史

  • 2020 年 5 月 3 日 - 初始版本
© . All rights reserved.