【发布时间】:2017-03-23 17:30:18
【问题描述】:
问题
我有一个 WCF OData 服务,由实体框架和 SQL 提供支持,我正在尝试为此实施行级访问控制。
考虑以下数据模型,其中用户有订单,而这些订单有商品:
┌───────┐ ┌────────┐ ┌────────┐
│User │ │Order │ │Item │
├───────┤ ├────────┤ ├────────┤
│UserID │ │OrderID │ │ItemID │
└───────┘ │UserID │ │OrderID │
└────────┘ └────────┘
应限制用户只能查看自己的订单以及这些订单的订单商品。
为了实现这个,我使用WCF query interceptors,一个基本的实现是:
// currentUser is request-scoped User entity of the logged in user
[QueryInterceptor("Orders")]
public Expression<Func<Order, bool>> OrderInterceptor()
{
return order => order.UserID == currentUser.UserID;
}
[QueryInterceptor("Items")]
public Expression<Func<Item, bool>> ItemInterceptor()
{
return item => item.Order.UserID == currentUser.UserID;
}
但是,我想调用拦截器内部的通用代码,因为实体很多,而且访问控制规则不仅仅是匹配用户 ID。
可能的解决方案
My previous question 处理了从拦截器调用公共方法,以返回多种类型的表达式。提供的答案解决了这个问题,但事实证明这只是冰山一角。
使用界面
public interface ICommonInterface
{
int GetUserID();
}
public partial class Item : ICommonInterface
{
public int GetUserID()
{
return this.Order.UserID;
}
}
[QueryInterceptor("Items")]
public Expression<Func<Item, bool>> ItemInterceptor()
{
return CommonFilter<Item>();
}
private Expression<Func<T, bool>> CommonFilter<T>() where T : class, ICommonInterface
{
return entity => entity.GetUserID() == currentUser.UserID
}
除了 LINQ 到实体onlysupportsinitializers,members,andnavigationproperties。这意味着我添加的用于获取用户 ID 的任何属性或方法都将不起作用,因此这些已失效。
将表达式放入实体类
与其让每个实体返回其关联的用户 ID,不如让它自己实现过滤器。由于过滤器对类型而不是实例进行操作,因此它必须是静态的。
public partial class Item : ICommonInterface
{
public static Expression<Func<Item, bool>> CurrentUserFilter(int userID)
{
return item => item.Order.UserID == userID;
}
}
除了interfacesdon't allowstatic methods,所以我们必须用抽象基类替换它。除了抽象类alsodon't allowstatic methods。
重点是对多个实体类型应用相同的过滤逻辑,所以如果无法从CommonFilter调用过滤表达式方法,则将其放在实体类中没有多大意义。
将 UserID 列添加到所有表
这严重破坏了数据库的规范化,是不可取的。
忘记表格并使用视图
不使用 Items 表,而是创建一个 Items 视图,在每一行中包含用户 ID。我还没有尝试过,因为这是一个很大的变化。
所以问题是,如何在我的服务中实现记录级别的安全性?
【问题讨论】:
标签: c# entity-framework wcf linq-to-entities access-control