Пользовательский атрибут авторизации — ASP .NET Core 2.2

#c# #asp.net-core

#c# #asp.net-core

Вопрос:

Я хочу создать пользовательский атрибут авторизации, чтобы иметь возможность отправлять персонализированный ответ в случае сбоя. Есть много примеров, но я не смог найти то, что я ищу. При регистрации политики я добавляю «утверждение». Возможно ли получить доступ к этому зарегистрированному утверждению в пользовательском атрибуте без необходимости передавать утверждение по параметру? или возможно узнать, произошла ли проверка утверждения, и если нет, вернуть персонализированный ответ? Спасибо!

 public static void AddCustomAuthorization(this IServiceCollection serviceCollection)
{
    serviceCollection.AddAuthorization(x =>
    {
        x.AddPolicy(UserPolicy.Read,
            currentPolicy => currentPolicy.RequireClaim(UserClaims.Read));
    });
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext authorizationFilterContext)
    {
        if (authorizationFilterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (!authorizationFilterContext.HttpContext.User.HasClaim(x => x.Value == "CLAIM_NAME")) // ACCESS TO REGISTER CLAIM => currentPolicy => currentPolicy.RequireClaim(UserClaims.Read)
            {
                authorizationFilterContext.Result = new ObjectResult(new ApiResponse(HttpStatusCode.Unauthorized));
            }
        }
    }
}

[HttpGet]
[CustomAuthorizeAttribute(Policy = UserPolicy.Read)]
public async Task<IEnumerable<UserDTO>> Get()
{
    return ...
}
  

Комментарии:

1. вы должны иметь возможность получить доступ к утверждению из метода OnAuthorization

Ответ №1:

Вы можете использовать IAuthorizationPolicyProvider для получения политики, а затем использовать ClaimsAuthorizationRequirement.ClaimType для получения имени утверждения. И поскольку у него есть async API, его лучше использовать IAsyncAuthorizationFilter вместо IAuthorizationFilter . Попробуйте это:

 public class CustomAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
    public async Task OnAuthorizationAsync(AuthorizationFilterContext authorizationFilterContext)
    {
        var policyProvider = authorizationFilterContext.HttpContext
            .RequestServices.GetService<IAuthorizationPolicyProvider>();
        var policy = await policyProvider.GetPolicyAsync(UserPolicy.Read);
        var requirement = (ClaimsAuthorizationRequirement)policy.Requirements
            .First(r => r.GetType() == typeof(ClaimsAuthorizationRequirement));

        if (authorizationFilterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (!authorizationFilterContext.HttpContext
              .User.HasClaim(x => x.Value == requirement.ClaimType))
            {
                authorizationFilterContext.Result = 
                   new ObjectResult(new ApiResponse(HttpStatusCode.Unauthorized));
            }
        }
    }
}
  

Комментарии:

1. Привет @AlbertK. Спасибо за ответ. Он мне очень помог. Мне пришлось изменить только 3 строки кода. 1) authorizationFilterContext.HttpContext.RequestServices. GetRequiredService <IAuthorizationPolicyProvider>() 2) Политика авторизации. Требования. OfType<ClaimsAuthorizationRequirement>(). FirstOrDefault() 3) policyProvider. GetPolicyAsync(политика)

Ответ №2:

Этот атрибут принимает массив строк, который был необходим в моем случае. Мне нужно было передать роли разных пользователей этому атрибуту и вернуть результат на основе некоторой пользовательской логики.

 public class CustomAuthFilter : AuthorizeAttribute, IAuthorizationFilter
{
    public CustomAuthFilter(params string[] args)
    {
        Args = args;
    }

    public string[] Args { get; }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        //Custom code ...

        //Resolving a custom Services from the container
        var service = context.HttpContext.RequestServices.GetRequiredService<ISample>();
        string name = service.GetName(); // returns "anish"

        //Return based on logic
        context.Result = new UnauthorizedResult();
    }
}
  

Вы можете украсить свой контроллер этим атрибутом, как показано ниже

  [CustomAuthFilter("Anish","jiya","sample")]
 public async Task<IActionResult> Index()
  

Sample — это класс, который возвращает жестко закодированную строку

 public class Sample : ISample
{
    public string GetName() => "anish";
}
  

Услуги.AddScoped(); //Register является примером, образец как ограниченный.

ДЛЯ АСИНХРОННОЙ ПОДДЕРЖКИ используйте IAsyncAuthorizationFilter

 public class CustomAuthFilter : AuthorizeAttribute, IAsyncAuthorizationFilter
{

    public CustomAuthFilter(params string[] args)
    {
        Args = args;
    }

    public string[] Args { get; }

    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        //DO Whatever...

        //Resolve Services from the container
        var service = context.HttpContext.RequestServices.GetRequiredService<ISample>();
        var httpClientFactory = context.HttpContext.RequestServices.GetRequiredService<IHttpClientFactory>();
        string name = service.GetName();

        using var httpClient = httpClientFactory.CreateClient();

        var resp = await httpClient.GetAsync("https://jsonplaceholder.typicode.com/todos/1");
        var data = await resp.Content.ReadAsStringAsync();

        //Return based on logic
        context.Result = new UnauthorizedResult();

    }
}
  

Надеюсь, это поможет..

Комментарии:

1. Спасибо за совет по использованию IAsyncAuthorizationFilter поддерживаемого метода Async (например, внешние вызовы для проверки будут асинхронными).