在ASP.NET核心中的每个操作之前查询角色授权数据库

时间:2017-02-20 21:54:49

标签: c# authentication asp.net-core asp.net-core-mvc user-roles

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
}

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

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

1 个答案:

答案 0 :(得分: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
    };
});