ASP.NET Core AddJwtBearer TimeProvider:TimeTravel 证明 Token 验证

问题描述 投票:0回答:1

我们正在尝试通过跳跃到未来(通过使用我们的 IClock 抽象)来加速一些集成测试(.NET 8)。

这本身工作正常,但在创建一个新的 JWT 令牌(也具有正确的时间戳)后,验证失败并显示

www-authenticate: Bearer error="invalid_token",error_description="令牌在 '07/26/2024 17:56:05'" 之前无效

所以看起来身份验证中间件仍在使用系统时间,这有点奇怪,因为我们将时间提供者传递给了

AddJwtBearer

services.AddSingleton<IClock>(ClockMutable.Default);

services
    .AddAuthentication()
    .AddJwtBearer(AuthenticationSchemes.Default, c => {
        ...
        c.TimeProvider = ClockMutable.Default;
        ...
    });

也不会调用

ClockMutable.Default
实例的任何属性。

我需要做什么才能

AddJwtBearer
使用提供的
TimeProvider

编辑: IClock 实现

public class ClockMutable : TimeProvider, IClock {
    private DateTimeOffset? stoppedOn;
    private TimeSpan offset;

    public DateTimeOffset Now => (stoppedOn != null ? stoppedOn.Value.ToLocalTime() : DateTimeOffset.Now) + offset;
    public DateTimeOffset UtcNow => (stoppedOn != null ? stoppedOn.Value : DateTimeOffset.UtcNow) + offset;
    public DateTime UtcDateTime => DateTime.UtcNow;

    public void Resume() {
        if(stoppedOn == null) return;
        offset += stoppedOn.Value - DateTimeOffset.UtcNow;
        stoppedOn = null;
    }

    public void Pause() {
        if(stoppedOn != null) return;
        stoppedOn = DateTimeOffset.UtcNow;
    }

    public void SkipForward(TimeSpan delta) {
        if(delta < TimeSpan.Zero) throw new Exception();
        offset += delta;
    }
    public void Set(DateTimeOffset future) {
        if(future < UtcNow) throw new Exception();
        offset = TimeSpan.Zero;
        stoppedOn = future;
    }

    public void Reset() {
        stoppedOn = null;
        offset = TimeSpan.Zero;
    }

    public override DateTimeOffset GetUtcNow() => UtcNow;
}

asp.net-core authentication testing time jwt
1个回答
0
投票

由于未调用实例,可能是您的提供商中未持续注册服务的问题。我这里有一个解决方法。

1.在IntegrationTestFixture.cs中再次注册实例

public class IntegrationTestFixture : IDisposable
{
    public ServiceProvider ServiceProvider { get; private set; }
    public MockClock MockClock { get; private set; }

    public IntegrationTestFixture()
    {
        var serviceCollection = new ServiceCollection();

        // Register the instance here
        var initialTime = new DateTime(2024, 7, 30);
        MockClock = new MockClock(initialTime);

        var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true)
        .Build();

        serviceCollection.AddSingleton<IConfiguration>(configuration);

        serviceCollection.AddSingleton<IClock>(MockClock);
        serviceCollection.AddSingleton<TimeProvider>(new CustomTimeProvider(MockClock));
        serviceCollection.AddTransient<TokenService>();
        serviceCollection.AddTransient<MyService>();

        ServiceProvider = serviceCollection.BuildServiceProvider();
    }

    public void Dispose()
    {
        if (ServiceProvider is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

2。然后在测试 MyServiceIntegrationTests.cs 中,令牌在模拟时间验证。

    public class MyServiceIntegrationTests : 
        IClassFixture<IntegrationTestFixture>
    {
    private readonly IntegrationTestFixture _fixture;

    public MyServiceIntegrationTests(IntegrationTestFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public void Test_DoSomething()
    {
        var tokenService = _fixture.ServiceProvider.GetService<TokenService>();
        if (tokenService == null)
            throw new InvalidOperationException("Failed to retrieve TokenService from service provider.");

        var token = tokenService.GenerateToken();

        // Act
        // Simulate advancing time to check if the token will be valid after a certain time
        _fixture.MockClock.Advance(TimeSpan.FromMinutes(15));
        var isTokenValidAfter15Minutes = tokenService.ValidateToken(token);

        _fixture.MockClock.Advance(TimeSpan.FromMinutes(20));
        var isTokenValidAfter35Minutes = tokenService.ValidateToken(token); // token should now be expired

        // Assert
        Assert.True(isTokenValidAfter15Minutes);
        Assert.False(isTokenValidAfter35Minutes);
    }
    }

enter image description here enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.