寻找防止恶意数据更改的想法:userA操纵(编辑或删除)属于userB的数据。由于我们在客户端上创建实体,因此我们需要将它们(或至少其中一些)分配给经过身份验证的用户。
例如:
var newItem = ds.createNewItem();
newItem.OwnerId(22); //this is the problem that I see.
newItem.Name("New Item");
newItem.Description("I just changed your item!");
... //and so on
ds.saveChanges();
假设我们知道在我们的API上调用SaveChanges
的用户的身份,我们如何针对该用户验证我们的实体(新的或修改的)?
首先想到的是子类EFContextProvider
,覆盖BeforeSaveEntity
并根据用户的身份检查实体OwnerId
属性。例如:
if (entityInfo.Entity.GetType() == typeof(Item)
&& (entityInfo.EntityState == EntityState.Added
|| entityInfo.EntityState == EntityState.Modified)
&& ((Item)entityInfo.Entity).OwnerId != _currentUserId) {
return false
... //and so on
如果使用这种方法,在新_currentUserId
类的构造函数中建立EFContextProvider
是否有意义?
一个想法或者更好的方法来解决这个问题?
答案 0 :(得分:5)
我认为你走在正确的轨道上。我自己一直在唠叨这一点,并走了很多路。
我们假设您已经处理了身份验证,并且可以使用IPrincipal
。您也可以自定义IIdentity
(称之为AppIdentity
),您可以为已认证用户存储UserId
。
Web Api的基础ApiController
类通过其IPrincipal
属性提供环境User
。我们将在您的自定义Breeze Web Api控制器中利用它,这可能是这样的:
[Authorize] [JsonFormatter, ODataActionFilter] public class BreezeApiController : ApiController { private readonly AppContextProvider _context; public BreezeApiController() { // pass 'User' IPrincipal to the context ctor _context = new AppContextProvider(User); } ... // one of the Query action methods [HttpGet] public IQueryable<Foo> Foos() { return _context.Foos } ...
您的自定义EFContextProvider
可能会这样开始:
public class AppContextProvider : EFContextProvider<AppDbContext> { public AppContextProvider(IPrincipal user) { UserId = ((AppIdentity) user.Identity).UserId; } public int UserId { get; private set; } ...
现在您可能希望防止UserA看到UserB的实体。因此,您的自定义Foo
可以相应地过滤,而不是允许每个EFContextProvider
出门。
public DbQuery Foos { get { // Here the 'Context' is your EF DbContext return (DbQuery) Context.Foos .Where(f => f.UserId == UserId); } }
回顾一下控制器,我们发现它的Foos
GET动作方法对于过滤器一无所知......应该如此。我们希望我们的控制器很轻松,并将业务逻辑移动到自定义EFContextProvider
及其帮助程序。
最后,高度简化的通用BeforeSaveEntity
可能如下所示:
private bool BeforeSaveEntity(EntityInfo info) { var entity = info.Entity; if (info.EntityState == EntityState.Added) { entity.UserId = UserId; return true; } return UserId == entity.UserId || throwCannotSaveEntityForThisUser(); } ... private bool throwCannotSaveEntityForThisUser() { throw new SecurityException("Unauthorized user"); }
请注意,服务器上的自定义上下文提供程序负责设置已添加实体的UserId
。无论如何,我们不会相信客户这样做。当然,它负责验证已修改和删除的实体的UserId
。
希望这会有所帮助。 请记住,这只是草图。真正的交易将更加复杂,并被重构为帮助者。