13

在修改(更新或删除)模型之前的控制器中,我试图验证执行操作的用户是否真正拥有他们试图修改的对象。

我目前正在方法级别执行此操作,这似乎有点多余。

[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

4

4 回答 4

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 与另一个用户匹配。

于 2012-09-21T20:08:07.767 回答
3

过滤器听起来是一个不错的方法,但它有点受限。如果你能有这样的过滤器就好了:

[RequireOwnership<Notebook>(n => n.UserProfileId)]

...但Attributes在允许的数据类型方面受到限制,而且我也不认为泛型是允许的。因此,您可以拥有一个[RequireOwnership]通过使用反射检查模型属性来工作的属性,或者您可以创建一个自定义验证器,您的模型如下所示:

public class Notebook
{
    [MatchesCurrentUserId]
    public int UserProfileId { get; set; }
}

那么你的ModelState.IsValid支票就足够了。

编辑:

我想到了另一个选择。您可以将过滤器与模型上的属性结合使用(不必是 a ValidationAttribute)。过滤器可以检查您的请求模型并检查带有 的属性[MatchesCurrentUserId],并与当前用户 ID 进行比较。

于 2012-09-19T20:14:11.930 回答
3

我可以看到您所拥有的一个问题 - 您依赖用户输入来执行安全检查。

考虑你的代码

if (notebook.UserProfileId != WebSecurity.CurrentUserId)

Notebook 来自模型绑定。所以 UserProfileId 来自模型绑定。你可以很高兴地伪造它——例如,我使用 Firefox 的 TamperData 来更改隐藏的 UserProfileId 的值以匹配我的登录名,然后我就走了。

我最终要做的事情(在服务中,而不是在控制器中)是在一个帖子上根据传递的唯一 id 从数据库中拉回记录(例如,Edit/2 将使用 2),然后检查 User.Identity。我在返回的数据库记录中拥有的当前所有者字段的名称(嗯,传递的身份参数)。

因为我从数据库(存储库,无论如何)中撤回,所以属性不会为此工作,而且我不确定您是否可以在属性的方法中足够通用。

于 2012-09-20T19:18:43.433 回答
1

当我过去做过这样的事情时,它真的并没有好多少。对于我们的项目,我们将有一个方法可以接受一个Notebook对象并根据当前登录的用户检查它。

您可以使用所有不同的对象类型重载此方法,并使用一致的方法来检查访问。抱歉,这是我所知道的最好的方法。

[HttpPost]
public ActionResult Edit(Notebook notebook)
{
    if(!SessionUser.LoggedInUser.CheckAccess(notebook))
        return HttpNotFound();

    //Other code...
}

PSSessionUser是一个自定义类,我们基本上只是为了管理当时登录的人。您可以编写类似的东西,但不要期望它默认使用 .NET。

于 2012-09-19T20:16:17.327 回答