简体   繁体   中英

Custom middleware (or authorize) for specific route in ASP .NET Core 3.1 MVC

In my ASP .NET Core 3.1 MVC app, I use endpoint routing like so

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            endpoints.MapControllerRoute(
                name: "access",
                pattern: "access/",
                defaults: new { controller = "Home", action = "Access" });
        });

So browsing to /access, launches the Access action, where the app checks if user complies with some access requirements.

if (access checks...)
{
    return View();
}

Now I would prefer having this check in a custom middleware (or possibly a custom authorize attribute) instead of having it in the Controller. So my question to you is, how should I rewrite the UseEndPoints call, to include a custom middleware for the /access area?

Authorization policy extending [Authorize]

You could do this using authorization policies. Configure these in your Startup.cs inside ConfigureServices(IServiceCollection services) like so:

services.AddAuthorization(options =>
{
    // Create your own policy and make the "access checks" in there
    options.AddPolicy("MyAccessPolicy", policy => policy.RequireAssertion(httpCtx =>
    {
        if (access checks...)
            return true;
        else
            return false;
    }));
});

Then you simply decorate your controller action with the Authorize attribute like so:

[Authorize(Policy = "MyAccessPolicy")]
public IActionResult Access()
{
    return View();
}

Now, whenever you try to go to /access this policy will run, and if the policy returns false, the user will be met with an HTTP 403 (Forbidden) status code.

Custom middleware mapped to route

In response to your comment here's an example of a middleware and how to map it to a specific route.

An example from my own project with a global error handling middleware (some irrelevant parts stripped out):

public class ExceptionHandlingMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            // Call next middleware
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        ErrorDetails error = null;
        if (ex is FileNotFoundException || ex is DirectoryNotFoundException)
        {
            context.Response.StatusCode = StatusCodes.Status404NotFound;
            error = _localizer.FilesOrFoldersNotFound();
        }
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonConvert.SerializeObject(
            new CustomResponse(false, error ?? _localizer.DefaultError()),
            _serializerSettings));
        }
    }

To only use this middleware for specific routes you could do as suggested here :

// Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.Map("path/where/error/could/happen",
        b => b.UseMiddleware<ExceptionHandlingMiddleware>());
    // ...
}

Or check the path inside the middleware itself:

// ExceptionHandlingMiddleware.cs
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    if (!context.Request.Path.StartsWithSegments("path/where/error/could/happen"))
    {
        // Skip doing anything in this middleware and continue as usual
        await next(context);
        return;
    }

    // Use middleware logic 
    try
    {
        // Call next middleware
        await next(context);
    }
    catch (Exception ex)
    {
        await HandleExceptionAsync(context, ex);
    }
}

You can Extending AuthorizeAttribute along with IAuthorizationFilter in Asp.Net Core

1.Create a class which extends AuthorizeAttribute , this will used on top of controller or action like Asp.Net core's inbuilt [Authorize] attribute.

2.Implement the method OnAuthorization(AuthorizationFilterContext context) which is part of IAuthorizationFilter interface.

3.Call return keyword without any additional operation for authorized user.

4.Set AuthorizationFilterContext result as Unauthorized for unauthorized users as context.Result = new UnauthorizedResult()

    public class SampleAuthorizePermission : AuthorizeAttribute, IAuthorizationFilter
{
    public string Permissions { get; set; }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        if (string.IsNullOrEmpty(Permissions))
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        var userName = context.HttpContext.User.Identity.Name;

        var assignedPermissionsForUser =
            MockData.UserPermissions
                .Where(x => x.Key == userName)
                .Select(x => x.Value).ToList();

        var requiredPermissions = Permissions.Split(",");
        foreach (var x in requiredPermissions)
        {
            if (assignedPermissionsForUser.Contains(x))
                return;
        }

        context.Result = new UnauthorizedResult();
        return;
    }
}

and in your controller

[SampleAuthorizePermission(Permissions = "CanRead")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return "value";
    }

Taking the middleware specific approach in .NET Core 3.1, we can conditionally add middleware using the following- In configure method-

app.UseWhen(context=>context.Request.Path.StartsWithSegments("your-route-url"),branch=>branch.useMiddleware(););

There are a few ways how the pipeline branching can happen, follow the docs for more information- https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0#branch-the-middleware-pipeline

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