.net中基于资源的授权

oli*_*lif 12 .net asp.net-mvc authorization asp.net-web-api

假设你有一个带有GetResource(int resourceId)动作的.net web api.此操作(具有指定的id)应仅授权与该id相关联的用户(该资源可以是例如用户编写的博客帖子).

这可以通过多种方式解决,但下面给出一个例子.

    public Resource GetResource(int id)
    {
        string name = Thread.CurrentPrincipal.Identity.Name;
        var user = userRepository.SingleOrDefault(x => x.UserName == name);
        var resource = resourceRepository.Find(id);

        if (resource.UserId != user.UserId)
        {
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        return resource;
    }
Run Code Online (Sandbox Code Playgroud)

用户已通过某种机制进行身份验证的位置.

现在,假设我还希望授权用户(例如,管理员类型)使用端点(具有相同的ID).此用户与资源没有任何直接关系,但由于其类型(或角色)而具有授权.这可以通过检查用户是否为管理员类型并返回资源来解决.

有没有办法集中这种方式,以便我不必在每个动作中编写授权代码?

编辑 根据答案,我想我必须澄清我的问题.

我真正追求的是一些机制,可以使基于资源的授权成为可能,但同时允许一些用户也使用相同的端点和相同的资源.以下操作将解决此特定端点和此特定角色(管理员)的问题.

    public Resource GetResource(int id)
    {
        string name = Thread.CurrentPrincipal.Identity.Name;
        var user = userRepository.SingleOrDefault(x => x.UserName == name);
        var resource = resourceRepository.Find(id);

        if (!user.Roles.Any(x => x.RoleName == "Admin" || resource.UserId != user.UserId)
        {
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        return resource;
    }
Run Code Online (Sandbox Code Playgroud)

我所追求的是解决此问题的一些通用方法,这样我就不必编写具有相同目的的两个不同端点或在每个端点中编写特定于资源的代码.

Hon*_*Sun 5

对于基于资源的授权,我建议使用基于声明的身份并将用户 ID 嵌入作为声明。编写一个扩展方法来从身份读取声明。所以示例代码将如下所示:

public Resource GetResource(int id)
{
     var resource = resourceRepository.Find(id);
    if (resource.UserId != User.Identity.GetUserId())
    {
        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    return resource;
}
Run Code Online (Sandbox Code Playgroud)

如果你想进一步简化代码,你可以编写一个UserRepository,它知道用户数据和资源存储库来集中代码。代码如下所示:

public Resource GetResource(int id)
{
    return User.Identity.GetUserRepository().FindResource(id);
}
Run Code Online (Sandbox Code Playgroud)

对于基于角色的授权,AuthorizeAttribute将是处理它的最佳位置,您最好为此使用单独的操作或控制器。

[Authorize(Roles = "admin")]
public Resource GetResourceByAdmin(int id)
{
    return resourceRepository.Find(id);
}
Run Code Online (Sandbox Code Playgroud)

[编辑]如果OP确实想使用一个操作来处​​理不同类型的用户,我个人更喜欢使用用户存储库工厂。操作代码将是:

public Resource GetResource(int id)
{
    return User.GetUserRepository().FindResource(id);
}
Run Code Online (Sandbox Code Playgroud)

扩展方法将是:

public static IUserRepository GetUserRepository(this IPrincipal principal)
{
    var resourceRepository = new ResourceRepository();
    bool isAdmin = principal.IsInRole("Admin");
    if (isAdmin)
    {
        return new AdminRespository(resourceRepository);
    }
    else
    {
       return new UserRepository(principal.Identity, resourceRepository);
    }
}
Run Code Online (Sandbox Code Playgroud)

我不想使用 AuthorizeAttribute 进行每个资源身份验证的原因是,不同的资源可能有不同的代码来检查所有权,很难将代码集中在一个属性中,并且需要额外的数据库操作,而这并不是真正必要的。另一个问题是 AuthroizeAttribute 发生在参数绑定之前,因此您需要确保操作的参数来自路由数据。否则,例如,从帖子正文中,您将无法获取参数值。