我正在尝试按照教程实现自定义授权要求。好像context.Resource
不再包含AuthorizationFilterContext
,因此:
var authFilterContext = context.Resource as AuthorizationFilterContext;
返回null
,其余逻辑失败。我也无法获取查询字符串值,因为它是空的。 以下是代码:
public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ManageAdminRolesAndClaimsRequirement requirement)
{
var authFilterContext = context.Resource as AuthorizationFilterContext;
if (authFilterContext == null)
{
return Task.CompletedTask;
}
string loggedInAdminId =
context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
string adminIdBeingEdited = authFilterContext.HttpContext.Request.Query["userId"];
if (context.User.IsInRole("Admin") &&
context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
如何在 ASP.NET Core 3.0中解决此问题?
这是由于 .NET Core 3.0 中的新终结点路由。
引用下面的票证。
这是因为在 Core 3.0 中使用终结点路由 ASP.NET:
Mvc 将不再将 AuthorizeFilter 添加到 ActionDescriptor 和 ResourceInvoker 不会调用 AuthorizeAsync(( https://github.com/aspnet/AspNetCore/blob/90ab2cb965aeb8ada13bc4b936b3735ca8dd28df/src/Mvc/Mvc.Core/src/ApplicationModels/AuthorizationApplicationModelProvider.cs#L40
Mvc 会将所有筛选器作为元数据添加到终结点。元数据 https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Mvc/Mvc.Core/src/Routing/ActionEndpointFactory.cs#L348
而是通过 AuthorizationMiddleware 调用 AuthorizeAsync(( 和 资源是端点 https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs#L63
新方法。
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CookieOrTokenAuthorizationRequirement requirement)
{
if (context.Resource is Endpoint endpoint)
{
if (endpoint.Metadata.OfType<IFilterMetadata>().Any(filter => filter is MyFilter))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
https://github.com/dotnet/aspnetcore/issues/11075
还值得注意的是,使用新上下文,您将无法像以前使用 AuthorizationFilterContext 那样访问路由数据。您需要将 IHttpContextAccessor 注入到 AuthorizationHandler 中。
// Ensure your handler is registered as scoped
services.AddScoped<IAuthorizationHandler, InvestorRequirementHandler>();
public class InvestorRequirementHandler : AuthorizationHandler<InvestorRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public InvestorRequirementHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, InvestorRequirement requirement)
{
var tenant = httpContextAccessor.HttpContext.GetRouteData().Values[ExceptionHandlerMiddleware.TenantCodeKey].ToString();
}
}
public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
private readonly IHttpContextAccessor httpContextAccessor;
public CanEditOnlyOtherAdminRolesAndClaimsHandler(
IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ManageAdminRolesAndClaimsRequirement requirement)
{
var loggedInAdminId = context.User.Claims
.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value.ToString();
var adminIdBeingEdited = httpContextAccessor.HttpContext
.Request.Query["userId"].ToString();
if (context.User.IsInRole("Admin")
&& context.User.HasClaim(c => c.Type == "Edit Role" && c.Value == "true")
&& adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
private readonly IHttpContextAccessor httpContextAccessor;
public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ManageAdminRolesAndClaimsRequirement requirement)
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Fail();
return Task.CompletedTask;
}
string loggedInAdminId =
context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();
if (context.User.IsInRole("Admin") &&
context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
然后将以下服务添加到类ConfigureServices
Startup
方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
}
services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();
}
如果要处理某个要求的多个自定义授权:
在类CanEditOnlyOtherAdminRolesAndClaimsHandler
检查用户是否具有Admin
角色并具有Edit Role
声明。假设您要求用户必须具有Super Admin
角色,在这种情况下,您可以:
- 将类中的条件编辑CanEditOnlyOtherAdminRolesAndClaimsHandler
如下所示:
if (context.User.IsInRole("Admin") &&
context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
adminIdBeingEdited.ToLower() != loggedInUserId.ToLower() ||
context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
{
context.Succeed(requirement);
}
- 或者为新要求自定义另一个授权处理程序,在本例中为角色Super Admin
:
创建一个新类并将其命名为ManageRolesAndClaimsSuperAdminHandler
,该类的实现应该如下
public class ManageRolesAndClaimsSuperAdminHandler : AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
private readonly IHttpContextAccessor httpContextAccessor;
public ManageUsersRolesSuperAdminHandler(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ManageAdminRolesAndClaimsRequirement requirement)
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Fail();
return Task.CompletedTask;
}
string loggedInAdminId = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();
if (context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
现在在类ConfigureServices
方法Startup
注册新处理程序
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
}
services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();
services.AddScoped<IAuthorizationHandler, ManageRolesAndClaimsSuperAdminHandler>();
}
启动中的更改.cs如果在控制器中使用 Rout 属性 您可以替换此
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "home",
pattern: "{controller=Home}");
});
在配置(( 中使用它
app.UseMvc(routes =>
{
routes.MapRoute(
name: "home",
template: "{controller=Home}");
});
并在 ConfigureSevices(( 中禁用端点路由
services.AddMvc().AddMvcOptions(mvcopt=> { mvcopt.EnableEndpointRouting = false;});
也适用于 Asp .Net Core 5
private readonly IHttpContextAccessor httpContextAccessor;
public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
Resource 属性将仅是 [Authorize] 属性上下文中的 AuthorizationFilterContext。