英文:
ASP.NET Core 3 mock authorization during integration testing
问题
我正在使用ASP.NET Core构建一个API,并尝试从.NET Core 2.2升级到.NET Core 3.1。
我正在使用[Authorize]
属性来保护API端点,并且我想在集成测试期间绕过它。
我已经成功创建了一个自定义的AuthenticationHandler,用于对一个虚拟用户进行身份验证,以及一个授权处理程序,用于授权任何人(包括匿名用户)。
我的问题是,注入到Authentication处理程序中的用户未传播到Authorization处理程序,并且上下文中的用户为null,导致了DenyAnonymousAuthorizationRequirement
过滤器失败。
有人处理过类似的情况吗?
顺便说一下,DenyAnonymousAuthorizationRequirement
类是Microsoft的类,我只是将代码复制到这里以供参考。
我的自定义AuthenticationHandler:
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
ClaimsPrincipal fakeUser = FakeUserUtil.FakeUser();
var ticket = new AuthenticationTicket(fakeUser, "Test");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
AuthorizationRequirement:
public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
{
var user = context.User;
var userIsAnonymous =
user?.Identity == null ||
!user.Identities.Any(i => i.IsAuthenticated);
if (!userIsAnonymous)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
在ConfigureServices方法中使用上述类的代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", null);
services.AddAuthorization(configure =>
{
var builder = new AuthorizationPolicyBuilder(new List<string> {"Test"}.ToArray())
.AddRequirements(new DenyAnonymousAuthorizationRequirement());
configure.DefaultPolicy = builder.Build();
});
services.AddControllers()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
ReferenceLoopHandling.Ignore);
}
英文:
I am using ASP.NET core to build an API, and I am trying to upgrade from .NET core 2.2 to .NET core 3.1.
I am using the [Authorize]
attribute to secure the API endpoints and I want to bypass it during integration tests.
I have managed to to create a custom AuthenticationHandler that authenticates a fake user, and an authorization handler that authorizes anybody (including anonymous users).
My problem is that the user injected in the Authentication handler is not propagated to the Authorization handler and the filter DenyAnonymousAuthorizationRequirement
fails
because the User in the context is null.
Has anyone dealt with something similar?
By the way, the class DenyAnonymousAuthorizationRequirement
is a Microsoft class, I copied the code as it appeared in the IDE just for the post here.
My custom AuthenticationHandler:
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
ClaimsPrincipal fakeUser = FakeUserUtil.FakeUser();
var ticket = new AuthenticationTicket(fakeUser, "Test");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
The AuthorizationRequirement:
public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
{
var user = context.User;
var userIsAnonymous =
user?.Identity == null ||
!user.Identities.Any(i => i.IsAuthenticated);
if (!userIsAnonymous)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
The code in the ConfigureServices method that uses the above classes:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", null);
services.AddAuthorization(configure =>
{
var builder = new AuthorizationPolicyBuilder(new List<string> {"Test"}.ToArray())
.AddRequirements(new DenyAnonymousAuthorizationRequirement());
configure.DefaultPolicy = builder.Build();
});
services.AddControllers()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
ReferenceLoopHandling.Ignore);
}
答案1
得分: 20
在这篇文章中,它展示了你可以通过以下方式覆盖在HttpContext中设置的用户:
class FakeUserFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "123"),
new Claim(ClaimTypes.Name, "Test user"),
new Claim(ClaimTypes.Email, "test@example.com"),
new Claim(ClaimTypes.Role, "Admin")
}));
await next();
}
}
然后在ConfigureTestServices中注入它:
builder.ConfigureTestServices(services =>
{
services.AddMvc(options =>
{
options.Filters.Add(new AllowAnonymousFilter());
options.Filters.Add(new FakeUserFilter());
})
.AddApplicationPart(typeof(Startup).Assembly);
});
希望这有所帮助。
英文:
Have a related problem and have stumbled on this article here
it shows that you can override the user that is set in the HttpContext this way:
class FakeUserFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "123"),
new Claim(ClaimTypes.Name, "Test user"),
new Claim(ClaimTypes.Email, "test@example.com"),
new Claim(ClaimTypes.Role, "Admin")
}));
await next();
}
}
and then inject it in ConfigureTestServices:
builder.ConfigureTestServices(services =>
{
services.AddMvc(options =>
{
options.Filters.Add(new AllowAnonymousFilter());
options.Filters.Add(new FakeUserFilter());
})
.AddApplicationPart(typeof(Startup).Assembly);
});
Hope this helps
答案2
得分: 2
请查看此文档 https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1 中的“模拟身份验证”部分。我认为在创建客户端之后,您需要添加以下代码:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Test");
英文:
Check out this documentation https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1 at "Mock Authentication" session. I think that you need to add this code after create you client:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Test");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论