首页 > 解决方案 > 在测试 ASP.Net Core 3.1 Web Api 期间跳过 JWT Auth

问题描述

我有一个非常简单的应用程序,其中包含一个经过 JWT 身份验证的控制器:

[ApiController]
[Authorize]
[Route("[controller]")]
public class JwtController : ControllerBase
{

    public JwtController() { }

    [HttpGet]
    public ActionResult Get() => Ok("Working!");
}

身份验证配置为:

services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false
    };
});

在测试期间,我希望用户始终被“认证”,以便跳过 [Authorize]。

[Fact]
public async Task JwtIsSkipped()
{
    var response = (await _Client.GetAsync("/jwt")).EnsureSuccessStatusCode();
    var stringResponse = await response.Content.ReadAsStringAsync();

    Assert.Equal("Working!", stringResponse);
}

像这样运行测试会失败,所以按照这个文档,我添加了这个简单的身份验证处理程序:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public const string DefaultScheme = "Test";
    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
        var identity = new ClaimsIdentity(claims, DefaultScheme);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, DefaultScheme);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

所以现在我的测试类看起来像这样:

public class UnitTest : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _Factory;
    private readonly HttpClient _Client;

    public UnitTest(WebApplicationFactory<Startup> factory)
    {
        _Factory = factory;
        _Client = _Factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication(TestAuthHandler.DefaultScheme)
                        .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
                            TestAuthHandler.DefaultScheme, options => { });
            });
        }).CreateClient();

        _Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(TestAuthHandler.DefaultScheme);
    }

    [Fact]
    public async Task JwtIsSkipped()
    {
        var response = (await _Client.GetAsync("/jwt")).EnsureSuccessStatusCode();
        var stringResponse = await response.Content.ReadAsStringAsync();

        Assert.Equal("Working!", stringResponse);
    }
}

它仍然失败,我不知道我做错了什么。

标签: c#asp.net-coreauthenticationjwtasp.net-core-3.1

解决方案


我以前在 Microsoft 示例中遇到过类似的情况,可以向您保证它会让人头疼,它可能适用于特定的 Core 版本,但我已经放弃了。我已经通过这种方式解决了。

我的目标是在测试时授权系统,而不是AddAuthentication在我们的测试中使用我们只是创建一个FakePolicyEvaluator类并将其作为单例添加到我们的测试中。

所以放开我们的 FakePolicyEvaluator 类:

public class FakePolicyEvaluator : IPolicyEvaluator
{
    public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
    {
        var principal = new ClaimsPrincipal();
        principal.AddIdentity(new ClaimsIdentity(new[] {
            new Claim("Permission", "CanViewPage"),
            new Claim("Manager", "yes"),
            new Claim(ClaimTypes.Role, "Administrator"),
            new Claim(ClaimTypes.NameIdentifier, "John")
        }, "FakeScheme"));
        return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal,
            new AuthenticationProperties(), "FakeScheme")));
    }

    public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy,
        AuthenticateResult authenticationResult, HttpContext context, object resource)
    {
        return await Task.FromResult(PolicyAuthorizationResult.Success());
    }
}

然后在我们的ConfigureTestServices我们添加services.AddSingleton<IPolicyEvaluator, FakePolicyEvaluator>();

所以在你的测试代码中是这样的:

private readonly HttpClient _client;

public UnitTest(WebApplicationFactory<Startup> factory)
{
    _client = factory.WithWebHostBuilder(builder =>
    {
        builder.ConfigureTestServices(services =>
        {
            services.AddSingleton<IPolicyEvaluator, FakePolicyEvaluator>();
        });
    }).CreateClient();
}

[Fact]
public async Task JwtIsSkipped()
{
    var response = (await _client.GetAsync("/jwt")).EnsureSuccessStatusCode();
    var stringResponse = await response.Content.ReadAsStringAsync();

    Assert.Equal("Working!", stringResponse);
}

这就对了。现在,当您进行测试时,它将绕过身份验证。我已经使用提供的控制器对其进行了测试,并且可以正常工作。

也可以将假的放在应用程序启动中,它既可用于测试又可在开发环境下工作。检查引用的文章。

免责声明:我已经在我的个人网站参考中写了更深入的文章,您可以下载此https://github.com/maythamfahmi/BlogExamples/tree/master/BypassAuthorization的 github 版本


推荐阅读