ASP.NET Core 3 集成测试期间模拟授权

huangapple go评论69阅读模式
英文:

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&lt;AuthenticationSchemeOptions&gt;
  {
        public TestAuthHandler(IOptionsMonitor&lt;AuthenticationSchemeOptions&gt; options,
                               ILoggerFactory logger,
                               UrlEncoder encoder,
                               ISystemClock clock)
            : base(options, logger, encoder, clock) { }

        protected override Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync()
        {
            ClaimsPrincipal fakeUser = FakeUserUtil.FakeUser();
            var ticket = new AuthenticationTicket(fakeUser, &quot;Test&quot;);
            var result = AuthenticateResult.Success(ticket);
            return Task.FromResult(result);
        }
  }

The AuthorizationRequirement:

public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler&lt;DenyAnonymousAuthorizationRequirement&gt;, IAuthorizationRequirement
    {
       
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
        {
            var user = context.User;
            var userIsAnonymous =
                user?.Identity == null ||
                !user.Identities.Any(i =&gt; 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(&quot;Test&quot;)
                    .AddScheme&lt;AuthenticationSchemeOptions, TestAuthHandler&gt;(&quot;Test&quot;, null);
            services.AddAuthorization(configure =&gt;
            {
                var builder = new AuthorizationPolicyBuilder(new List&lt;string&gt; {&quot;Test&quot;}.ToArray())
                    .AddRequirements(new DenyAnonymousAuthorizationRequirement());
                configure.DefaultPolicy = builder.Build();
            });
            services.AddControllers()
                    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
                    .AddNewtonsoftJson(options =&gt;
                                           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&lt;Claim&gt;
        {
            new Claim(ClaimTypes.NameIdentifier, &quot;123&quot;),
            new Claim(ClaimTypes.Name, &quot;Test user&quot;),
            new Claim(ClaimTypes.Email, &quot;test@example.com&quot;),
            new Claim(ClaimTypes.Role, &quot;Admin&quot;)
        }));

        await next();
    }
}

and then inject it in ConfigureTestServices:

builder.ConfigureTestServices(services =&gt;
        {
            services.AddMvc(options =&gt;
            {                   
                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(&quot;Test&quot;);

huangapple
  • 本文由 发表于 2020年1月3日 19:06:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/59577491.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定