在修改模型之前的我的控制器中(更新或删除)我试图验证执行操作的用户实际上是否拥有他们试图修改的对象。
我目前正在方法级别执行此操作,似乎有点多余。
[HttpPost]
public ActionResult Edit(Notebook notebook)
{
if (notebook.UserProfileId != WebSecurity.CurrentUserId) { return HttpNotFound(); }
if (ModelState.IsValid)
{
db.Entry(notebook).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(notebook);
}
是否存在可以在各种模型中重复使用的通用方法?
是否可以使用ActionFilter
?
答案 0 :(得分:5)
过滤方法可能如下所示:
public class VerifyOwnership : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
foreach(var parameter in filterContext.ActionParameters)
{
var owned = paramter.Value as IHaveAnOwner;
if(owned != null)
{
if(owned.OwnerId != WebSecurity.CurrentUserId)
{
// ... not found or access denied
}
}
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
假设像Notebook这样的模型实现了特定的接口。
public interface IHaveAnOwner
{
int OwnerId { get; set; }
}
Blowdart有一个很好的观点,即用户可以在帖子中篡改OwnerId。我相信他们也可以篡改他们的身份证,但他们必须知道其他用户的票并且篡改两者以获得与其他用户匹配的ID,我相信。
答案 1 :(得分:3)
过滤器听起来很不错,但有些限制。如果你有这样的过滤器会很好:
[RequireOwnership<Notebook>(n => n.UserProfileId)]
...但Attributes
仅限于允许哪些数据类型,我认为也不允许使用泛型。因此,您可以通过使用反射检查模型属性来使用[RequireOwnership]
属性,或者您可以创建自定义验证器,其中模型如下所示:
public class Notebook
{
[MatchesCurrentUserId]
public int UserProfileId { get; set; }
}
然后您的ModelState.IsValid
检查就足够了。
修改强>
另一种选择发生在我身上。您可以将过滤器与模型上的属性结合使用(不必是ValidationAttribute
)。过滤器可以检查您的请求模型并检查[MatchesCurrentUserId]
的属性,并与当前用户ID进行比较。
答案 2 :(得分:2)
我可以看到你所拥有的一个问题 - 你依靠用户输入来执行安全检查。
考虑您的代码
if (notebook.UserProfileId != WebSecurity.CurrentUserId)
Notebook来自模型绑定。所以UserProfileId来自模型绑定。并且你可以非常高兴地假装 - 例如我使用Firefox的TamperData来改变隐藏的UserProfileId的值以匹配我的登录并离开我去。
我最终做的事情(在服务中,而不是控制器中)是根据传递的唯一ID从数据库中拉回记录(例如,编辑/ 2将使用2),然后检查用户.Identity.Name(以及传递的标识参数)对我在返回的数据库记录中的当前所有者字段。
因为我从数据库(存储库,无论如何)撤回,因此属性对此不起作用,并且我不确定在属性的方法中你是否足够通用。
答案 3 :(得分:1)
过去我做过这样的事情时,实际上并没有好多少。对于我们的项目,我们将有一个接受Notebook
对象的方法,并针对当前登录的用户进行检查。
您可以使用所有不同的对象类型重载此方法,并使用一致的方法来检查访问权限。对不起,这是我所知道的最好方式。
[HttpPost]
public ActionResult Edit(Notebook notebook)
{
if(!SessionUser.LoggedInUser.CheckAccess(notebook))
return HttpNotFound();
//Other code...
}
P.S。 SessionUser
是一个自定义类,我们基本上只是为了管理当时登录的用户。您可以编写类似的东西,但不要指望默认情况下它在.NET中。