为什么“UseAuthentication”必须放在“UseRouting”之后而不是之前?

Rua*_*urg 10 asp.net-core asp.net-core-middleware asp.net-core-3.0

根据文档,中间件的顺序应该是这样的:

app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
Run Code Online (Sandbox Code Playgroud)

根据这篇文章(保护某些路由),我有中间件来保护静态文件。我遇到的问题是订单对我不起作用。如果用户已获得授权,我只能保护文件夹。所以,我需要的地方UseProtectFolder之前UseStaticFiles和之后UseAuthenticationUseAuthorization

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseProtectFolder(new ProtectFolderOptions
{
    Path = "/Secret",
    PolicyName = "Authenticated"
});
app.UseStaticFiles();
Run Code Online (Sandbox Code Playgroud)

但这不会返回任何静态文件。看起来UseRouting正在做一些使文件不可用的事情,返回 404,因为当我将顺序更改为此,移动UseRouting之后UseStaticFiles,它可以工作:

app.UseAuthentication();
app.UseAuthorization();

app.UseProtectFolder(new ProtectFolderOptions
{
    Path = "/Secret",
    PolicyName = "Authenticated"
});
app.UseStaticFiles();

app.UseRouting();
Run Code Online (Sandbox Code Playgroud)

所以实际的顺序变化UseAuthentication是放在之前UseRouting(甚至之前UseStaticFiles)。

从文档:

在 Startup.Configure 方法中添加中间件组件的顺序定义了对请求调用中间件组件的顺序以及响应的相反顺序。该顺序对于安全性、性能和功能至关重要

我现在的问题是:按照记录的顺序,为什么UseAuthentication放在UseRouting

是否有特殊原因或仅出于性能原因?并且通过在管道中更早地移动身份验证/授权,这是否会影响响应(相反的顺序)?

Rua*_*urg 10

发布这个问题后,我在 github 上打开了一个关于路由的问题,一个关于本地化的问题,希望能提供更多信息。尽管并非所有问题都以直接的方式回答,但它帮助我找到了这个问题的答案。

阅读大卫福勒评论后

UseAuthorization() -> 将查看填充的用户和当前端点以确定是否需要应用授权策略。

我突然想到 UseAuthorization 没有问题。它用于端点,所以我不需要它来保护文件夹。它还解释了为什么此语句仅在 UseEndpoints 语句之后才有意义。

为了全面了解我的配置,我有一个策略提供程序(包括策略)、一个 url 重写器(如 UseDefaultFiles)和保护某些文件夹的中间件。

我的结论是,我可以使用以下顺序,这与记录的几乎相同:

// Identify the user. The only statement that is not in the order as documented
app.UseAuthentication();

// Middleware that adds policies
app.UsePolicyProvider();
// Protect the folder by policy
app.UseProtectFolder(new ProtectFolderOptions { Path = "/p", PolicyName = "admin" });
// URL rewriter for serving tenant specific files
app.UseTenantStaticFiles();

// Serve the static files
app.UseStaticFiles();

app.UseCookiePolicy();
app.UseCors();

app.UseRouting();
app.UseRequestLocalization();
app.UseAuthorization();
app.UseEndpoints();
Run Code Online (Sandbox Code Playgroud)

关于订单的两点说明:

  1. UseRequestLocalization 仅在 UseRouting 之后有效
  2. 当涉及到 URL 重写器(如 UseDefaultFiles)时,UseStaticFiles 在 UseRouting 之后不起作用。


Jer*_*eck 6

我想在@Ruard 关于这个问题的“Why UseRouting before UseAuth”部分的回答中添加的一件事是摘自ASP.NET Core 身份验证概述

使用端点路由时,必须调用 UseAuthentication:

  • 在 UseRouting 之后,路由信息可用于身份验证决策。
  • 在 UseEndpoints 之前,以便用户在访问端点之前进行身份验证。

我仍然很好奇在调用 UseAuthentication() 之前需要什么路由信息,所以我对源代码进行了一些挖掘,发现 UseRouting() 必须为 UseAuthentication() 和 UseAuthorization() 提供的信息只是端点类。具体来说,Endpoint.MetadataEndpointMetadataCollection类型。

EndpointMetadataCollection 只是一个对象数组,所以为了弄清楚那里可能实际填充了什么,我刚刚创建了一个空的 WebAPI 项目,在控制器上方设置了一个授权属性,加入了一些测试中间件,并在分配 HttpContext.GetEndpoint 后立即添加了一个断点().元数据到一个变量。

事实证明,它填充的内容之一是有关我添加的 Authorization 属性的数据:

调试中的 EndpointMetadataCollection 变量值

事后看来,这很有意义。在您知道端点是否需要授权之前(或者在我们知道请求需要身份验证之前用户是否已通过身份验证)之前尝试确定请求是否已授权是愚蠢的。

我偶然发现的其他非常有见地的文章是 Areg Sarkissian 的这篇文章,它真正深入了解了端点路由的本质,而不像微软文档那样枯燥乏味。这个例子特别出色地展示了我上面提到的内容:

{
    if (env.IsDevelopment())
        app.UseDeveloperExceptionPage();
    else
        app.UseHsts();

    app.UseHttpsRedirection();

    app.UseRouting(routes =>
    {
        routes.MapControllers();

        //Mapped route that gets attached authorization metadata using the RequireAuthorization extension method.
        //This metadata will be added to the resolved endpoint for this route by the endpoint resolver
        //The app.UseAuthorization() middleware later in the pipeline will get the resolved endpoint
        //for the /secret route and use the authorization metadata attached to the endpoint
        routes.MapGet("/secret", context =>
        {
            return context.Response.WriteAsync("secret");
        }).RequireAuthorization(new AuthorizeAttribute(){ Roles = "admin" });
    });

    app.UseAuthentication();

    //the Authorization middleware check the resolved endpoint object
    //to see if it requires authorization. If it does as in the case of
    //the "/secret" route, then it will authorize the route, if it the user is in the admin role
    app.UseAuthorization();

    //the framework implicitly dispatches the endpoint at the end of the pipeline.
}
Run Code Online (Sandbox Code Playgroud)