不要在测试中验证 JWT 过期

Posted

技术标签:

【中文标题】不要在测试中验证 JWT 过期【英文标题】:Do not validate JWT expiration in tests 【发布时间】:2021-12-17 22:10:38 【问题描述】:

我正在使用 JWT 身份验证为我的 Web 服务编写集成测试。我想用从真实服务收到的令牌对其进行测试。问题是真正的令牌会在 1 小时后过期。

一种可能的方法是在下面的Startup 类的AddJwtBearer 中设置options.TokenValidationParameters.ValidateLifetime。 不过Startup类也是一个待测试的代码,所以我不想更改或替换它进行测试。

有没有一种简洁的方法来测试除过期之外的所有 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);
    

【问题讨论】:

AddJwtBearer lambda 中有很多不同的“验证...”属性。您已经为权威和观众设置了两个。其中一个是在谈论“终身”还是“到期”?例如:***.com/a/59014327/1043380 @gunr2171 好点,看看我的编辑。 您是在 AddJwtBearer 中对所有选项进行硬编码,还是通过配置设置它们(您应该这样做)? @gunr2171 是的,我愿意。所以解决方案是为测试项目使用不同的应用配置,太好了! 我会警惕这样做 - 如果您进行不同的配置,您可能会引入安全错误。 【参考方案1】:

最后我找到了一种管理身份验证处理程序选项的简单方法:

身份验证方案是一个名称,对应于:

身份验证处理程序。 用于配置特定处理程序实例的选项。

见MSDN。

因此,在测试设置中指定身份验证方案名称 "JWT" 作为选项实例名称后配置 JwtBearerOptions 就足够了:

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 过期的主要内容,如果未能解决你的问题,请参考以下文章

尝试进行身份验证时 JWT 令牌过期?

如何使用 UTC 过期时间验证 JWT?

在服务总线后面验证过期的 JWT 令牌

为啥我的 JWT 不记名身份验证在令牌说 5 分钟后将令牌识别为过期?

在流明 jwt 令牌中设置过期时间

在永久存储中跟踪 JWT