简体   繁体   中英

How to get params from AuthorizationHandler .NET Core

I am using an authorization handler to put custom authorization in my controller in .net core. How can I get the parameters from the controller and use it to the authorization handler?

In the old .NET I can get the parameters from HttpContext request param like this:

var eventId = filterContext.RequestContext.HttpContext.Request.Params["id"];

I am not sure how can I achieved it in .net core

public class HasAdminRoleFromAnySiteRequirement : AuthorizationHandler<HasAdminRoleFromAnySiteRequirement>, IAuthorizationRequirement
{
    public HasAdminRoleFromAnySiteRequirement()
    {

    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
        HasAdminRoleFromAnySiteRequirement requirement)
    {   

    //need to call get param from controller to used in the validation
    // something like this 
    //var eventId = filterContext.RequestContext.HttpContext.Request.Params["id"];
   // I tried the suggestion below but I can't get the parameter from routedata
   // var mvcContext = context.Resource as     Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;            

        return Task.FromResult(0);
    }
}

In ASP.NET Core 3.0 with endpoint routing enabled, you can get a route parameter value like this:

public class MyRequirementHandler : AuthorizationHandler<MyRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyRequirementHandler(IHttpContextAccessor httpContextAccessor)
    {
       _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
    {
        var routeData = _httpContextAccessor.HttpContext.GetRouteData();

        var areaName = routeData?.Values["area"]?.ToString();
        var area = string.IsNullOrWhiteSpace(areaName) ? string.Empty : areaName;

        var controllerName = routeData?.Values["controller"]?.ToString();
        var controller = string.IsNullOrWhiteSpace(controllerName) ? string.Empty : controllerName;

        var actionName = routeData?.Values["action"]?.ToString();
        var action = string.IsNullOrWhiteSpace(actionName) ? string.Empty : actionName;

        //...
    }
}

In your handler you can do the following

var mvcContext = context.Resource as 
    Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;

if (mvcContext != null)
{
    // Examine MVC specific things like routing data.
}

If you want parameter values then the authorize attribute pieces run before binding has taking place. Instead you would move to an imperative call, inside your controller. This is basically resource based authorization , your parameter is a resource.

You would inject the authorization service into your controller;

public class DocumentController : Controller
{
    IAuthorizationService _authorizationService;

    public DocumentController(IAuthorizationService authorizationService)
    {
        _authorizationService = authorizationService;
    }
}

Then write your handler slightly differently;

public class DocumentAuthorizationHandler : AuthorizationHandler<MyRequirement, Document>
{
    public override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                MyRequirement requirement,
                                                Document resource)
    {
        // Validate the requirement against the resource and identity.

        return Task.CompletedTask;
    }
}

You can see this handler takes a document, this can be whatever you like, be it an integer for an ID, or some type of view model.

Then you have access to it inside your HandleRequirementAsync() method.

Finally, you'd call it from within your controller, once binding has taken place;

if (await authorizationService.AuthorizeAsync(
    User, 
    document,     
    yourRequirement))
{
}

In ASP.NET Core 2.2, you can get a route parameter value like this:

public class MyRequirementHandler : AuthorizationHandler<MyRequirement>
{
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
    {
        var authContext = (AuthorizationFilterContext)context.Resource;
        var routeValueOfX = authContext.HttpContext.GetRouteValue("X");
        .
        .
        .
    }
}

For future reference, starting .NET Core 5.0, the HttpContext is now passed instead, so you can do:

if (context.Resource is HttpContext httpContext)
{
   var value = httpContext.GetRouteValue("key");
}

You can access parameters directly from your handler pretty easily. Now, I'm sure if think works for earlier versions of core (you should update core anyway if you can), but in core 2.0 and beyond, you can cast the context.Resource to an AuthorizationFilterContext in the HandleRequirementAsync method like so

if(context.Resource is AuthorizationFilterContext mvcContext)
{
   //do stuff to the mvcContext
}

Then, you can access the parameters like this

var value = mvcContext.HttpContext.Request.Query[key].FirstOrDefault();

where key is the parameter name you are looking for.

Or you could parse the query string like this

var queryString = mvcContext.HttpContext.Request.QueryString.ToString()
var foo = HttpUtility.ParseQueryString(queryString);   
var value = foo[key] 

again, where key is the parameter name you are looking for.

For .Net 5.0 (If you are using .NET 3.0, 3.1 then it will be better) use following:

public class MyAuthorizationPolicyHandler : AuthorizationHandler<OperationAuthorizationRequirement>
{

    public MyAuthorizationPolicyHandler()
    {
    }

    protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement requirement)
    {
        var result = false;

        if (context.Resource is Microsoft.AspNetCore.Http.DefaultHttpContext httpContext)
        {
            var endPoint = httpContext.GetEndpoint();
            if (endPoint != null)
            {
                var attributeClaims = endPoint.Metadata.OfType<MyAuthorizeAttribute>()
                //TODO: Add your logic here
            }

            if (result)
            {
                context.Succeed(requirement);
            }
        }
    }

Please refer to following related discussion: "context.Resource as AuthorizationFilterContext" returning null in ASP.NET Core 3.0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM