Asp Net Core 3.1 Авторизация по пользовательским ролям - PullRequest
0 голосов
/ 25 января 2020

Я видел в основном тот же вопрос, на который ответили net core 2.2 на ASP Net Forum . Когда я пытался реализовать для моего приложения net core 3.1, я получаю сообщение об ошибке

System.InvalidCastException: Unable to cast object of type 'System.Security.Claims.ClaimsIdentity' to type 'System.Security.Principal.WindowsIdentity'

Ошибка происходит на RoleAuthHandler.cs, пытающемся привести Identity к WindowsIdentity.

Мне интересно, произошло ли изменение между ядром 2.2 и 3.1, которое объясняет это, или я делаю что-то не так. Любая помощь будет отличной.

Startup.cs содержит

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddSingleton<IAuthorizationHandler, RoleAuthHandler>();

    services.AddAuthorization(options =>
        options.AddPolicy(
            Role.Admin,
            policy => policy.AddRequirements(new RoleRequirement(Role.Admin))));
}

Role.cs

public static class Role
{
    public const string Guest = "Guest";
    public const string Admin = "Admin";
}

RoleRequirement.cs

public class RoleRequirement : IAuthorizationRequirement
{
    public string[] Roles { get; }

    public RoleRequirement(params string[] roles)
    {
        this.Roles = roles;
    }
}

RoleAuthHandler .cs содержит

protected override Task HandleRequirementAsync(
    AuthorizationHandlerContext context,
    RoleRequirement requirement)
{
    var wi = (WindowsIdentity)context.User.Identity;
    var groupSet = new HashSet<string>();

    if (wi.Groups != null)
    {
        foreach (var group in wi.Groups)
        {
            groupSet.Add(group.Translate(typeof(NTAccount)).ToString());
        }
    }

    string[] userRoles = roleService.GetRolesForGroup(groupSet);
    var intersectRoles = Enumerable.Intersect(userRoles, requirement.Roles, StringComparer.OrdinalIgnoreCase);

    if (intersectRoles.Count() > 0)
    {
        context.Succeed(requirement);
    }

    return Task.CompletedTask;
}

Класс контроллера

[Authorize]
[ApiController]
[Route("[controller]")]
public class InterestingController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "Something Interesting";
    }
}

1 Ответ

0 голосов
/ 07 февраля 2020

@ Sayah imad - Спасибо за напоминание. Я видел это решение где-то в посте и отклонил его, потому что информация, которую я искал, была «к какой группе принадлежит пользователь». Что я не понял, так это то, что информация была доступна в ClaimsIdentity. Ваше упоминание заставило меня дважды подумать об этом, и я нашел свой ответ:

protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        RoleRequirement requirement)
    {
        var claimsIdentity = (ClaimsIdentity)context?.User.Identity;
        var userGroups = claimsIdentity.Claims
            .Where(x => x.Type.Contains("groupsid", StringComparison.OrdinalIgnoreCase))
            .ToList();

        if (userGroups == null || !userGroups.Any())
        {
            return Task.CompletedTask;
        }

        var groupNames = new HashSet<string>();
        foreach (var group in userGroups)
        {
            var groupName = new SecurityIdentifier(group.Value)
                .Translate(typeof(NTAccount))
                .ToString();
            groupNames.Add(groupName);
        }

        var userRoles = this.authenticationService.GetRoles(groupNames.ToArray());

        // If the user is an Admin, always allow
        if (userRoles.Contains(Role.Admin))
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }

        var intersectingRoles = Enumerable.Intersect(
            userRoles,
            requirement?.Roles,
            StringComparer.OrdinalIgnoreCase);
        if (intersectingRoles?.Count() > 0)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
...