MapWhen() 中的 MVC 授权应用于所有控制器
MVC authorization inside MapWhen() is applied to all controllers
在ASP.NetCore 3.0 Preview 7中,我试着写了一些代码如下:
public void Configure(IApplicationBuilder app) {
app.MapWhen(context =>
context.Request.Path.StartsWithSegments(
new PathString("/UnsecureLog")),
a => {
a.UseRouting();
a.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
);
app.UseAuthentication();
app.UseAuthorization();
app.MapWhen(context =>
context.Request.Path.StartsWithSegments(
new PathString("/SecureLog")),
a => {
a.UseRouting();
a.UseEndpoints(endpoints => {
endpoints.MapControllers()
.RequireAuthorization("MustBeReader");
});
}
);
}
我的目标是允许在中间件中处理某些控制器而无需身份验证,我的想法是 MapWhen() 是实现这一目标的方法。
相反,我在点击 /UnsecureLog
端点时看到了这个错误:
System.InvalidOperationException: Endpoint ... contains authorization metadata,
but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization()
inside the call to Configure(..) in the application startup code.
翻译:“你如何为你不想保护的端点实施安全功能”。
我的结论是,在 any MapWhen() 块处理控制器逻辑中对 RequireAuthorization("MustBeReader")
的任何调用实际上将应用于 all MVC 控制器路由。
我目前的解决方法是删除第二个 MapWhen() 代码块中的 .RequireAuthorization("MustBeReader")
调用,并将其作为属性 ([RequireAuthorization("MustBeReader")]
) 重新应用到我希望保护的那些端点。这可以正常工作并产生所需的行为。
但那不是目标,是吗?
我更愿意使用类似的策略来管理整个控制器组,同时让其他控制器完全不受安全保护,并在 Configure() 中处理所有这些。相反,我必须将所需的授权要求逐个应用到每个控制器。
我希望有更好的方法来实现路由,避免此处提到的问题。可能是我操作不当。
有想法吗?
我在 classes/controllers 上使用 Authorize 属性来强制系统要求经过身份验证的用户。如果您在此属性上指定参数,则需要用户拥有此声明。
对于无需身份验证即可访问的任何控制器或方法,我们在控制器或特定方法上设置 AllowAnonymous 属性。这应该否决任何授权要求。
将以下代码移到您的 Startup class 中更高的位置(app.MapWhen
上方)。
app.UseAuthentication();
app.UseAuthorization();
需要注意的是app.UseAuthorization
必须出现在app.UseRouting()
和app.UseEndpoints(...)
之间;
因此,它应该看起来像这样:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
今天我花了一些时间在这上面并发布了代码,我对自己的完整答案表示赞赏。感谢 mwilson 鼓励我再试一次。
在我的新方法之前,每次代码 运行 都会显示此警告...
Startup.cs(189,13): warning ASP0001: The call to UseAuthorization
should appear between app.UseRouting() and app.UseEndpoints(..)
for authorization to be correctly evaluated.
所以解决方案不仅仅是识别警告并注意它,还要找到一种方法来安抚编译器之神。 (你会在下面看到我还没有想出如何做到这一点。)
这是我今天弄明白的。我将 UseAuthentication
和 UseAuthorization
调用放在两个不同的地方。这行得通。有点。
在这个 API 项目中,我现在能够 运行 匿名不安全的端点、安全的端点,并且为了加分,还可以使用安全的 GraphQL 端点。
FWIW,代码如下:
public void Configure(...)
{
...
...
app.UseStaticFiles();
app.UseRouting();
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/api"))
),
a =>
{
a.UseRouting();
a.UseAuthentication();
a.UseAuthorization();
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
app.UseAuthentication();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQLWebSockets<...>(...);
app.UseGraphQL<...>(...);
}
可行,但我仍然收到编译器警告。更重要的是,如果我太聪明了一半并尝试使用以下控制器...
[Route("vapi/[controller]")]
//[AllowAnonymous]
public class VersionController : Controller
{ ...
连同这个额外的 Startup.cs 代码...
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/vapi"))
),
a =>
{
a.UseRouting();
// a.UseAuthentication(); <-- look, Ma, no authentication!
// a.UseAuthorization(); <-- look, Ma, no authorization!
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
我也仍然收到我的 OP 中指出的错误。 (这一切又回到了我的脑海里,就像一个古老的噩梦......)
Endpoint ....Controllers.VersionController.Version (...) contains authorization
metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() ...
所以我要离开的地方是这样的:我仍然不能做我想做的事情,那就是只保护某些控制器路径。我可以做的(不添加 "vapi"-路径支持代码启动)是这样的:
[Route("api/[controller]")]
[AllowAnonymous]
public class VersionController : Controller
{ ...
所以...我会称之为答案,直到有人发布了一组可用的代码来改进它。
在ASP.NetCore 3.0 Preview 7中,我试着写了一些代码如下:
public void Configure(IApplicationBuilder app) {
app.MapWhen(context =>
context.Request.Path.StartsWithSegments(
new PathString("/UnsecureLog")),
a => {
a.UseRouting();
a.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
);
app.UseAuthentication();
app.UseAuthorization();
app.MapWhen(context =>
context.Request.Path.StartsWithSegments(
new PathString("/SecureLog")),
a => {
a.UseRouting();
a.UseEndpoints(endpoints => {
endpoints.MapControllers()
.RequireAuthorization("MustBeReader");
});
}
);
}
我的目标是允许在中间件中处理某些控制器而无需身份验证,我的想法是 MapWhen() 是实现这一目标的方法。
相反,我在点击 /UnsecureLog
端点时看到了这个错误:
System.InvalidOperationException: Endpoint ... contains authorization metadata,
but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization()
inside the call to Configure(..) in the application startup code.
翻译:“你如何为你不想保护的端点实施安全功能”。
我的结论是,在 any MapWhen() 块处理控制器逻辑中对 RequireAuthorization("MustBeReader")
的任何调用实际上将应用于 all MVC 控制器路由。
我目前的解决方法是删除第二个 MapWhen() 代码块中的 .RequireAuthorization("MustBeReader")
调用,并将其作为属性 ([RequireAuthorization("MustBeReader")]
) 重新应用到我希望保护的那些端点。这可以正常工作并产生所需的行为。
但那不是目标,是吗?
我更愿意使用类似的策略来管理整个控制器组,同时让其他控制器完全不受安全保护,并在 Configure() 中处理所有这些。相反,我必须将所需的授权要求逐个应用到每个控制器。
我希望有更好的方法来实现路由,避免此处提到的问题。可能是我操作不当。
有想法吗?
我在 classes/controllers 上使用 Authorize 属性来强制系统要求经过身份验证的用户。如果您在此属性上指定参数,则需要用户拥有此声明。 对于无需身份验证即可访问的任何控制器或方法,我们在控制器或特定方法上设置 AllowAnonymous 属性。这应该否决任何授权要求。
将以下代码移到您的 Startup class 中更高的位置(app.MapWhen
上方)。
app.UseAuthentication();
app.UseAuthorization();
需要注意的是app.UseAuthorization
必须出现在app.UseRouting()
和app.UseEndpoints(...)
之间;
因此,它应该看起来像这样:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
今天我花了一些时间在这上面并发布了代码,我对自己的完整答案表示赞赏。感谢 mwilson 鼓励我再试一次。
在我的新方法之前,每次代码 运行 都会显示此警告...
Startup.cs(189,13): warning ASP0001: The call to UseAuthorization
should appear between app.UseRouting() and app.UseEndpoints(..)
for authorization to be correctly evaluated.
所以解决方案不仅仅是识别警告并注意它,还要找到一种方法来安抚编译器之神。 (你会在下面看到我还没有想出如何做到这一点。)
这是我今天弄明白的。我将 UseAuthentication
和 UseAuthorization
调用放在两个不同的地方。这行得通。有点。
在这个 API 项目中,我现在能够 运行 匿名不安全的端点、安全的端点,并且为了加分,还可以使用安全的 GraphQL 端点。
FWIW,代码如下:
public void Configure(...)
{
...
...
app.UseStaticFiles();
app.UseRouting();
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/api"))
),
a =>
{
a.UseRouting();
a.UseAuthentication();
a.UseAuthorization();
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
app.UseAuthentication();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQLWebSockets<...>(...);
app.UseGraphQL<...>(...);
}
可行,但我仍然收到编译器警告。更重要的是,如果我太聪明了一半并尝试使用以下控制器...
[Route("vapi/[controller]")]
//[AllowAnonymous]
public class VersionController : Controller
{ ...
连同这个额外的 Startup.cs 代码...
app.MapWhen(
context => (context.Request.Path
.StartsWithSegments(new PathString("/vapi"))
),
a =>
{
a.UseRouting();
// a.UseAuthentication(); <-- look, Ma, no authentication!
// a.UseAuthorization(); <-- look, Ma, no authorization!
a.UseEndpoints(endpoints =>
{
endpoints
.MapControllers()
.RequireAuthorization(...);
});
}
);
我也仍然收到我的 OP 中指出的错误。 (这一切又回到了我的脑海里,就像一个古老的噩梦......)
Endpoint ....Controllers.VersionController.Version (...) contains authorization
metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() ...
所以我要离开的地方是这样的:我仍然不能做我想做的事情,那就是只保护某些控制器路径。我可以做的(不添加 "vapi"-路径支持代码启动)是这样的:
[Route("api/[controller]")]
[AllowAnonymous]
public class VersionController : Controller
{ ...
所以...我会称之为答案,直到有人发布了一组可用的代码来改进它。