在 ASP.NET Core Identity 中使用 Authorize 属性检查多个策略之一

Posted

技术标签:

【中文标题】在 ASP.NET Core Identity 中使用 Authorize 属性检查多个策略之一【英文标题】:Checking for one of multiple policies with Authorize attribute in ASP.NET Core Identity 【发布时间】:2017-11-09 18:51:37 【问题描述】:

我已经在 ASP.NET Core 应用程序中设置了标准身份验证系统。

用户、角色、角色声明(作为权限)

在 Startup.cs 中,我为每个角色和每个权限创建一个策略。假设这将使我的视图具有完全的灵活性,以便能够说我希望此按钮显示用户是否属于具有声明 DeleteCustomer 的角色的一部分,或者用户是否属于角色超级用户。

如何使用 Authorize 属性执行 OR 条件。例如,在我的整个站点中,我希望 SuperuserRole Policy 拥有对所有内容的完全权限。

在一个动作方法上,假设我有以下内容:

[授权(Policy = "EditCustomer")]

这需要将登录用户分配给具有声明:Edit.Customer 的角色,因为我正在为声明 Edit.Customer 创建策略。这一切都很好,但是我怎么说我希望任何具有超级用户角色的用户都能够访问 EditCustomer 操作方法。超级用户作为角色在数据库中,另外添加为名为 RequireSuperUser 的策略。

【问题讨论】:

你得到这个为你工作了吗?我面临着类似的问题。 @Richard Mneyan 的回答得到了我的投票并为我工作。得到这个答案会很好,因为它会帮助其他人。 【参考方案1】:

您可以在 Startup.cs 中添加 OR 条件:

services.AddAuthorization(options => 
    options.AddPolicy("EditCustomer", policy =>
        policy.RequireAssertion(context => 
        context.User.HasClaim(c => (c.Type == "DeleteCustomer" || c.Type == "Superuser"))));
);

我遇到了类似的问题,我只希望“John Doe”、“Jane Doe”用户查看“结束合同”屏幕或仅来自“MIS”部门的任何人也能够访问同一屏幕。以下对我有用,我的声明类型为“部门”和“用户名”:

services.AddAuthorization(options => 
    options.AddPolicy("EndingContracts", policy =>
        policy.RequireAssertion(context => context.User.HasClaim(c => (c.Type == "department" && c.Value == "MIS" ||
        c.Type == "UserName" && "John Doe, Jane Doe".Contains(c.Value)))));
);

【讨论】:

太棒了,谢谢!对于登陆此处并针对 Azure 角色进行身份验证的任何人,都可以使用以下内容:policy.RequireAssertion(context => context.User.HasClaim(ClaimTypes.Role, "Analyst") || context.User.HasClaim(ClaimTypes.Role, "阅读器"))【参考方案2】:

您可以使用AuthorizationHandler 的两个不同处理程序,而IAuthorizationRequirement 的要求相同:

public class FooOrBooRequirement : IAuthorizationRequirement  

public class FooHandler : AuthorizationHandler<FooOrBooRequirement>

    protected override Task HandleRequirementAsync(AuthorizationContext context, FooOrBooRequirement requirement)
    
        if (context.User.HasClaim(c => c.Type == "Foo" && c.Value == true))
        
            context.Succeed(requirement);
            return Task.FromResult(0);
        
    


public class BooHandler : AuthorizationHandler<FooOrBooRequirement>

    protected override Task HandleRequirementAsync(AuthorizationContext context, FooOrBooRequirement requirement)
    
        if (context.User.HasClaim(c => c.Type == "Boo" && c.Value == true))
        
            context.Succeed(requirement);
            return Task.FromResult(0);
        
    

因此,在AddPolicy 中,您将只有一个可以由FooHandlerBooHandler 满足的要求:

services.AddAuthorization(authorizationOptions =>

    authorizationOptions.AddPolicy(
        "MustBeFooOrBoo",
        policyBuilder =>
        
            policyBuilder.RequireAuthenticatedUser();
            policyBuilder.AddRequirements(new FooOrBooRequirement());
        );
);

请注意,AuthorizationHandler 提供了更强大的自定义策略,因此它仅适用于需要更复杂身份验证的特殊情况,例如,如果您想确定用户是否是资源所有者。

对于仅基于索赔的简单政策,我建议使用@richard-mneyan 的答案。

【讨论】:

这对我来说非常有用,谢谢! 嘿@MattG,很高兴我能帮上忙【参考方案3】:

我们可以将 DBContext(如果使用 EF,或者如果不使用用户存储库)或 HttpContextAccessor 注入到AuthorizationHandler 来实现这一点。

通过注入 EF 上下文(或用户存储库),我们可以查询数据库以确定当前用户是否为超级用户。

或者通过注入HttpContextAccessor,我们可以确定用户是否是超级用户。这种方法比第一种方法更快,但取决于用户上下文是否具有所需的信息。

要实现这一点,假设您拥有的是EditCustomerRequirementEditCustomerAuthorizationHandler

public class EditCustomerAuthorizationHandler: AuthorizationHandler<EditCustomerRequirement>

     readonly AppDbContext _context;
     readonly IHttpContextAccessor _contextAccessor;

     public EditCustomerAuthorizationHandler(DbContext c, IHttpContextAccessor ca)
     
         _context = c;
         _contextAccessor = ca;
     

     protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, EditCustomerRequirement requirement)
     
         //here we have the access to DB and HttpContext
         //we can get the information if the user is superuser from either db or httpContext 
         var isSuperUser = await IsSuperUser(_context);
         //or
         var isSuperUser = IsSuperUser(_contextAccessor);
         if(isSuperUser) 
         
             context.Succeed(requirement);
         
         else
         
            //the current implementation of EditCustomer Policy
         
     

IsSuperUser 是一种我们可以用来从 db 上下文或 http 上下文中获取信息的方法。

【讨论】:

以上是关于在 ASP.NET Core Identity 中使用 Authorize 属性检查多个策略之一的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET CORE 5.0 Identity 显示当前登录用户

Asp.Net core MVC6如何在Identity 3中初始添加角色

如何按照存储库模式在 Asp.Net Core 5.0 项目上实现 .Net Core Identity?

在 ASP.Net Core Identity 中刷新用户 cookie 票证

ASP.NET Core Identity 系列之六

在 ASP.NET Core 中使用基于本地存储的 JWT-Token 更改用户密码(ASP.Identity)