【问题标题】:Custom middleware (or authorize) for specific route in ASP .NET Core 3.1 MVCASP .NET Core 3.1 MVC 中特定路由的自定义中间件(或授权)
【发布时间】:2020-07-27 13:39:30
【问题描述】:

在我的 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" });
        });

因此浏览到 /access 会启动 Access 操作,应用会在该操作中检查用户是否符合某些访问要求。

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

现在我更喜欢在自定义中间件(或者可能是自定义授权属性)中进行此检查,而不是在控制器中进行检查。所以我的问题是,我应该如何重写 UseEndPoints 调用,以包含 /access 区域的自定义中间件?

【问题讨论】:

    标签: c# asp.net asp.net-core routing middleware


    【解决方案1】:

    授权策略扩展 [Authorize]

    您可以使用授权策略来做到这一点。在 Startup.cs 内的 ConfigureServices(IServiceCollection services) 中配置这些,如下所示:

    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;
        }));
    });
    

    然后你只需像这样用Authorize 属性装饰你的控制器动作:

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

    现在,无论您何时尝试访问 /access,此策略都会运行,如果该策略返回 false,用户将看到 HTTP 403(禁止访问)状态代码。

    自定义中间件映射到路由

    作为对您的评论的回应,这里有一个中间件示例以及如何将其映射到特定路由。

    我自己的项目中的一个示例,其中包含一个全局错误处理中间件(去掉了一些不相关的部分):

    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));
            }
        }
    

    要仅将此中间件用于特定路由,您可以这样做as suggested here

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

    或者检查中间件内部的路径:

    // 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);
        }
    }
    

    【讨论】:

    • 感谢您的回答,但是这需要授权中间件(我宁愿不用)。您知道如何将自定义中间件添加到特定路由吗?
    • @user2768479 您是否有充分的理由不希望这样做?我的意思是,您的问题听起来与典型的授权案例完全一样。
    • 你是对的 - 它确实听起来(并且是身份验证/授权的东西) - 但是我不希望使用 Auth/Authorize 中间件。我不是想重新发明轮子,我只是想知道它是如何完成的。
    • @user2768479 我添加了一个使用自定义中间件的示例。我还没有测试过路由部分,但是这样的东西应该可以工作。
    【解决方案2】:

    您可以在 Asp.Net Core 中扩展 AuthorizeAttributeIAuthorizationFilter

    1.创建一个扩展AuthorizeAttribute的类,这将用于控制器或Asp.Net核心内置[Authorize]属性等动作之上。

    2.实现OnAuthorization(AuthorizationFilterContext context)方法,它是IAuthorizationFilter接口的一部分。

    3.授权用户无需任何额外操作即可调用return关键字。

    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;
        }
    }
    

    在你的控制器中

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

    【讨论】:

    • 我喜欢这种方法,我不确定的一件事是,如果您还想从控制器中添加任何授权“东西”,如何实现这种方法,以便层次结构功能仍然有效.
    【解决方案3】:

    在 .NET Core 3.1 中采用特定于中间件的方法,我们可以使用以下方式有条件地添加中间件-在配置方法中-

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

    管道分支的发生方式有多种,请关注文档以获取更多信息 - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0 #branch-the-middleware-pipeline

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 2020-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-10
      • 1970-01-01
      • 2017-11-16
      相关资源
      最近更新 更多