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





5.00/5 (17投票s)
关于 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 日 - 初始版本