Razor Pages .NET Core 2.1 集成测试后身份验证 [英] Razor Pages .NET Core 2.1 Integration Testing post authentication

查看:35
本文介绍了Razor Pages .NET Core 2.1 集成测试后身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一些指导...

I am looking for some guidance...

我目前正在尝试为 .net core 2.1 中的 Razor Pages 应用程序编写一些集成测试,我想要测试的页面是后身份验证,但我不确定接近它的最佳方式.文档似乎建议创建一个 CustomWebApplicationFactory,但除此之外,我对如何使用基于 cookie 的基本身份验证来伪造/模拟经过身份验证的用户/请求有点迷茫.

I'm currently looking at trying to write some integration tests for a Razor Pages app in .net core 2.1, the pages I'm wanting to test are post authentication but I'm not sure about the best way of approaching it. The docs seem to suggest creating a CustomWebApplicationFactory, but apart from that I've got a little bit lost as how I can fake/mock an authenticated user/request, using basic cookie based authentication.

我看到有一个开放的 GitHub 问题针对 Microsoft 文档(这里是实际的 GitHub 问题),有一个提到的使用 IdentityServer4 的解决方案,但我只是在寻找如何使用基于 cookie 的身份验证来做到这一点.

I've seen that there is an open GitHub issue against the Microsoft docs (here is the actual GitHub issue), there was a mentioned solution using IdentityServer4 but I’m just looking how to do this just using cookie based authentication.

有没有人有任何他们可以建议的指导?

Has anyone got any guidance they may be able to suggest?

提前致谢

到目前为止我的代码是:

My Code so far is:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
        {
            services.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseMySql(connectionString);
                options.EnableSensitiveDataLogging();
            });

            services.AddLogging(builder =>
            {
                builder.AddSeq();
            });

            services.ConfigureAuthentication();
            services.ConfigureRouting();
    }
}

ConfigureAuthentication.cs

  namespace MyCarparks.Configuration.Startup
  {
      public static partial class ConfigurationExtensions
      {
          public static IServiceCollection ConfigureAuthentication(this IServiceCollection services)
          {
              services.AddIdentity<MyCarparksUser, IdentityRole>(cfg =>
              {
                  //cfg.SignIn.RequireConfirmedEmail = true;
              })
              .AddDefaultUI()
              .AddDefaultTokenProviders()
              .AddEntityFrameworkStores<ApplicationDbContext>();

              services.ConfigureApplicationCookie(options =>
              {
                  options.LoginPath = $"/Identity/Account/Login";
                  options.LogoutPath = $"/Identity/Account/Logout";
                  options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
              });

              services.AddMvc()
                  .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                  .AddRazorPagesOptions(options =>
              {
                    options.AllowAreas = true;
                    options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
                    options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");

                    options.Conventions.AuthorizeFolder("/Sites");
                });

            return services;
        }
    }
}

集成测试

PageTests.cs

namespace MyCarparks.Web.IntegrationTests
{
    public class PageTests : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        private readonly CustomWebApplicationFactory<Startup> factory;

        public PageTests(CustomWebApplicationFactory<Startup> webApplicationFactory)
        {
            factory = webApplicationFactory;
        }


    [Fact]
    public async Task SitesReturnsSuccessAndCorrectContentTypeAndSummary()
    {
        var siteId = Guid.NewGuid();
        var site = new Site { Id = siteId, Address = "Test site address" };
        var mockSite = new Mock<ISitesRepository>();
        mockSite.Setup(s => s.GetSiteById(It.IsAny<Guid>())).ReturnsAsync(site);

        // Arrange
        var client = factory.CreateClient();

        // Act
        var response = await client.GetAsync("http://localhost:44318/sites/sitedetails?siteId=" + siteId);

        // Assert
        response.EnsureSuccessStatusCode();

        response.Content.Headers.ContentType.ToString()
            .Should().Be("text/html; charset=utf-8");

        var responseString = await response.Content.ReadAsStringAsync();
        responseString.Should().Contain("Site Details - MyCarparks");
    }

    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.UseStartup<Startup>();
        }
    }
}

推荐答案

遵循 Chris Pratt 建议,但对于 Razor Pages .NET Core 3.1,我使用先前的请求对登录端点(这也是另一个剃刀页面)进行身份验证,并且从响应中获取 cookie.然后我添加相同的 cookie 作为 http 请求的一部分,瞧,这是一个经过身份验证的请求.

Following Chris Pratt suggestion, but for Razor Pages .NET Core 3.1 I use a previous request to authenticate against login endpoint (which is also another razor page), and grab the cookie from the response. Then I add the same cookie as part of the http request, and voila, it's an authenticated request.

这是一段使用 HttpClient 和 AngleSharp,作为微软官方文档,测试一个剃刀页面.所以我重用它来从响应中获取 cookie.

This is a piece of code that uses an HttpClient and AngleSharp, as the official microsoft documentation, to test a razor page. So I reuse it to grab the cookie from the response.

private async Task<string> GetAuthenticationCookie()
{
    var formName = nameof(LoginModel.LoginForm); //this is the bounded model for the login page
    var dto =
        new Dictionary<string, string>
        {
            [$"{formName}.Username"] = "foo",
            [$"{formName}.Password"] = "bar",
        };

    var page = HttpClient.GetAsync("/login").GetAwaiter().GetResult();
    var content = HtmlHelpers.GetDocumentAsync(page).GetAwaiter().GetResult();

    //this is the AndleSharp
    var authResult =
        await HttpClient
            .SendAsync(
                (IHtmlFormElement)content.QuerySelector("form[id='login-form']"),
                (IHtmlButtonElement)content.QuerySelector("form[id='login-form']")
                    .QuerySelector("button"),
                dto);
    _ = authResult.Headers.TryGetValues("Set-Cookie", out var values);
    return values.First();
}

然后可以重用该值并与新的 http 请求一起传递.

Then that value can be reused and passed with the new http request.

//in my test, the cookie is a given (from the given-when-then approach) pre-requirement
protected void Given()
{
    var cookie = GetAuthenticationCookie().GetAwaiter().GetResult();
    //The http client is a property that comes from the TestServer, when creating a client http for tests as usual. Only this time I set the auth cookie to it
    HttpClient.DefaultRequestHeaders.Add("Set-Cookie", cookie);
    var page = await HttpClient.GetAsync($"/admin/protectedPage");
    //this will be a 200 OK because it's an authenticated request with whatever claims and identity the /login page applied
}

这篇关于Razor Pages .NET Core 2.1 集成测试后身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆