为啥 `UseAuthentication` 必须放在 `UseRouting` 之后而不是之前?

Posted

技术标签:

【中文标题】为啥 `UseAuthentication` 必须放在 `UseRouting` 之后而不是之前?【英文标题】:Why does `UseAuthentication` have to be placed after `UseRouting` and not before?为什么 `UseAuthentication` 必须放在 `UseRouting` 之后而不是之前? 【发布时间】:2019-10-20 17:41:53 【问题描述】:

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

app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

我有中间件来保护静态文件,基于this article(保护某些路由)。我遇到的问题是订单对我不起作用。如果用户已获得授权,我只能保护文件夹。所以我需要在UseStaticFiles之前和UseAuthenticationUseAuthorization之后放置UseProtectFolder

app.UseRouting();

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

app.UseProtectFolder(new ProtectFolderOptions

    Path = "/Secret",
    PolicyName = "Authenticated"
);
app.UseStaticFiles();

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

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

app.UseProtectFolder(new ProtectFolderOptions

    Path = "/Secret",
    PolicyName = "Authenticated"
);
app.UseStaticFiles();

app.UseRouting();

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

来自文档:

中间件组件的添加顺序 Startup.Configure 方法定义了中间件的顺序 组件在请求和相反的顺序被调用 回复。该顺序对于安全性、性能和 功能

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

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

【问题讨论】:

【参考方案1】:

发布此问题后,我在 github 上打开了一个关于 routing 的问题,以及一个关于 localization 的问题,希望获得更多信息。虽然不是所有问题都得到了直接的回答,但它帮助我找到了这个问题的答案。

看完comment of David Fowler:

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

关于订单的两点说明:

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

【讨论】:

如果您仍然碰巧有它们,您介意在此处的 cmets 中发布您在 github 上打开的那些问题的链接吗?我希望看到更多这样的讨论。 感谢您的链接!我仍然对引擎盖下到底发生了什么感到好奇,所以我做了更多的挖掘,并添加了另一个关于 UseRouting 为 UseAuthentication 和 UseAuthorization 提供什么的答案。 请更新你的答案,根据docs.microsoft.com/en-us/aspnet/core/security/…UseCors必须放在UseRouting之后【参考方案2】:

关于这个问题的“为什么在 UseAuth 之前使用路由”部分,我想在@Ruard 的回答中添加一件事是来自Overview of ASP.NET Core authentication 的摘录:

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

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

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

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

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

事后看来,这很有意义。在您甚至不知道端点是否需要授权之前(或者在我们知道请求需要身份验证之前是否对用户进行了身份验证)就试图弄清楚请求是否已被授权,这将是愚蠢的。

我偶然发现的其他一些非常有见地的是this article by 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.

【讨论】:

以上是关于为啥 `UseAuthentication` 必须放在 `UseRouting` 之后而不是之前?的主要内容,如果未能解决你的问题,请参考以下文章

Dotnet webapp 模板有 app.UseAuthorization() 没有 app.UseAuthentication()

.Net Core 6 web api jwt 一直 401 Postman返回WWW-Authenticate Bearer没有其他提示的解决方法

为啥我们必须输入 vim ~/.vimrc 而为啥不只输入 vim ~.vimrc? [关闭]

SQL查询中左连接之后的所有连接是不是也必须是左连接?为啥或者为啥不?

C++:为啥必须声明私有函数?

为啥专业化论证必须无效?