基于资源的授权
有些场景下,授权需要依赖于要访问的资源,例如:每个资源通常会有一个创建者属性,我们只允许该资源的创建者才可以对其进行编辑,删除等操作,这就无法通过[Authorize]
特性来指定授权了。因为授权过滤器会在我们的应用代码,以及MVC的模型绑定之前执行,无法确定所访问的资源。此时,我们需要使用基于资源的授权,下面就来演示一下具体是如何操作的。
定义资源Requirement
在基于资源的授权中,我们要判断的是用户是否具有针对该资源的某项操作,因此,我们先定义一个代表操作的Requirement:
public class MyRequirement : IAuthorizationRequirement{
public string Name { get; set; }
}
可以根据实际场景来定义需要的属性,在本示例中,只需要一个Name
属性,用来表示针对资源的操作名称(如:增查改删等)。
然后,我们预定义一些常用的操作,方便业务中的调用:
public static class Operations{
public static MyRequirement Create = new MyRequirement { Name = "Create" };
public static MyRequirement Read = new MyRequirement { Name = "Read" };
public static MyRequirement Update = new MyRequirement { Name = "Update" };
public static MyRequirement Delete = new MyRequirement { Name = "Delete" };
}
上面定义的 MyRequirement 虽然很简单,但是非常通用,因此,在 ASP.NET Core 中也内置了一个OperationAuthorizationRequirement
:
public class OperationAuthorizationRequirement : IAuthorizationRequirement{ public string Name { get; set; }
}
在实际应用中,我们可以直接使用OperationAuthorizationRequirement
,而不需要再自定义 Requirement,而在这里只是为了方便理解,后续也继续使用 MyRequirement 来演示。
实现资源授权Handler
每一个 Requirement 都需要有一个对应的 Handler,来完成授权逻辑,可以直接让 Requirement 实现IAuthorizationHandler
接口,也可以单独定义授权Handler,在这里使用后者。
在本示例中,我们是根据资源的创建者来判断用户是否具有操作权限,因此,我们定义一个资源创建者的接口,而不是直接依赖于具体的资源:
public interface IDocument{ string Creator { get; set; }
}
然后实现我们的授权Handler:
public class DocumentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, IDocument>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement, IDocument resource) { // 如果是Admin角色就直接授权成功
if (context.User.IsInRole("admin"))
{
context.Succeed(requirement);
} else
{ // 允许任何人创建或读取资源
if (requirement == Operations.Create || requirement == Operations.Read)
{
context.Succeed(requirement);
}
else
{ // 只有资源的创建者才可以修改和删除
if (context.User.Identity.Name == resource.Creator)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
} return Task.CompletedTask;
}
}
在前面章节的《自定义策略》示例中,我们继承的是AuthorizationHandler<NameAuthorizationRequirement>
,而这里继承了AuthorizationHandler<OperationAuthorizationRequirement, Document>
,很明显,比之前的多了resource
参数,以便用来实现基于资源的授权。
如上,我们并没有验证用户是否已登录,以及context.User
是否为空等。这是因为在 ASP.NET Core 的默认授权中,已经对这些进行了判断,我们只需要在要授权的控制器上添加