12

我有一个系统,其中所有页面(视图)和所有控件(按钮、链接、菜单项...)都应用了安全角色。

所以我有一个管理界面,所有页面和控件都已注册。每个用户都有一组单独的权限。

因此,例如:

我有一个 View EditCar,有 3 个按钮:“新建”、“删除”和“返回”。

因此用户 X 有权查看查看 EditCar,并且只有“返回”按钮

所以每个新视图都必须注册,并与用户关联。没有角色,因为每个用户都是 100% 可配置的。

所以,我有一个 FilterAttribute:

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

所以我的问题是: - 我应该在数据库中保留什么来识别每个 View(Action) ?也许3个值?区域控制器动作?

这是最好的选择吗?关于该解决方案的任何其他想法?

谢谢

4

5 回答 5

5

我的网络应用程序中有相同的场景,它的工作方式如下:

我们在数据库中有:

权限包含查看、添加、编辑、删除

Feature包含所有可以设置在角色之上的特征

FeaturePermission将功能与权限绑定,例如哪个功能有什么权限

UserRole具有用户的角色

RoleFeaturePermission显示哪个角色有什么权限被允许

现在在代码中,当用户进行身份验证时,我生成分配给它的具有功能的权限列表,然后我定义了一个枚举,例如:

public enum FeatureValue
{
    Custom = 1,
    Schedule = 2,
    Export=3          
}

public enum PermissionValue
{
    View = 1,
    Add = 2,
    Edit = 3,
    Delete = 4
}

和 UserPermission 静态类来获得授权:

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

        }
    }

并且在操作上允许角色具有对自定义和导出的查看权限:

[CustomFeaturePermission(new FeatureValue[] { FeatureValue.Custom, FeatureValue.Export }, new PermissionValue[] { PermissionValue.View, PermissionValue.View},pageId)]
public ActionResult Custom()
{
   //action body
}
于 2013-10-28T07:10:26.977 回答
3

我将创建一种定义每个权限的抽象方式,例如枚举。例如:

public enum UserPermissions
{
    ViewCars,
    EditCars,
    DeleteCars,
    ViewUsers,
    EditUsers,
    DeleteUsers
}

您可以在数据库中名为 Permissions 的表中创建这些,然后创建一个多对多映射,其中每个用户都可以分配到任意数量的权限。

然后,您将通过派生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 控制器/操作逻辑分开。

尽管我建议不要使用这种单独存储每个权限的方法。角色系统使事情变得更简单,并将提高性能。我真的认为您可以使用许多细粒度的角色而不是细粒度的权限来做到这一点。

于 2013-10-18T19:59:17.310 回答
1

请注意,授权用户查看特定页面元素与授权 CRUD 或其他数据库操作不同,除非这些元素指向 Controller 中的操作操作。考虑到您可能有一些特定用户不需要看到的元素,并且没有特定的数据库操作。到目前为止,我们得出结论,我们需要以下权限:

  1. 允许查看
  2. 指挥权

我相信您可以将Microsoft Role Provider用于这两个部分。根据 MSDN 文档,考虑到:

Authorize 属性允许您指示授权仅限于预定义的角色或单个用户。这使您可以高度控制谁有权查看网站上的任何页面。

在下一步/问题是如何做到这一点?

我认为有 3 种方法可以实现我们的目的:

  • 解决方案 1:由于将每个用户转发到相关视图,因此创建具有特定页面元素的单独视图。在这种情况下,我们也必须创建单独的控制器动作。我们必须在每个操作之前检查用户类型,例如[Authorise(Roles="Administrator")]. 我们被迫拥有静态(预定义)角色和可访问性。由于冗余和不稳定,一句话不是一个好的解决方案。

  • 解决方案 2:动态创建页面,只需为One Pageif中的每个访问受限元素添加一些条件(例如 Edit Page)。这就像使用授权特定用户并显示按钮等相关页面元素。在控制器端,我们可以使用 条件(不是由于基于生成/添加的新角色添加动态功能)并控制针对数据库的有效事务。尽管s 添加了一些很棒的功能主义者(例如性能优化)。一句话温和的解决方案@if (User.IsInRole("Admin"))ifFilterAttributeFilterAttribute

  • 解决方案3:像解决方案2一样,通过创建我们自己的自定义FilterAttribute进行授权来解决Controller问题。这将继承AuthorizeAttribute并覆盖该OnAuthorize 方法以执行您只需要为 Operations 执行的操作

例如 :

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完美但复杂的解决方案

请注意,通过使用FilterAttributebefore Actions,我们将应用程序限制为静态/预定义角色。无需使用其他数据结构或在数据库中生成表。

于 2013-10-25T22:52:20.373 回答
0

我在过去看到过类似的实现,它使用了令牌概念。

每个 Action 方法都由一个令牌表示。一系列标记定义了一个角色。将角色分配给用户。

我使用一个简单的控制台应用程序来反映我的 MVC 应用程序并查找所有控制器并确定其中的每个操作方法。

将这些“令牌”与您的角色一起存储在您的数据库中。

该实现保持简单,只是使用带有命名空间等的完全限定名称来识别它们。这样,数据必须特定于您的应用程序,这可以提高安全性

于 2013-10-23T16:51:20.190 回答
0

我会采用 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.UserPermissons,例如:


@{ if((ViewBag.UserPermissons & PermissionsEnum.listbutton) == PermissionsEnum.listbutton)
    {
        @Html.ActionLink("Listitems","List")
    }
}
于 2013-10-27T09:22:56.653 回答