Как я могу создать атрибут, чтобы проверить, есть ли у пользователя претензия с Identity core 2.2? - PullRequest
2 голосов
/ 16 июня 2019

У меня есть приложение, написанное с C# вверху в ASP.NET Core 2.2 framework.

Я хочу проверить, есть ли у пользователя претензии, прежде чем я разрешу им доступ к действию.

Я создал AuthorizationHandler, чтобы проверить, есть ли у пользователя претензия, например

public class ClaimExistanceHandler : AuthorizationHandler<MustHaveClaimRequirement>
    {
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MustHaveClaimRequirement requirement)
        {
            if (context == null
                || context.User == null
                || context.User.Identity == null
                || !context.User.Identity.IsAuthenticated
                || requirement == null
                || string.IsNullOrWhiteSpace(requirement.Type)
                || context.User.HasClaim(requirement.Type, requirement.Value))
            {
                context.Fail();

            }
            else
            {
                context.Succeed(requirement);
            }

            await Task.Yield();
        }
    }
}

тогда требование следующее

public class MustHaveClaimRequirement : IAuthorizationRequirement
{
    public string Type { get; set; }
    public string Value { get; set; }

    public MustHaveClaimRequirement(string type, string value)
    {
        Type = type;
        Value = value;
    }
}

Но как я могу назвать это требование атрибутом? Например HasPermission("do something", "1")

Кажется, что мой класс HasPermission должен реализовывать AuthorizeAttribute, но не уверен, как бы я вызвал обработчик из атрибута.

Ответы [ 3 ]

2 голосов
/ 17 июня 2019

Для начала необходимо зарегистрировать политику и связанные с ней претензии:

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

    services.AddAuthorization(options =>
    {
        //Scenario 0: Policy requires Claim0 without care what the value is
        options.AddPolicy("MyPolicy0", policy => policy.RequireClaim("Claim0"));

        //Scenario 1: Policy requires Claim1 with value ClaimValue1_1 OR ClaimValue 1_2
        options.AddPolicy("MyPolicy1", policy => policy.RequireClaim("Claim1", "ClaimValue1_1", "ClaimValue1_2"));

        //Scenario 2: Policy requires Claims2 AND Claim3 with particular values
        options.AddPolicy("MyPolicy2", policy => {
            policy.RequireClaim("Claim2", "ClaimValue2");
            policy.RequireClaim("Claim3", "ClaimValue3"));
        }

        //Scenario 3: Policy requires Claims4 OR Claim5 with particular values
        options.AddPolicy("MyPolicy3", policy => {
            policy.RequireAssertion(ctx =>
            {
              return ctx.User.HasClaim("Claim4", "ClaimValue4") ||
                     ctx.User.HasClaim("Claim5", "ClaimValue5");
            })
        }
    });
}

Затем примените эти проверки всякий раз, когда вам нужно (может применяться на уровне контроллера или действия):

[Authorize(Policy = "Policy1")]
public class HomeController : Controller
{

    [Authorize(Policy = "Policy2")]
    public ActionResult MyAction()
    {
       ...
    }

    [Authorize(Policy = "Policy3")]
    public ActionResult MyAnotherAction()
    {
       ...
    }

    [AllowAnonymous]
    public ActionResult NotSecuredAtAll()
    {
       ...
    }
}

Не забывайте, что если вы применяете несколько политик к контроллеру или действию, то все политики должны пройти, прежде чем будет предоставлен доступ (если только они не имеют атрибута AllowAnonymous).

Подробнее о авторизации на основе утверждений в ASP.NET Core 2.2

2 голосов
/ 18 июня 2019

Мне удалось написать атрибут, который позволяет проверять наличие претензии после получения отзывов от nlawalker и ivamax9

В заключение, класс HasPermissionAttribute принимает claimType, а необязательный claimValue затем создает имя политики. Класс атрибута выглядит следующим образом

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HasPermissionAttribute : AuthorizeAttribute   
{
    public const string Policy_Prefix = "HasClaim";
    public const string Policy_Glue = ".";

    public HasPermissionAttribute(string type, string value = null)
    {
        Policy = GetPolicyValue(type, value);
    }

    private string GetPolicyValue(string type, string value)
    {
        if (string.IsNullOrWhiteSpace(type))
        {
            throw new ArgumentNullException($"{type} cannot be null.");
        }

        List<string> parts = new List<string> { type.Replace(Policy_Glue, "_").Trim() };

        if (!string.IsNullOrWhiteSpace(value))
        {
            parts.Add(value.Replace(Policy_Glue, "_"));
        }

        string policy = $"{Policy_Prefix}{Policy_Glue}{string.Join(Policy_Glue, parts)}";

        return policy;
    }
}

Теперь, когда у нас есть политика, применяемая через HasPermissionAttribute, теперь нам нужно взять примененную политику и зарегистрировать ее, используя AuthorizationPolicyBuilder, которая проверяет, существует ли данное требование или нет. Тем не менее, я добавил класс с именем ClaimCheckerPolicyProvider, который принимает предоставленную заявку и обрабатывает чек следующим образом

internal class ClaimCheckerPolicyProvider : IAuthorizationPolicyProvider
{
    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if (IsClaimBasePolicy(policyName))
        {
            string[] parts = GetParts(policyName);

            if (parts.Length > 0)
            {
                AuthorizationPolicyBuilder policy = GetPolicyBuilder(parts);

                return Task.FromResult(policy.Build());
            }
        }

        return Task.FromResult<AuthorizationPolicy>(null);
    }

    private bool IsClaimBasePolicy(string policyName)
    {
        return !string.IsNullOrWhiteSpace(policyName)
                        && policyName.StartsWith(HasPermissionAttribute.Policy_Prefix, StringComparison.OrdinalIgnoreCase);
    }

    private string[] GetParts(string policyName)
    {
        return policyName.Split(HasPermissionAttribute.Policy_Glue, StringSplitOptions.RemoveEmptyEntries)
                         .Where(x => !x.Equals(HasPermissionAttribute.Policy_Prefix))
                         .ToArray();
    }

    private AuthorizationPolicyBuilder GetPolicyBuilder(string[] parts)
    {
        if (parts == null)
        {
            throw new ArgumentNullException($"{nameof(parts)} cannot be null.");
        }

        var length = parts.Length;

        if (length == 0)
        {
            throw new ArgumentOutOfRangeException($"{nameof(parts)} cannot cannot be empty.");
        }

        var policy = new AuthorizationPolicyBuilder();

        if (length > 1)
        {
            return policy.RequireClaim(parts[0], parts[1]);
        }

        return policy.RequireClaim(parts[0]);
    }
}

Наконец, нам нужно зарегистрировать провайдера как услугу. В ConfigureServices класса Startup мы добавляем следующее

services.AddTransient<IAuthorizationPolicyProvider, ClaimCheckerPolicyProvider>();
2 голосов
/ 17 июня 2019

Ваша основная задача здесь - включить требование в политику , а затем использовать или создать атрибут, который может указывать эту политику со строковым именем.Как только вы это сделаете, не нужно беспокоиться о вызове обработчика самостоятельно, потому что ASP.NET Core позаботится об этом за вас.

Самый простой способ создания политик - это сделать это при запуске приложения,как задокументировано здесь .Вы создаете свои политики, а затем с помощью AuthorizeAttribute указываете, какую политику подключать к каждой конечной точке.

Однако для этого необходимо заранее определить все свои политики.Если это приведет к тому, что вам потребуется создать тонны различных политик (потому что вы будете проверять множество различных типов утверждений), и вам действительно нужно иметь 1011 * возможность иметь атрибут, который определяетИнформация о претензии, есть более динамичный способ сделать это: см. здесь .Вам необходимо создать реализацию AuthorizeAttribute, которая вставляет значения параметров (имя и тип) в строку, а также создать и зарегистрировать IAuthorizationPolicyProvider, который может интерпретировать эту строку и генерировать политику с соответствующим требованием.

РЕДАКТИРОВАТЬ: Стоит также отметить, что ASP.NET Core уже включает реализацию требований для проверки заявки: ClaimsAuthorizationRequirement.AuthorizationPolicyBuilder имеет ярлык для него (RequireClaim), поэтому вы можете быстро создавать политики, которые проверяют заявки:

services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...