在 ASP.NET Core 中的每个操作之前查询数据库以获得角色授权

Posted

技术标签:

【中文标题】在 ASP.NET Core 中的每个操作之前查询数据库以获得角色授权【英文标题】:Query Database for Role Authorization Before Each Action in ASP.NET Core 【发布时间】:2017-07-10 08:15:10 【问题描述】:

ASP.NET Core 结合 Identity 已经提供了一种在登录后检查角色的简单方法,但我想在每个控制器操作之前查询数据库以获取当前用户的当前角色。

我已阅读 Microsoft 提供的基于角色、基于策略和基于声明的授权。 (https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) 这些解决方案似乎都没有检查每个动作的角色。这是我最近尝试以一些基于策略的授权的形式实现预期结果:

在 Startup.cs 中:

DatabaseContext context = new DatabaseContext();

services.AddAuthorization(options =>

    options.AddPolicy("IsManager",
        policy => policy.Requirements.Add(new IsManagerRequirement(context)));
    options.AddPolicy("IsAdmin",
        policy => policy.Requirements.Add(new IsAdminRequirement(context)));
);

在我的需求文件中:

public class IsAdminRequirement : IAuthorizationRequirement

    public IsAdminRequirement(DatabaseContext context)
    
        _context = context;
    

    public DatabaseContext _context  get; set; 

public class IsAdminHandler : AuthorizationHandler<IsAdminRequirement>

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsAdminRequirement requirement)
    
        // Enumerate all current users roles
        int userId = Int32.Parse(context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value);
        Roles adminRoles = requirement._context.Roles.FirstOrDefault(r => r.Name == "Administrator" && r.IsActive == true);
        bool hasRole = requirement._context.UserRoles.Any(ur => ur.UserId == userId && adminRoles.Id == ur.RoleId && ur.IsActive == true);
        // Check for the correct role
        if (hasRole)
        
            context.Succeed(requirement);
        

        return Task.CompletedTask;
    

在控制器中:

[HttpGet]
[Authorize(Policy = "IsAdmin")]
public async Task<IActionResult> Location()

    // do action here

使用此代码,需求中间件永远不会被调用,因此永远不会检查数据库。

在执行每个控制器操作之前,如何正确查询数据库以检查当前用户的角色?

【问题讨论】:

您的实际问题是什么?为什么没有什么可以解决这个问题,或者如何正确地做到这一点,或者其他什么? 已编辑以使问题更清晰。 不是您问题的答案,但可能值得一读:ardalis.com/favor-privileges-over-role-checks 关于每个操作中的角色检查。 您可以尝试覆盖控制器继承的 OnActionExecuting 操作并在那里进行身份验证 谢谢,帕特里克。这就是我最终所做的。 【参考方案1】:

我通过处理 OnTokenValidated 事件在我的应用程序(SignalR + JwtBearer)中解决了这个问题。我只是用我的数据库中的角色检查声明中的角色。如果它们不再有效,我将 TokenValidatedContext 设置为失败。

这是我的 ASP.NET Core Startup.cs 的摘录:

services.AddAuthentication(x =>

    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(x =>

    x.Events = new JwtBearerEvents
    
        OnTokenValidated = async context =>
        
            var userService = context.HttpContext.RequestServices.GetRequiredService<IUserRoleStore<User>>();
            var username = context.Principal.Identity.Name;
            var user = await userService.FindByNameAsync(username, CancellationToken.None);
            if (user == null)
            
                // return unauthorized if user no longer exists
                context.Fail("Unauthorized");
            
            else
            
                // Check if the roles are still valid.
                var roles = await userService.GetRolesAsync(user, CancellationToken.None);
                foreach (var roleClaim in context.Principal.Claims.Where(p => p.Type == ClaimTypes.Role))
                
                    if (roles.All(p => p != roleClaim.Value))
                    
                        context.Fail("Unauthorized");
                        return;
                    
                
                context.Success();
            
        ,
        OnMessageReceived = context =>
        
            var accessToken = context.Request.Query["access_token"];

            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
            
                // Read the token out of the query string
                context.Token = accessToken;
            
            return Task.CompletedTask;
        
    ;
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false,
        ValidateAudience = false
    ;
);

【讨论】:

这是一个很好的问题,但真正的问题仍然存在。让令牌验证和授权检查成为不同的东西。

以上是关于在 ASP.NET Core 中的每个操作之前查询数据库以获得角色授权的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 开发-中间件(Middleware)

ASP.NET Core 中的按操作身份验证处理程序

[Core]Asp.Net Core中的各种过滤器(授权资源操作结果异常)

在 ASP.NET Core MVC 中将自定义查询参数添加到操作 URL

[Core]Asp.Net Core中的各种过滤器(授权资源操作结果异常)

Asp.Net Core 中的@Html.Action