具有动态策略提供者授权的.NET Core多重身份验证

时间:2019-05-27 19:49:43

标签: c# authentication .net-core authorization

.NET Core 2.0 API:

首先,我尝试使用IAuthorizationPolicyProvider来制定动态策略:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    internal const string PolicyPrefix = "MyJwt";

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}



public class MyAuthorizationHandler: AuthorizationHandler<MyAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyAuthorizationRequirement requirement)
    {
        if(!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if(SomeLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

并添加身份验证:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

“我的授权”属性:

public class MyAuthorizeAttribute: AuthorizeAttribute
{

    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizeAttribute(string permission)
    {
        this.Permission = permission;
    }

    public string Permission
    {
        get
        {
            if(this.Policy.Length < PolicyPrefix.Length)
                return (string)null;
            return this.Policy.Substring(PolicyPrefix.Length);
        }
        set
        {
            this.Policy = PolicyPrefix + value;
        }
    }
}

我曾经用过:[MyAuthorize("anythings")]

到目前为止,一切都很好。现在,我需要添加一个新的身份验证(我的意思是两个都可以一起工作)。

因此,我更改了MyAuthorizationPolicyProvider以支持新政策:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
    {
        internal const string PolicyPrefix = "MyJwt";

        public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
        }

        public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
        {

             if(policyName == "newpolicy") //here
                return AddNewPolicy();


            if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
                return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

            var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
            authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
            return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
        }
    }

并更改添加身份验证:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    })
    .AddJwtBearer("newSchema", options =>
    {
        options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

MyNewAuthorizationHandler是:

public class MyNewAuthorizationHandler: AuthorizationHandler<MyNewAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyNewAuthorizationRequirement requirement)
    {
        if(!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if(SomeNewLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

我使用[Authorize(Policy = "newpolicy")]

现在,第一个身份验证可与MyAuthorizationHandler一起正常工作,但第二个身份验证在MyNewAuthorizationHandler中失败,因为未对用户进行身份验证并且此行返回失败:if(!context.User.Identity.IsAuthenticated)

我错过了什么?

如何进行多重身份验证,同时使用IAuthorizationPolicyProvider动态添加策略?

我知道是否将AddAuthentication更改为:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "newSchema"; //here
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});

然后第二个工作正常,第一个失败,如何验证两个模式?

1 个答案:

答案 0 :(得分:0)

经过一番搜索,我找到了multiple-authorization-policy-providers

首先更改MyAuthorizationPolicyProvider以支持所有其他策略:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    private readonly DefaultAuthorizationPolicyProvider _fallbackPolicyProvider;
    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
    {
        _fallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }


    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {

        if(!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return _fallbackPolicyProvider.GetPolicyAsync(policyName); //here return back to static policies

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}

然后修改添加身份验证的方式:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});



services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema") //add new schemas here
        .Build();

    //here: add static policies as much as you want   
    options.AddPolicy("newpolicy", new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema")
        .AddRequirements(new MyNewAuthorizationRequirement("newpolicy"))
        .Build());

});

services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();