Asp.Net 在执行设置为文件夹的策略后,PageModel 上具有 Authorize 属性的策略的核心 Razor 页面授权处理程序仍然 运行
Asp.Net Core Razor Pages authorization handlers for policies with Authorize attribute on PageModel still run after executing policies set to folders
我正在使用 Razor Pages 和 Asp.Net Core Identity 创建一个简单的 Asp.Net Core Web 应用程序,我遇到了一个问题,即应用到 PageModel 的策略的授权处理程序在尽管每个授权处理程序都与不同的要求 Class 相关,但对同一页面文件夹应用的策略。我的问题是,即使在文件夹策略授权处理程序中调用 Context.Fail(),未经身份验证的用户似乎仍然可以访问页面,这会触发页面策略授权处理程序,然后抛出异常,因为用户对象为空。我想实现一个简单的流程,其中对页面的请求首先由在它们所属的文件夹上设置的策略验证,好吗?继续页面(和 运行 页面授权处理程序),不行,return 立即禁止响应。
非常感谢任何帮助。
要求:
public class SectionsAccessRequirement : IAuthorizationRequirement
{
public string SectionName { get; }
public SectionsAccessRequirement(string Name)
{
SectionName = Name;
}
}
public class RecordCreateRequirement : IAuthorizationRequirement { }
授权处理程序:
public class SectionsAccessHandler : AuthorizationHandler<SectionsAccessRequirement>
{
public IHttpContextAccessor _accessor;
public SectionsAccessHandler(IHttpContextAccessor Accessor) => _accessor = Accessor;
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SectionsAccessRequirement requirement)
{
// Require authentication
if (!_accessor.HttpContext.User.Identity.IsAuthenticated) context.Fail();
// Admin
if (context.User.HasClaim(c => c.Type == ClaimNameConstants.UserGroup && c.Value == UserGroupConstants.AdminGroup)) context.Succeed(requirement);
if (requirement.SectionName == SectionNameConstants.AdminSection) context.Fail();
// Client
if (requirement.SectionName == SectionNameConstants.ClientSection && context.User.HasClaim(c => c.Type == ClaimNameConstants.ClientSection)) context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class RecordCreateAuthorizationHandler : AuthorizationHandler<RecordCreateRequirement>
{
private readonly UserManager<AppUser> _users;
public RecordCreateAuthorizationHandler(UserManager<AppUser> Users) => _users = Users;
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordCreateRequirement requirement)
{
// User ID
int sessionUserId = int.Parse(_users.GetUserId(context.User));
// Privileges
if (!context.User.HasClaim(c => c.Type == ClaimNameConstants.UserGroup && c.Value == UserGroupConstants.AdminGroup))
{
if (context.User.HasClaim(c => c.Type == ClaimNameConstants.ClientCreate)) context.Succeed(requirement);
}
else
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
我有这两种扩展方法:
public static class AuthorizationOptionsExtension
{
public static void AddPolicies(this AuthorizationOptions Options)
{
// Folders policies
Options.AddPolicy(PolicyNameConstants.AdminSectionPolicy, p => p.Requirements.Add(new SectionsAccessRequirement(SectionNameConstants.AdminSection)));
Options.AddPolicy(PolicyNameConstants.ClientSectionPolicy, p => p.Requirements.Add(new SectionsAccessRequirement(SectionNameConstants.ClientSection)));
// Pages policies
Options.AddPolicy(PolicyNameConstants.ClientRecordCreatePolicy, p => p.Requirements.Add(new Client.RecordCreateRequirement()));
// Fallback policy
Options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
}
}
并且:
public static class RazorPagesOptionsExtension
{
public static void AddFoldersAuthorization(this RazorPagesOptions Options)
{
Options.Conventions.AuthorizeFolder("/Admin", PolicyNameConstants.AdminSectionPolicy);
Options.Conventions.AuthorizeFolder("/Client", PolicyNameConstants.ClientSectionPolicy);
}
}
页面的政策是使用授权属性应用的:
[Authorize(Policy = PolicyNameConstants.ClientRecordCreatePolicy)]
public class CreateModel : PageModel
{ ... }
这是我的初创公司 class,我正在使用 Asp.Net Core Identity:
public class Startup
{
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
// Add (and configure) services to the container
public void ConfigureServices(IServiceCollection services)
{
// Global application settings (formerly known as Web.config) and other settings
services.Configure<ApplicationSettings>(_config);
// Application
services.AddApplicationLayerServices();
services.AddRazorPages();
services.AddHttpContextAccessor();
services.Configure<RazorViewEngineOptions>(options =>
{
options.PageViewLocationFormats.Add("/Pages/Shared/Partials/{0}" + RazorViewEngine.ViewExtension);
options.PageViewLocationFormats.Add("/Pages/Client/Partials/{0}" + RazorViewEngine.ViewExtension);
});
// Data store
services.AddDbContext<DataContext>(options => options.UseOracle(_config.GetConnectionString("AppConnectionString")));
// Security
services.AddIdentity<AppUser, IdentityRole<int>>(options => {
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 8;
}).AddEntityFrameworkStores<DataContext>();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/SignIn";
options.AccessDeniedPath = "/FourZeroSomething";
options.SlidingExpiration = true;
});
services.AddAccessControlLayerServices();
services.AddAuthorization(options => options.AddPolicies());
services.Configure<RazorPagesOptions>(options => options.AddFoldersAuthorization());
}
// Configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
// Navigation
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// Security
app.UseAuthentication();
app.UseAuthorization();
// Endpoints
app.UseEndpoints(endpoints => endpoints.MapRazorPages());
}
}
}
如 docs 中所述,此行为是设计:
If a handler calls context.Succeed
or context.Fail
, all other handlers are still called. This allows requirements to produce side effects, such as logging, which takes place even if another handler has successfully validated or failed a requirement. When set to false
, the InvokeHandlersAfterFailure
property short-circuits the execution of handlers when context.Fail
is called. InvokeHandlersAfterFailure
defaults to true
, in which case all handlers are called.
如果您不想设置 InvokeHandlersAfterFailure
选项,您可以检查更具体的处理程序中的 AuthorizationHandlerContext.HasFailed
属性,看看它是否是 false
和 return早。例如:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordCreateRequirement requirement)
{
if (context.HasFailed)
return;
// User ID
int sessionUserId = int.Parse(_users.GetUserId(context.User));
// ...
}
我正在使用 Razor Pages 和 Asp.Net Core Identity 创建一个简单的 Asp.Net Core Web 应用程序,我遇到了一个问题,即应用到 PageModel 的策略的授权处理程序在尽管每个授权处理程序都与不同的要求 Class 相关,但对同一页面文件夹应用的策略。我的问题是,即使在文件夹策略授权处理程序中调用 Context.Fail(),未经身份验证的用户似乎仍然可以访问页面,这会触发页面策略授权处理程序,然后抛出异常,因为用户对象为空。我想实现一个简单的流程,其中对页面的请求首先由在它们所属的文件夹上设置的策略验证,好吗?继续页面(和 运行 页面授权处理程序),不行,return 立即禁止响应。
非常感谢任何帮助。
要求:
public class SectionsAccessRequirement : IAuthorizationRequirement
{
public string SectionName { get; }
public SectionsAccessRequirement(string Name)
{
SectionName = Name;
}
}
public class RecordCreateRequirement : IAuthorizationRequirement { }
授权处理程序:
public class SectionsAccessHandler : AuthorizationHandler<SectionsAccessRequirement>
{
public IHttpContextAccessor _accessor;
public SectionsAccessHandler(IHttpContextAccessor Accessor) => _accessor = Accessor;
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SectionsAccessRequirement requirement)
{
// Require authentication
if (!_accessor.HttpContext.User.Identity.IsAuthenticated) context.Fail();
// Admin
if (context.User.HasClaim(c => c.Type == ClaimNameConstants.UserGroup && c.Value == UserGroupConstants.AdminGroup)) context.Succeed(requirement);
if (requirement.SectionName == SectionNameConstants.AdminSection) context.Fail();
// Client
if (requirement.SectionName == SectionNameConstants.ClientSection && context.User.HasClaim(c => c.Type == ClaimNameConstants.ClientSection)) context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class RecordCreateAuthorizationHandler : AuthorizationHandler<RecordCreateRequirement>
{
private readonly UserManager<AppUser> _users;
public RecordCreateAuthorizationHandler(UserManager<AppUser> Users) => _users = Users;
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordCreateRequirement requirement)
{
// User ID
int sessionUserId = int.Parse(_users.GetUserId(context.User));
// Privileges
if (!context.User.HasClaim(c => c.Type == ClaimNameConstants.UserGroup && c.Value == UserGroupConstants.AdminGroup))
{
if (context.User.HasClaim(c => c.Type == ClaimNameConstants.ClientCreate)) context.Succeed(requirement);
}
else
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
我有这两种扩展方法:
public static class AuthorizationOptionsExtension
{
public static void AddPolicies(this AuthorizationOptions Options)
{
// Folders policies
Options.AddPolicy(PolicyNameConstants.AdminSectionPolicy, p => p.Requirements.Add(new SectionsAccessRequirement(SectionNameConstants.AdminSection)));
Options.AddPolicy(PolicyNameConstants.ClientSectionPolicy, p => p.Requirements.Add(new SectionsAccessRequirement(SectionNameConstants.ClientSection)));
// Pages policies
Options.AddPolicy(PolicyNameConstants.ClientRecordCreatePolicy, p => p.Requirements.Add(new Client.RecordCreateRequirement()));
// Fallback policy
Options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
}
}
并且:
public static class RazorPagesOptionsExtension
{
public static void AddFoldersAuthorization(this RazorPagesOptions Options)
{
Options.Conventions.AuthorizeFolder("/Admin", PolicyNameConstants.AdminSectionPolicy);
Options.Conventions.AuthorizeFolder("/Client", PolicyNameConstants.ClientSectionPolicy);
}
}
页面的政策是使用授权属性应用的:
[Authorize(Policy = PolicyNameConstants.ClientRecordCreatePolicy)]
public class CreateModel : PageModel
{ ... }
这是我的初创公司 class,我正在使用 Asp.Net Core Identity:
public class Startup
{
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
// Add (and configure) services to the container
public void ConfigureServices(IServiceCollection services)
{
// Global application settings (formerly known as Web.config) and other settings
services.Configure<ApplicationSettings>(_config);
// Application
services.AddApplicationLayerServices();
services.AddRazorPages();
services.AddHttpContextAccessor();
services.Configure<RazorViewEngineOptions>(options =>
{
options.PageViewLocationFormats.Add("/Pages/Shared/Partials/{0}" + RazorViewEngine.ViewExtension);
options.PageViewLocationFormats.Add("/Pages/Client/Partials/{0}" + RazorViewEngine.ViewExtension);
});
// Data store
services.AddDbContext<DataContext>(options => options.UseOracle(_config.GetConnectionString("AppConnectionString")));
// Security
services.AddIdentity<AppUser, IdentityRole<int>>(options => {
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 8;
}).AddEntityFrameworkStores<DataContext>();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/SignIn";
options.AccessDeniedPath = "/FourZeroSomething";
options.SlidingExpiration = true;
});
services.AddAccessControlLayerServices();
services.AddAuthorization(options => options.AddPolicies());
services.Configure<RazorPagesOptions>(options => options.AddFoldersAuthorization());
}
// Configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
// Navigation
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// Security
app.UseAuthentication();
app.UseAuthorization();
// Endpoints
app.UseEndpoints(endpoints => endpoints.MapRazorPages());
}
}
}
如 docs 中所述,此行为是设计:
If a handler calls
context.Succeed
orcontext.Fail
, all other handlers are still called. This allows requirements to produce side effects, such as logging, which takes place even if another handler has successfully validated or failed a requirement. When set tofalse
, theInvokeHandlersAfterFailure
property short-circuits the execution of handlers whencontext.Fail
is called.InvokeHandlersAfterFailure
defaults totrue
, in which case all handlers are called.
如果您不想设置 InvokeHandlersAfterFailure
选项,您可以检查更具体的处理程序中的 AuthorizationHandlerContext.HasFailed
属性,看看它是否是 false
和 return早。例如:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordCreateRequirement requirement)
{
if (context.HasFailed)
return;
// User ID
int sessionUserId = int.Parse(_users.GetUserId(context.User));
// ...
}