【问题标题】:How to keep customer data segregated如何保持客户数据隔离
【发布时间】:2013-11-26 06:57:35
【问题描述】:

作为一个简化的例子,我有用户、产品和客户。允许用户访问某些产品和某些客户。

我正在使用 edmx 文件将我的 SQL Server 映射到我的代码并使用 linq 获取数据。一个典型的查询可能如下所示:

from prod in ctx.Products
join userProduct in ctx.UserProduct
on prod.Id equals userProduct.ProductId

join user in ctx.UserProfile
on userProduct.UserId equals user.Id

where user.UserName == username  // <-- username is a method parameter
select new Product
{
    Id = prod.Id,
    DisplayText = prod.UserFriendlyText
}

每次我需要数据库中的数据时,我都必须加入访问权限表以排除用户无权访问的数据。这意味着如果有人(最终会发生)忘记加入访问表,用户将看到太多。有没有办法改为包含数据,这样如果我忘记了访问表,什么都不会显示?

我也一直在考虑将不同的客户分离到不同的数据库中,因为他们的数据永远不会相互关联,如果我在客户之间泄露数据将是一场小灾难。在同一客户的用户之间泄漏产品是不好的,但不是那么严重。

如果重要的话,我在 C# MVC4 CQRS 架构中,读写端之间最终保持一致。

我已经检查过堆栈溢出是否有类似的问题,但我只能找到这个没有答案的问题:

【问题讨论】:

    标签: c# entity-framework cqrs


    【解决方案1】:

    使用Repository pattern 并强制您的开发人员使用它来调用数据库怎么样?这将促进代码重用并提高应用程序的可维护性。

    因为将从存储库中调用一个方法,所以您可以控制与数据库交互的代码并强制保持一致性,这样您就可以确保始终使用访问表,并按您的意愿使用。

    【讨论】:

    • 我想一个简单的存储库模式,比如接受用户名作为参数并只返回您在IQueryable 中可以访问的产品的方法可以工作,但我仍然觉得我错误地可以查询未加入访问权限表的产品。
    • 然后使您的开发人员无法直接查询产品。例如,只为您的模块和数据库 API 提供 Repository 接口(然后使用依赖注入在运行时提供实际的 Repository 服务)。
    【解决方案2】:

    我的数据库中有类似的问题。我的 90% 的实体都是“组织依赖的”。我的方法使用具有以下方法的通用基础存储库:

        public virtual T Find(int id)
        {
            T e = Context.Set<T>().Find(id);
    
            var od = e as OrganisationDependent;
            if (od != null && od.OrganisationID != CurrentOrganisationID)
                return null;
    
            if (e == null)
                return null;
    
            return e;
        }
    

    “全部”方法是一个特殊问题。由How to conditionally filter IQueryable解决

        private static readonly PropertyInfo _OrganisationIDProperty = ReflectionAPI.GetProperty<OrganisationDependent, int>(o => o.OrganisationID);
    
        private static Expression<Func<TOrg, bool>> FilterByOrganization<TOrg>(int organizationId)
        {
            //The FilterByOrganisation method uses the LINQ Expressions API to generate an expression that will filter on organisation id
            //This avoids having to cast the set using .AsEnumerable().Cast<OrganisationDependent>().Where(x => x.OrganisationID == CurrentOrganisationID).AsQueryable().Cast<T>();
            //https://stackoverflow.com/questions/20052827/how-to-conditionally-filter-iqueryable-by-type-using-generic-repository-pattern
            var item = Expression.Parameter(typeof(TOrg), "item");
            var propertyValue = Expression.Property(item, _OrganisationIDProperty);
            var body = Expression.Equal(propertyValue, Expression.Constant(organizationId));
            return Expression.Lambda<Func<TOrg, bool>>(body, item);
        }
    
        public virtual IQueryable<T> All
        {
            get
            {
                if (typeof(T).IsSubclassOf(typeof(OrganisationDependent)))
                    return Context.Set<T>().Where(FilterByOrganization<T>(CurrentOrganisationID));
    
                return Context.Set<T>();
            }
        }
    

    这会关闭用户可以访问其他人数据的大部分位置。但它不会过滤导航属性。所以我必须向非组织相关实体上的所有导航属性添加代码才能做到这一点。

    我不想将我的数据分离到不同的数据库中,但有一天我会发现在不同的架构中创建按组织过滤的视图是否可行 - 与我的表具有相同的名称和结构,然后根据切换架构给用户.....哦,我想为每个新组织自动创建它们,并使用代码优先自动迁移它们......

    你可以在这里投票给Allow filtering for Include extension method

    【讨论】:

      【解决方案3】:

      如果您使用的是 CQRS 样式架构,您可以考虑为每个用户拥有一个或多个视图模型,其中包含他们有权访问的产品/客户。

      如果您发现自己必须在 CQRS 的查询端实现逻辑,这强烈表明您做错了。

      【讨论】:

      • 谢谢 Niklas 我也感觉查询端的逻辑有问题。您能否详细说明如何进行这样的每个用户拆分?例如,在处理事件时或在将事件移交给相关订阅者之前会发生这种情况吗?
      • 查询端的实际实现当然会受到您的存储引擎的影响,如果它是关系、基于文档等。
      • 在不知道将在什么上下文中使用的情况下,很难给出您需要的视图模型的示例,但这里的主要问题是不可能查询它并接收显示的结果他们无法访问的用户信息。如果您使用的是 documentDb,则可以将 userId 作为键并包含他们有权访问的产品/ID 列表。 ProductsPerCustomerViewModel 或类似的。然后,该事件需要包含有关 viewmodelupdater 应该如何知道访问权限或让您的域在创建事件之前执行该逻辑的信息
      猜你喜欢
      • 1970-01-01
      • 2013-02-09
      • 2015-07-15
      • 1970-01-01
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多