如何使用自定义的授权属性来为角色和特定用户授权?

35

我有一个Action方法

[Authorize(Roles="Admin")]
public ActionResult EditPosts(int id)
{
    return View();
}
在我的案例中,我需要授权管理员以便他们可以编辑帖子,但是(这里是有趣的部分),我还需要允许创建帖子的用户(普通用户)能够编辑该帖子。那么如何过滤掉创建帖子的用户以及管理员,但保留其他未经授权的用户呢?我将PostEntry id作为路由参数接收,但那是在属性之后,并且属性仅接受常量参数,看起来非常困难,非常感谢您的答复。祝好!

1
由于在查找之前可能无法知道是谁创建了帖子,最好在填充对象后包含此逻辑。否则,如果将其实现为方面,则可能需要两次查找帖子(一次用于授权,一次用于编辑)。 - Davin Tryon
说得好,确实我会再次查找他的ID是否在角色条目中,并再次执行我的控制器逻辑。有什么想法可以只访问一次数据库吗? - Freeman
再仔细思考一下,如果您使用了一个好的ORM,并在授权调用之前设置了上下文(不确定这一部分),那么它应该缓存在第一级缓存中。然后,您就不应该在第二次hydrate时看到任何影响。 - Davin Tryon
我确实在使用Entity Framework 4.0,但我不认为它会缓存任何内容。 - Freeman
2个回答

74

你可以编写自定义的授权属性:

public class AuthorizeAdminOrOwnerOfPostAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            // The user is not authenticated
            return false;
        }

        var user = httpContext.User;
        if (user.IsInRole("Admin"))
        {
            // Administrator => let him in
            return true;
        }

        var rd = httpContext.Request.RequestContext.RouteData;
        var id = rd.Values["id"] as string;
        if (string.IsNullOrEmpty(id))
        {
            // No id was specified => we do not allow access
            return false;
        }

        return IsOwnerOfPost(user.Identity.Name, id);
    }

    private bool IsOwnerOfPost(string username, string postId)
    {
        // TODO: you know what to do here
        throw new NotImplementedException();
    }
}

然后在你的控制器操作中使用它进行装饰:

[AuthorizeAdminOrOwnerOfPost]
public ActionResult EditPosts(int id)
{
    return View();
}

为什么会出现“现在已指定id => 我们不允许访问”这个错误? - Royi Namir
现在我明白你需要更改:_// 现在已指定ID_ 为未指定ID...(似乎如果有ID,我们不允许访问) - Royi Namir
谢谢 - 直戳问题的核心,避免了冗长无用的废话! - cyclical
这篇文章非常有用,因为它帮助我实现了在MVC应用程序中允许除“用户”角色外的所有角色访问某些控制器。 - Sorangwala Abbasali

9

我知道您已经接受了一个答案,而且这是一段时间以前发布的。(顺便说一句:非常好的添加自定义属性的答案)然而,我想指出以下几点:

如果您只在单个方法中使用此属性,则这不是一个好的实现方式。相反,您应该有:

[Authorize]   // Just make sure they are auth'ed at all.
public ActionResult EditPosts(int id)
{
    Post SomePost = findPostByID (id);   // However you do it - single lookup of post

    if (!user.IsInRole("Admin") &&  !{IsOwnerOfPost(post)} )  Return Not Authorized

  ... Edit post code here
}

这样做的好处有:
  1. 没有额外的类,避免日后出现人们不知道该在哪里使用它的情况。
  2. 没有无法在其他地方使用的类(使用自定义属性无法实现重用)
  3. 性能更好:只需一次获取帖子。
  4. 对于他人来说,更容易阅读/理解其工作原理。没有需要查找的神奇代码。
  5. 即使 HttpContextBase 类不存在或其他用于获取 Id 参数的技巧消失了,多年之后,代码仍然有效…

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接