基于策略和资源的授权机制

身份认证完成后 HttpContext.User 中包含了用户的身份信息,但是用户的身份信息并不包含用户的权限信息。用户的权限信息需要通过授权系统来获取。

ASP.NET Core 中的授权系统

ASP.NET Core 中的授权系统是基于策略的授权系统,可以通过声明式的方式来定义授权策略。授权策略可以基于角色,也可以基于资源,也可以基于其他的条件。授权策略可以通过声明式的方式来定义,也可以通过代码的方式来定义。

authorization

使用 IAuthorizationService 接口

IAuthorizationService 接口是 ASP.NET Core 中的授权服务接口,可以通过该接口来进行授权操作。IAuthorizationService 接口提供了多个授权方法,可以通过这些方法来进行授权操作。

public interface IAuthorizationService
{
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, AuthorizationPolicy policy);
}

AuthorizationResult 是授权结果,包含了授权的结果和失败的原因。

实现授权策略提供者

使用 OperationAuthorizationRequirement 表示授权条件,将上一期视频中的权限定义转成授权条件提供给系统,一个策略中本可以有多个条件,但为了简单这里的策略中只加一个条件。

需要注意的是,多个策略是 OR 的关系,多个条件是 AND 的关系。


public class CustomAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options, IPermissionDefinitionManager permissionDefinitionManager) : DefaultAuthorizationPolicyProvider(options)

实现资源表示

public interface IAuthorizationResource
{
    string ResourceType => GetType().Name;

    string ResourceId => GetType().GetProperty("Id")?.GetValue(this)?.ToString() ?? throw new NotImplementedException();
}

设计一个表示资源的记录类

一般来说,实体类型就是一种资源,可以直接从 IAuthorizationResource 接口实现,但为了方便表示资源,可以设计一个表示资源的记录类。

public record struct ResourceInfo : IAuthorizationResource

实现授权条件处理器

public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler<OperationAuthorizationRequirement>
public class ResourcePermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler<OperationAuthorizationRequirement, IAuthorizationResource>

protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, TResource resource);

在授权处理器中使用 IPermissionChecker

public class PermissionRequirementHandler(IPermissionChecker permissionChecker) : AuthorizationHandler<OperationAuthorizationRequirement>
{
    private readonly IPermissionChecker _permissionChecker;

    public PermissionRequirementHandler(IPermissionChecker permissionChecker)
    {
        _permissionChecker = permissionChecker;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement)
    {
        if (await _permissionChecker.IsGrantedAsync(context.User, requirement.Name))
        {
            context.Succeed(requirement);
        }
    }
}