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,启动访问操作,应用程序检查用户是否符合某些访问要求。
if (access checks...)
{
return View();
}
现在我宁愿在自定义中间件(或可能是自定义授权属性)中进行此检查,而不是在控制器中进行检查。所以我的问题是,我应该如何重写 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
属性装饰您的控制器操作,如下所示:
[Authorize(Policy = "MyAccessPolicy")]
public IActionResult Access()
{
return View();
}
现在,无论何时您尝试访问 /access
,此政策都会 运行,如果政策 returns 为 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));
}
}
要仅将此中间件用于特定路由,您可以执行 :
// 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 核心
中扩展 AuthorizeAttribute
和 IAuthorizationFilter
1.Create 扩展 AuthorizeAttribute
的 class,这将用于控制器或类似 Asp.Net 核心内置 [Authorize]
属性的操作。
2.Implement 方法 OnAuthorization(AuthorizationFilterContext context)
是 IAuthorizationFilter
接口的一部分。
3.Call return
授权用户无需任何额外操作的关键字。
4.Set 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";
}
采用 .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
在我的 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,启动访问操作,应用程序检查用户是否符合某些访问要求。
if (access checks...)
{
return View();
}
现在我宁愿在自定义中间件(或可能是自定义授权属性)中进行此检查,而不是在控制器中进行检查。所以我的问题是,我应该如何重写 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
属性装饰您的控制器操作,如下所示:
[Authorize(Policy = "MyAccessPolicy")]
public IActionResult Access()
{
return View();
}
现在,无论何时您尝试访问 /access
,此政策都会 运行,如果政策 returns 为 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));
}
}
要仅将此中间件用于特定路由,您可以执行
// 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 核心
中扩展AuthorizeAttribute
和 IAuthorizationFilter
1.Create 扩展 AuthorizeAttribute
的 class,这将用于控制器或类似 Asp.Net 核心内置 [Authorize]
属性的操作。
2.Implement 方法 OnAuthorization(AuthorizationFilterContext context)
是 IAuthorizationFilter
接口的一部分。
3.Call return
授权用户无需任何额外操作的关键字。
4.Set 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";
}
采用 .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