不要在测试中验证 JWT 过期时间
Do not validate JWT expiration in tests
我正在使用 JWT 身份验证为我的 Web 服务编写集成测试。我想用从真实服务收到的令牌来测试它。问题是真正的令牌将在 1 小时后过期。
一种可能的方法是在下面的 class Startup
的 AddJwtBearer
中设置 options.TokenValidationParameters.ValidateLifetime
。
不过,Startup
class也是一个待测代码,所以我不想更改或替换它来测试。
是否有一种巧妙的方法来测试除过期之外的所有 JWT 验证逻辑?
我的项目代码:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("JWT")
.AddJwtBearer("JWT", options =>
{
options.Authority = "https://my-real-identity-server.com/";
options.Audience = "...";
// I don't want to disable lifetime validation in real life
// options.TokenValidationParameters.ValidateLifetime = false;
});
// Other stuff
}
public void Configure(IApplicationBuilder app) => app
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseEndpoints(endpoints => endpoints.MapControllers());
}
public class TestController : ControllerBase
{
[Authorize]
[HttpGet("/validate")]
public string Get() => "success";
}
我的测试代码:
public class HostBuilderTests
{
private IHost testHost;
private CancellationTokenSource cancel;
private HttpClient client;
[SetUp]
public async Task ShouldReturnStatus()
{
testHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
webBuilder
.UseStartup<Startup>()
.UseTestServer())
.ConfigureServices(services => services
.AddLogging(b => b.ClearProviders().AddNUnit()))
.Build();
cancel = new CancellationTokenSource(10000);
var server = testHost.Services.GetRequiredService<IServer>()
.ShouldBeOfType<TestServer>();
await testHost.StartAsync(cancel.Token);
client = server.CreateClient();
}
[TearDown]
public async Task TearDown()
{
await testHost.StopAsync(cancel.Token);
client.Dispose();
testHost.Dispose();
cancel.Dispose();
}
[Test]
[TestCase("<<JWT token copied from the real service>>")]
public async Task StatusShouldBeOk(string realToken)
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", realToken);
using var response = await client.GetAsync("/validate", cancel.Token);
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
}
最后我找到了一种管理身份验证处理程序选项的简单方法:
An authentication scheme is a name which corresponds to:
- An authentication handler.
- Options for configuring that specific instance of the handler.
见MSDN。
因此 post-configure JwtBearerOptions
指定身份验证方案名称 "JWT"
作为测试设置中的选项实例名称就足够了:
testHost = Host.CreateDefaultBuilder()
// other setup
.ConfigureServices(services => services
.PostConfigure<JwtBearerOptions>("JWT",
op => op.TokenValidationParameters.ValidateLifetime = false)
// other setup
).Build();
也可以传递 null
而不是 "JWT"
,因为它写在 comments:
// Null name is used to configure all named options.
我正在使用 JWT 身份验证为我的 Web 服务编写集成测试。我想用从真实服务收到的令牌来测试它。问题是真正的令牌将在 1 小时后过期。
一种可能的方法是在下面的 class Startup
的 AddJwtBearer
中设置 options.TokenValidationParameters.ValidateLifetime
。
不过,Startup
class也是一个待测代码,所以我不想更改或替换它来测试。
是否有一种巧妙的方法来测试除过期之外的所有 JWT 验证逻辑?
我的项目代码:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("JWT")
.AddJwtBearer("JWT", options =>
{
options.Authority = "https://my-real-identity-server.com/";
options.Audience = "...";
// I don't want to disable lifetime validation in real life
// options.TokenValidationParameters.ValidateLifetime = false;
});
// Other stuff
}
public void Configure(IApplicationBuilder app) => app
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseEndpoints(endpoints => endpoints.MapControllers());
}
public class TestController : ControllerBase
{
[Authorize]
[HttpGet("/validate")]
public string Get() => "success";
}
我的测试代码:
public class HostBuilderTests
{
private IHost testHost;
private CancellationTokenSource cancel;
private HttpClient client;
[SetUp]
public async Task ShouldReturnStatus()
{
testHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
webBuilder
.UseStartup<Startup>()
.UseTestServer())
.ConfigureServices(services => services
.AddLogging(b => b.ClearProviders().AddNUnit()))
.Build();
cancel = new CancellationTokenSource(10000);
var server = testHost.Services.GetRequiredService<IServer>()
.ShouldBeOfType<TestServer>();
await testHost.StartAsync(cancel.Token);
client = server.CreateClient();
}
[TearDown]
public async Task TearDown()
{
await testHost.StopAsync(cancel.Token);
client.Dispose();
testHost.Dispose();
cancel.Dispose();
}
[Test]
[TestCase("<<JWT token copied from the real service>>")]
public async Task StatusShouldBeOk(string realToken)
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", realToken);
using var response = await client.GetAsync("/validate", cancel.Token);
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
}
最后我找到了一种管理身份验证处理程序选项的简单方法:
An authentication scheme is a name which corresponds to:
- An authentication handler.
- Options for configuring that specific instance of the handler.
见MSDN。
因此 post-configure JwtBearerOptions
指定身份验证方案名称 "JWT"
作为测试设置中的选项实例名称就足够了:
testHost = Host.CreateDefaultBuilder()
// other setup
.ConfigureServices(services => services
.PostConfigure<JwtBearerOptions>("JWT",
op => op.TokenValidationParameters.ValidateLifetime = false)
// other setup
).Build();
也可以传递 null
而不是 "JWT"
,因为它写在 comments:
// Null name is used to configure all named options.