简体   繁体   English

ASP .NET Core 3.1 MVC 中特定路由的自定义中间件(或授权)

[英]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在我的 ASP .NET Core 3.1 MVC 应用程序中,我像这样使用端点路由

        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.因此,浏览到 /access 会启动 Access 操作,应用程序会在其中检查用户是否符合某些访问要求。

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.现在我更喜欢在自定义中间件(或者可能是自定义授权属性)中进行此检查,而不是在 Controller 中进行检查。 So my question to you is, how should I rewrite the UseEndPoints call, to include a custom middleware for the /access area?所以我的问题是,我应该如何重写 UseEndPoints 调用,以包含 /access 区域的自定义中间件?

Authorization policy extending [Authorize]授权策略扩展 [Authorize]

You could do this using authorization policies.您可以使用授权策略来执行此操作。 Configure these in your Startup.cs inside ConfigureServices(IServiceCollection services) like so:ConfigureServices(IServiceCollection services)中的Startup.cs中配置这些,如下所示:

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属性装饰您的 controller 操作,如下所示:

[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.现在,每当您尝试使用 go /access时,此策略都会运行,如果策略返回 false,用户将遇到 HTTP 403(禁止)状态代码。

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.为了回应您的评论,这里有一个中间件示例以及如何将其 map 到特定路由。

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您可以在 Asp.Net Core 中扩展AuthorizeAttributeIAuthorizationFilter

1.Create a class which extends AuthorizeAttribute , this will used on top of controller or action like Asp.Net core's inbuilt [Authorize] attribute. 1.创建一个扩展AuthorizeAttribute的 class ,这将用于 controller 或类似 Asp.Net 核心的内置[Authorize]属性的操作。

2.Implement the method OnAuthorization(AuthorizationFilterContext context) which is part of IAuthorizationFilter interface. 2.实现方法OnAuthorization(AuthorizationFilterContext context) ,它是IAuthorizationFilter接口的一部分。

3.Call return keyword without any additional operation for authorized user. 3.授权用户无需任何额外操作即可调用return关键字。

4.Set AuthorizationFilterContext result as Unauthorized for unauthorized users as context.Result = new UnauthorizedResult() 4.将AuthorizationFilterContext结果设置为未经授权的用户未授权为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在你的 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-采用 .NET Core 3.1 中的中间件特定方法,我们可以使用以下方式有条件地添加中间件 - 在配置方法中 -

app.UseWhen(context=>context.Request.Path.StartsWithSegments("your-route-url"),branch=>branch.useMiddleware();); 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管道分支的发生方式有多种,请按照文档获取更多信息 - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0#branch-中间件管道

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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