繁体   English   中英

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

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

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

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

授权策略扩展 [Authorize]

您可以使用授权策略来执行此操作。 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;
    }));
});

然后,您只需使用Authorize属性装饰您的 controller 操作,如下所示:

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

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

映射到路由的自定义中间件

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

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

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

要仅将此中间件用于特定路线,您可以按照此处的建议进行操作:

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

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

1.创建一个扩展AuthorizeAttribute的 class ,这将用于 controller 或类似 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;
    }
}

在你的 controller

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

采用 .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-中间件管道

暂无
暂无

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

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