>我有一个系统,其中所有页面(视图)和所有控件(按钮,链接,菜单...)都应用了安全角色。
所以我有一个管理界面,所有页面和控件都已注册。每个用户都有一组单独的权限。
因此,例如:
我有一个View EditCar,有3个按钮:"新建","删除"和"返回"。
因此,用户X有权查看View EditCar,并且只有"返回"按钮
因此,必须注册每个新视图,并与用户相关联。没有角色,因为每个用户都是 100% 可配置的。
所以,我有一个过滤器属性:
public class CustomAuthorize : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
var userPermissions = repository.GetAll().Where(x => x.Name.Equals(User.Identity.Name);
// if (!userPermissions.Pages.Any(x => x.NamePage.Contains(???))))
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
所以我的问题是: - 我应该在数据库中保留什么来识别每个视图(操作)?也许 3 个值?区域控制器操作?
这是最好的选择吗?关于该解决方案的任何其他想法?
谢谢
我的 Web 应用程序中有相同的场景,它的工作方式如下:
我们在数据库中有:
权限包含查看、添加、编辑、删除
功能包含可以在角色上设置的所有功能
功能权限将功能与权限绑定,例如哪个功能具有什么权限
用户角色具有用户角色
角色功能权限显示哪个角色具有允许的权限
现在在代码中,当用户进行身份验证时,我会生成分配给它的权限列表,然后我定义了一个枚举,如下所示:
public enum FeatureValue
{
Custom = 1,
Schedule = 2,
Export=3
}
public enum PermissionValue
{
View = 1,
Add = 2,
Edit = 3,
Delete = 4
}
和用户权限静态类来获取授权:
public static bool VerifyPermission(FeatureValue feature, PermissionValue permission, int id) {
return getFeaturePermissionsForReport(feature, permission, id);
}
private static bool getFeaturePermissionsForReport(FeatureValue feature, PermissionValue permission, int id) {
SessionHelper sessionHelper = new SessionHelper(null);
UserModel userModel = sessionHelper .getUser()//get user from session.
if (userModel != null && userModel.IsAuthorized == false) return false;
UserProfile userProfile = sessionHelper.Get<UserProfile> ();
if (userProfile != null && userProfile.AssignedRoleList != null) {
List<Core.Entities.FeaturePermission> featurePermission = userProfile.AssignedRoleList.SelectMany(b => b.RoleFeaturePermission).ToList();
if (featurePermission != null) {
if (featurePermission.Count(f = > f.Feature.Id == (int) feature && f.Permission.Id == (int) permission) > 0) {
bool isAllowed= false;
int featurePermissionId = featurePermission.Where(f = > f.Feature.Id == (int) feature && f.Permission.Id == (int) permission).Select(i = > i.Id).FirstOrDefault();
isAllowed = (reports.Count(r = > (r.FeaturePermissionId == featurePermissionId && r.Id == id)) > 0) ? true : false;
return isAllowed;
}
}
}
return false;
}
现在每个链接、按钮或操作使用一个:
@if (UserPermission.VerifyPermission(FeatureValue.Custom, PermissionValue.Edit))
{
//action link to edit custom view
}
对于操作,自定义属性为:
[AttributeUsage(AttributeTargets.All,AllowMultiple=true)]
public class CustomFeaturePermissionAttribute : ActionFilterAttribute
{
private FeatureValue[] feature;
private PermissionValue[] permission;
private bool excludeParamId;
/// <summary>
/// Set values of featurelist and permission list
/// </summary>
/// <param name="featureList"></param>
/// <param name="permissionList"></param>
public CustomFeaturePermissionAttribute(object featureList,object permissionList, int excludeParamId)
{
FeatureList = (FeatureValue[])featureList;
PermissionList = (PermissionValue[])permissionList;
ExcludeParamId = excludeParamId;
}
public FeatureValue[] FeatureList
{
get
{
return feature;
}
set
{
feature = value;
}
}
public bool ExcludeParamId
{
get
{
return excludeParamId;
}
set
{
excludeParamId = value;
}
}
public PermissionValue[] PermissionList
{
get
{
return permission;
}
set
{
permission = value;
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
bool isAccessAllowed = false;
FeatureValue feature;
PermissionValue permission;
for (int i = 0; i < FeatureList.Count(); i++)
{
feature = FeatureList[i];
permission = PermissionList[i];
isAccessAllowed = UserPermission.VerifyPermission(feature, permission, Convert.ToInt16(ExcludeParamId));
if (isAccessAllowed)
break;
}
if (!isAccessAllowed)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { action = "UnauthorizedAccess", controller = "Security" }));
}
}
}
和 on 操作允许角色对自定义和导出具有查看权限:
[CustomFeaturePermission(new FeatureValue[] { FeatureValue.Custom, FeatureValue.Export }, new PermissionValue[] { PermissionValue.View, PermissionValue.View},pageId)]
public ActionResult Custom()
{
//action body
}
我会创建一种定义每个权限的抽象方法,例如枚举。 例如:
public enum UserPermissions
{
ViewCars,
EditCars,
DeleteCars,
ViewUsers,
EditUsers,
DeleteUsers
}
您可以在数据库中名为"权限"的表中创建这些权限,然后创建多对多映射,其中可以将每个用户分配给任意数量的权限。
然后,您将通过从AuthorizeAttribute
派生来创建自定义授权属性,并重写 OnAuthorization
方法以从数据库加载用户。 这正是您在问题中所做的,除了关键部分是您想添加一些属性,您可以在其中定义操作所需的权限,如下所示:
public class UserPermissionsAttribute : AuthorizeAttribute
{
public IEnumerable<UserPermissions> PermissionsRequired { get; set; }
public UserPermissionsAttribute()
{
}
public UserPermissionsAttribute(params UserPermissions[] permissionsRequired)
{
PermissionsRequired = permissionsRequired;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var user = filterContext.HttpContext.User; // get user from DB
if (PermissionsRequired.All(x => user.Permissions.Any(y => x == y)))
{
// all permissions are met
base.OnAuthorization(filterContext);
}
else
{
throw new UnauthorizedAccessException();
}
base.OnAuthorization(filterContext);
}
}
现在,您可以使用权限或权限列表装饰每个操作或控制器:
[UserPermissions(UserPermissions.ViewCars, UserPermissions.EditCars)]
public ActionResult Index()
{
ViewBag.Title = "Home Page";
return View();
}
通过这种方式,您可以将权限系统与 MVC 控制器/操作逻辑分开。
尽管我不建议使用这种单独存储每个权限的方法。 角色系统使事情变得更加简单,并将提高绩效。 我真的认为您可以使用许多细粒度角色而不是细粒度权限来做到这一点。
请注意,授权用户查看特定页面元素不同于授权 CRUD 或其他数据库操作,除非元素指向控制器中的操作操作。考虑到您可能有一些元素,这些元素不需要被特定用户看到,并且没有特定的数据库操作。到目前为止,我们得出结论,我们需要以下权限:
- 查看权限
- 命令权限
我相信您可以对这两个部分使用Microsoft角色提供程序。根据MSDN文档考虑:
"授权"属性允许您指示授权是 仅限于预定义角色或单个用户。 这给你 高度控制谁有权查看任何页面 网站。
在下一步/问题是如何做到这一点?
我认为有3种方法可以达到我们的目的:
-
解决方案 1:由于将每个用户转发到相关视图,因此使用特定页面元素创建单独的视图。在这种情况下,我们必须还要创建单独的控制器操作。我们必须检查用户类型在每次操作之前,例如
[Authorise(Roles="Administrator")]
.我们强制具有静态(预定义)角色和可访问性。而在一句话 不是一个好的解决方案 因为冗余和不稳定。 -
解决方案 2:只需在 One Page 中为每个访问限制元素添加一些
if
条件即可动态创建页面(用于示例编辑页面)。这就像使用@if (User.IsInRole("Admin"))
来授权特定用户并显示相关的页面元素,如按钮。在控制器端,我们可以使用if
条件(由于添加动态而未FilterAttribute
基于生成/添加的新角色的功能)和控制有效针对数据库的事务。尽管FilterAttribute
添加了一些很棒的功能主义者(如性能优化)。一句话 一个温和的解决方案。
解决方案 3:像解决方案 2 一样,只需通过以下方式修复控制器问题创建我们自己的自定义过滤器属性以进行授权。那将继承自
AuthorizeAttribute
并覆盖OnAuthorize
方法,仅对操作执行所需操作。
例如:
public class TableAuthorizeAttribute : AuthorizeAttribute
{
public enum TableAction
{
Read,
Create,
Update,
Delete
}
public TableAction Action { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//do custom authorizization using Action and getting TableEntryID
//from filterContext.HttpContext.Request.QueryString or
//filterContext.HttpContext.Request.Form
}
}
它的用法将是这样的:
[TableAuthorize(Action=TableAuthorizeAttribute.TableAction.Update)]
这是关于上述概念的完整示例。下面是创建动态AuthorizeAttribute
以授权添加到应用程序的新角色的完整示例。
解决方案3一句话 完美但复杂的解决方案。
请注意,通过在操作之前使用FilterAttribute
,我们将应用程序限制为静态/预定义角色。无需使用其他数据结构或在数据库中生成表。
我过去见过类似的实现,它使用了令牌概念。
每个操作方法都由一个令牌表示。令牌的选择定义角色。将角色分配给用户。
我使用一个简单的控制台应用程序来反映我的 MVC 应用程序,并查找所有控制器并确定其中的每个操作方法。
将这些"令牌"与您的角色一起存储在数据库中。
实现保持简单,只是使用带有命名空间等的完全限定名称来标识它们。这样,数据必须特定于您的应用程序,这可以提高安全性
我会采用 Trevor 的方法,但它不会使用属性。我会创建一个常见的操作权限枚举,例如:
[Flags]
internal enum PermissionsEnum
{
listbutton = 1,
editbutton = 2,
deletebutton = 4,
savebutton = 8,
createbutton = 16,
action03 = 32,
action04 = 64,
action05 = 128,
action06 = 256,
action07 = 512,
action08 = 1024,
action09 = 2048,
action10 = 4096,
action11 = 8192,
action12 = 16384,
action13 = 32768
}
我为数据库中的每个区域/控制器和用户存储这样的权限对象,例如一些额外的约束权限值 -1 不允许调用操作,权限值 0 调用操作,但不允许调用其他权限:
Controller/Action UserId Permission
================= ====== =========
cars/delete User0001 -1
cars/edit User0001 8
cars/index User0001 0
cars/list User0001 16
cars/show User0001 2
应用权限我将创建一个基本控制器。调用操作时,基本控制器将检索被调用控制器的权限:
var currentController = this.Url.RouteData["controller"];
var currentAction = this.Url.RouteData["action"];
var currentUserPermissons = GetUserPermissonForController(string.Format("{0}/{1}",currentController,currentAction), userId);
if( 0 > currentUserPermissons ) RedirectToAction("PermissonDenied","Error");
ViewBag.UserPermissons = (PermissionsEnum)currentUserPermissons;
在每个视图中,我会在创建受保护的项目之前检查 ViewBag.UserMissons,例如:
@{ if((ViewBag.UserPermissons & PermissionsEnum.listbutton) == PermissionsEnum.listbutton)
{
@Html.ActionLink("Listitems","List")
}
}