c# - 使用 Razor Pages 中的约定按区域按不同方案进行授权
问题描述
我的 Razor Pages 应用程序的结构如下:
- 页面
- 索引.cshtml
- 领域
- 行政
- 页面
- 索引.cshtml
- 页面
- api
- 页面
- 索引.cshtml
- 页面
- 行政
我想允许匿名访问任何非区域页面(/Pages/ 中的任何内容)。我想对管理区域中的所有页面使用 Windows 身份验证,并通过承载令牌对 Api 中的所有页面进行授权。
我可以直接在 PageModels 上使用 Authorize 属性并指定方案来执行此操作。
//Non-area example
[AllowAnonymous]
public class IndexModel : PageModel
//Admin example
[Authorize(AuthenticationSchemes = "Windows")]
public class IndexModel : PageModel
//API example
[Authorize(AuthenticationSchemes = "ApiKey")]
public class IndexModel : PageModel
然后,我可以为 3 个区域中的每一个创建一个基本 PageModel,并从各自的基本 PageModel 继承每个区域的所有 PageModel。
有没有办法使用约定来完成同样的事情?
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.???
})
解决方案
我已经解决了。诀窍是 AuthorizeFilter 可以包含带有构造函数重载的方案。
var authorizeFilter = new AuthorizeFilter(new List<IAuthorizeData> {
new AuthorizeAttribute()
{
AuthenticationSchemes = authenticationSchemes
}
});
然后我必须编写我自己的 IPageApplicationModelConvention 将在区域级别应用。默认方法适用于文件夹和页面级别。我使用Microsoft.AspNetCore.Mvc.RazorPages的源代码作为指南。
public class AreaModelConvention : IPageApplicationModelConvention
{
private readonly string _areaName;
private readonly Action<PageApplicationModel> _action;
public AreaModelConvention(string areaName, Action<PageApplicationModel> action)
{
_areaName = areaName;
_action = action;
}
public void Apply(PageApplicationModel model)
{
if(string.Equals(_areaName, model.AreaName, StringComparison.OrdinalIgnoreCase))
{
_action(model);
}
}
}
我写了一些 PageConventionCollectionExtensions 这就是在Microsoft.AspNetCore.Mvc.RazorPages中完成的。
public static class PageConventionCollectionExtensions
{
public static PageConventionCollection RequireAuthenticationSchemesForArea(this PageConventionCollection conventions, string areaName, string authenticationSchemes)
{
if (conventions == null)
{
throw new ArgumentNullException(nameof(conventions));
}
if (string.IsNullOrEmpty(areaName))
{
throw new ArgumentException(nameof(areaName));
}
var authorizeFilter = new AuthorizeFilter(new List<IAuthorizeData> {
new AuthorizeAttribute()
{
AuthenticationSchemes = authenticationSchemes
}
});
conventions.AddAreaModelConvention(areaName, model => model.Filters.Add(authorizeFilter));
return conventions;
}
public static IPageApplicationModelConvention AddAreaModelConvention(this ICollection<IPageConvention> pageConventions, string areaName, Action<PageApplicationModel> action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
var convention = new AreaModelConvention(areaName, action);
pageConventions.Add(convention);
return convention;
}
}
最后我可以全部注册:
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToNonareas();
options.Conventions.RequireAuthenticationSchemesForArea("Admin", "Windows");
options.Conventions.RequireAuthenticationSchemesForArea("Api", "ApiKey");
})
注意: AllowAnonymousToNonareas 的代码在这里没有定义,但是非常相似。我用这个 Apply 方法创建了一个 NonareaModelConvention:
public void Apply(PageApplicationModel model)
{
if (model.AreaName == null)
{
_action(model);
}
}
并编写了类似的扩展方法将其绑定在一起。
请记住为应用打开匿名身份验证和 Windows 身份验证。
推荐阅读
- python - 检查子字符串是否在字符串列表中并返回包含的字符串
- docker - 将 Hyperledger Fabric 代码导出为 Docker 映像
- pandas - 有没有比python中具有不同键的嵌套字典更有效和可读的替代方法?
- c++ - 65536 之后的 C++ 数学怪异
- java - JPA 自定义存储库不是实体:类
- html - P5JS:如何使背景图像重复?
- javascript - 使用 awaitReactions() 函数时如何修复超时错误?
- python - 将值聚合到字符串等列表中,然后转换为真正的列表数据类型
- python - 如何在 Airflow 中更新 AwsBatchOperator 任务代码?
- terraform - Terraform 忽略 AWS metric_query 中的子块更改