Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用实体框架设计可配置的字段级权限_C#_Sql Server_Entity Framework_Permissions - Fatal编程技术网

C# 如何使用实体框架设计可配置的字段级权限

C# 如何使用实体框架设计可配置的字段级权限,c#,sql-server,entity-framework,permissions,C#,Sql Server,Entity Framework,Permissions,假设我们有一个关于某些车型的信息表,例如: 如果我还需要用户可配置的规则,那么如何最好地实现读写操作的字段级访问权限?我正在使用MSSQL Server 2016和EF 6 根据该表,我们可能有以下用例,这些用例描述了特定角色或组可见的字段: 1) 公共数据的默认权限组 2) 基于实体的权限组 3) 自定义基于字段的权限组 要求是,隐藏数据必须区别于空值,规则/权限必须是用户可配置的。我还需要对列表进行分页,这需要对可见数据进行正确排序。为此,我需要一种处理数据类型的方法。例如,构造年是

假设我们有一个关于某些车型的信息表,例如:

如果我还需要用户可配置的规则,那么如何最好地实现读写操作的字段级访问权限?我正在使用MSSQL Server 2016和EF 6

根据该表,我们可能有以下用例,这些用例描述了特定角色或组可见的字段:

1) 公共数据的默认权限组

2) 基于实体的权限组

3) 自定义基于字段的权限组

要求是,隐藏数据必须区别于空值,规则/权限必须是用户可配置的。我还需要对列表进行分页,这需要对可见数据进行正确排序。为此,我需要一种处理数据类型的方法。例如,构造年是不可为空的DateTime,但当字段不可见时,需要将其设置为DateTime.MinValue之类的默认值。在处理位(布尔)值时,这变得更具挑战性:-)


我目前正在考虑一种方法,要么使用表值函数(在我的场景中似乎更难动态实现),要么使用一个单独的缓存层来保存整个数据,我需要与数据库保持同步。

实现目标的一个简单方法是创建一个设置表,其中,按组指定每个字段的可见性

首先,您需要制作一个组(针对品牌)表,如下所示:

 public class Group
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
 public IEnumerable<TableVm> GetTableByGroupWithPag(int groupId, ApplicationDbContext context,int pageNumber, int rowsPerPage)
        {

            var table = context.Tables.Skip((pageNumber-1)*rowsPerPage).Take(rowsPerPage).ToList();

            var visibility = context.TableVisibilitySettings.FirstOrDefault(x => x.GroupId == groupId);

            return table.Select(x => new TableVm
            {
                Id = x.Id,
                Group = x.Grup.Name,
                Color = x.Color,
                ConstructionYear = visibility.ContructionYear == true ? x.ConstructionYear : null,
                Power = visibility.Power == true ? x.Power : null,
                IsConvertible = visibility.IsConvertible == true ? x.IsConvertible : null
            }).ToList();
        }
public class ApplicationUser
{
 ...
 public int GroupId {get;set;}
 public virtual Group Group
}
然后需要一个用于可见性设置的表:

  public class TableVisibilitySettings
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public virtual Group Group { get; set; }
        public bool ContructionYear { get; set; }
        public bool Power { get; set; }
        public bool IsConvertible { get; set; }
    }
然后需要表格和视图模型:

public class Table
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public virtual Group Grup { get; set; }

        public string Color { get; set; }
        public int? ConstructionYear { get; set; }
        public string Power { get; set; }
        public bool? IsConvertible { get; set; }


        public IEnumerable<TableVm> GetTableByGroupType(int groupId, ApplicationDbContext context)
        {
            var table = context.Tables.ToList();
            var visibility = context.TableVisibilitySettings.FirstOrDefault(x => x.GroupId == groupId);

            return table.Select(x => new TableVm
            {
                Id = x.Id,
                Brand= x.Grup.Name,
                Color = x.Color,
                ConstructionYear = visibility.ContructionYear == true ? x.ConstructionYear : null,
                Power = visibility.Power == true ? x.Power : null,
                IsConvertible = visibility.IsConvertible == true ? x.IsConvertible : null
            }).ToList();
        }
    }
首先,您需要从表中获取要显示的行,而不仅仅是应用可见性设置

编辑:

根据您的应用程序设计和技能,有几种方法可以将组链接到用户。 最简单的方法是设置
应用程序用户
之间的
一对一
多对多
关系,如下所示:

 public class Group
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
 public IEnumerable<TableVm> GetTableByGroupWithPag(int groupId, ApplicationDbContext context,int pageNumber, int rowsPerPage)
        {

            var table = context.Tables.Skip((pageNumber-1)*rowsPerPage).Take(rowsPerPage).ToList();

            var visibility = context.TableVisibilitySettings.FirstOrDefault(x => x.GroupId == groupId);

            return table.Select(x => new TableVm
            {
                Id = x.Id,
                Group = x.Grup.Name,
                Color = x.Color,
                ConstructionYear = visibility.ContructionYear == true ? x.ConstructionYear : null,
                Power = visibility.Power == true ? x.Power : null,
                IsConvertible = visibility.IsConvertible == true ? x.IsConvertible : null
            }).ToList();
        }
public class ApplicationUser
{
 ...
 public int GroupId {get;set;}
 public virtual Group Group
}
在Group类中,您需要添加:

 public virtual ICollection<ApplicationUser> Users {get;set;}
公共虚拟ICollection用户{get;set;}
另一种方法是为每个品牌创建角色,并根据您希望每个用户读/写的品牌为其提供一个或多个角色

另一种方法是使用声明,您只需向每个用户添加一个表示groupId、groupName或品牌的声明


希望这将帮助您选择一种将用户链接到组的方式

另一个选项是使用Castle.DynamicProxy()创建代理:

类程序
{
静态void Main(字符串[]参数)
{
ProxyGenerator=新的ProxyGenerator();
var person=新人{Id=1,Name=“Bob”,年龄=40};
var proxy=generator.CreateClassProxyWithTarget(person,new-EFInterceptor(new-SecurityInfo());
WriteLine(“Id:{0},Name:{1},Age:{2}”,person.Id,person.Name,person.Age);
WriteLine(“Id:{0},Name:{1},Age:{2}”,proxy.Id,proxy.Name,proxy.Age);
}
}
公共阶层人士
{
公共虚拟整数Id{get;set;}
公共虚拟字符串名称{get;set;}
公共虚拟整数{get;set;}
}
公共接口ISecurityInfo
{
bool是允许的(字符串propName);
}
公共类安全信息:ISecurityInfo
{
允许使用公共bool(字符串propName)
{
return propName!=姓名(Person.Age);
}
}
类interceptor:Castle.DynamicProxy.IInterceptor
{
私有只读ISecurityInfo securityInfo;
公共eInterceptor(ISecurityInfo)
{
this.securityInfo=info;
}
公共无效拦截(IInvocation调用)
{
if(invocation.Method.Name.StartsWith(“get_”))
{
var propName=invocation.Method.Name.Replace(“get_u3;”,即“”);
HandleAccess(调用,propName);
}
if(invocation.Method.Name.StartsWith(“set_”))
{
var propName=invocation.Method.Name.Replace(“set_”);
HandleAccess(调用,propName);
}
}
私有void HandleAccess(IInvocation调用,字符串propName)
{
如果(!securityInfo.IsAllowed(propName))
{
invocation.ReturnValue=GetDefault(invocation.Method.ReturnType);
}否则
{
invocation.procedure();
}
}
公共静态对象GetDefault(类型)
{
if(type.IsValueType)
{
返回Activator.CreateInstance(类型);
}
返回null;
}
}

如果您关心此解决方案的安全性,我认为您不能缓存,除了有问题的用户。这就是说,我将通过返回由位
可见
字段组成的数据来实现这一点,并将不可见的字段设置为空。如果您将数据拉入临时表,找出它们应该能够看到的内容(根据需要更新
visible
字段),然后
null
取出值,那么您就能够向UI清楚地指示某些内容是
null
,因为它是以这种方式存储的(
visible
为1,但字段中有
null
),或者因为用户不能看到它(
visible
为0,数据为
null


是的,这有点枯燥。这也意味着你没有向用户返回他们不应该看到的数据的风险-如果不应该显示数据,你的UI开发人员/维护人员将无法显示数据。如果你在做UI工作,你会更安全,因为你不会忘记你的意图mewhere down the road and expose the data.

因为您需要像这样配置权限(请参阅我的评论),所以问题与EF无关-这与您有关
var permissionConfigEntry.Permission = CarFieldPermission.ViewConstructionYear 
    | CarFieldPermission.ViewPower
;
public IEnumerable<Car> LoadCars(User user, int pageIndex, int pageSize)
{
    var result = db.Cars
        .Skip((pageIndex - 1) * pageSize)
        .Take(pageSize)
        .ToArray()
    ;

    var carsInInterest = result.Select(c => c.Id).ToArray();

    var allThePermissions = db.PermissionConfiguration
        .Where(pc => pc.User.Equals(user))
        .Where(pc => carsInInterest.Contains(pc.CarId))
        .ToArray()
    ;

    foreach (var carX in result)
    {
        var current = allThePermissions.FirstOrDefault(pc => pc.User.Equals(user) && pc.Car.Equals(carX));

        if (current != null)
        {
            if (!current.Permissions.HasFlag(CarFieldPermission.ViewConstructionYear))
            {
                carX.ConstructionYear = null;
            }
        }
    }

    return result;
}