Код: Выделить всё
fail: [namespace].[mycustommiddleware][0]
Request has finished and HttpContext disposed.
Object name: 'HttpContext'.
System.ObjectDisposedException: Request has finished and HttpContext disposed.
Object name: 'HttpContext'.
at Microsoft.AspNetCore.Http.DefaultHttpContext.ThrowContextDisposed()
at Microsoft.AspNetCore.Http.DefaultHttpContext.get_Features()
at Microsoft.AspNetCore.Authentication.RequestPathBaseCookieBuilder.Build(HttpContext context, DateTimeOffset expiresFrom)
at Microsoft.AspNetCore.Http.CookieBuilder.Build(HttpContext context)
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.BuildCookieOptions()
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
< /code>
Далее приведен мой код, настраивая задачи и создание запросов.[Test]
public async Task KeepMvcSessionAliveMiddleware_ShouldNotMixTokens()
{
// Arrange
var baseUrl = $"http://localhost:{5050}";
var companies = new[] { "ACME", "Globex", "Initech", "Umbrella", "WayneTech" };
var usersPerCompany = 3;
var userPool = companies
.SelectMany(company => Enumerable.Range(1, usersPerCompany).Select(i =>
{
var tokenDescriptor = GetTokenDescriptor(company);
var refreshTokenDescriptor = GetRefreshTokenDescriptor();
return new
{
UserName = $"{company}_User{i}",
Company = company,
Token = _tokenHandler.CreateToken(tokenDescriptor),
RefreshToken = _tokenHandler.CreateToken(refreshTokenDescriptor)
};
}))
.ToArray();
using (var warmupHandler = new HttpClientHandler { CookieContainer = new CookieContainer() })
using (var warmupClient = new HttpClient(warmupHandler) { BaseAddress = new Uri(baseUrl) })
{
var warmupCookie = CreateCookie("WarmUp", "Test", _dataProtectionProvider);
warmupHandler.CookieContainer = warmupCookie;
await warmupClient.GetAsync("/warmup");
}
// Act
var tasks = Enumerable.Range(1, 50).Select(async i =>
{
var random = new Random();
var user = userPool[random.Next(userPool.Length)];
using (var handler = new HttpClientHandler
{
CookieContainer = CreateCookie(user.Company, user.UserName, _dataProtectionProvider)
}) using (var client = new HttpClient(handler) { BaseAddress = new Uri(baseUrl) })
{
handler.CookieContainer.Add(new Cookie("RefreshToken", user.RefreshToken, "/", "localhost"));
handler.CookieContainer.Add(new Cookie("Token", user.Token, "/", "localhost"));
var task = client.GetAsync("/");
_activeRequests.Add(task);
var response = await task;
response.EnsureSuccessStatusCode();
Assert.That(response, Is.Not.Null);
}
});
await Task.WhenAll(tasks.ToArray());
foreach (var log in _loggerProvider.Logs)
{
TestContext.WriteLine(log);
}
}
Изменить:
Вот метод Invokeasync из промежуточного программного обеспечения:
public async Task InvokeAsync(HttpContext context)
{
try
{
var jwtExp = _configuration.GetValue("JWT:Expiration", 2);
if (context.Response.HasStarted) return;
// Check if the user is authenticated
if (!RequestingStaticFile(context) && context.User.Identity!.IsAuthenticated)
{
var model = GetTokenCookies(context);
if (string.IsNullOrEmpty(model.RefreshToken) ||
(await IsRefreshTokenExpired(model.RefreshToken)))
{
_logger.Log(LogLevel.Information, $"Refresh token expired or missing. Logging out user: {context.User.Identity.Name}");
// Refresh token has expired. End the session
await context.SignOutAsync();
// Call the next middleware in the pipeline (which will be YARP)
await _next(context);
return;
}
// Check the cache for the session timeout value
var companyName = context.User.Claims.FirstOrDefault(c => c.Type == "Company")?.Value.Replace(" ", string.Empty) ?? "Company";
var cacheKey = $"SessionTimeout_{companyName}_{context.User.Identity.Name}";
// Attempt to retrieve the cached sessionTimeout value
int sessionTimeout = await _memoryCache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(3);
entry.SlidingExpiration = TimeSpan.FromMinutes(2);
var token = context.Request.Cookies["Token"];
var companySettingsResult = await _companyService.GetCompanySettingsAsync(token ?? string.Empty);
return companySettingsResult?.Data?.SessionTimeout ?? 60;
});
var authResult = await context.AuthenticateAsync("LegacyAuth");
if (authResult.Succeeded && authResult.Properties != null)
{
authResult.Properties.AllowRefresh = true;
authResult.Properties.ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(sessionTimeout));
// Sign in to refresh the authentication cookie
await context.SignInAsync("LegacyAuth", authResult.Principal, authResult.Properties);
await TryRefreshTokensIfNeededAsync(context, model, companyName, jwtExp);
}
}
// Continue to the next middleware in the pipeline
await _next(context);
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, ex.Message);
}
}
< /code>
Я использую NUNIT в качестве структуры тестирования. Я отлаживал и с тех пор ушел от TestServer и получаю те же результаты с помощью сервера Kestrel.
Код: Выделить всё
return Host.CreateDefaultBuilder()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddProvider(loggerProvider);
logging.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseKestrel()
.UseUrls($"http://localhost:{port}")
.ConfigureServices(services =>
{
services.AddAuthentication("LegacyAuth")
.AddCookie("LegacyAuth", options =>
{
options.Cookie.Name = "Test";
options.Events.OnValidatePrincipal = async context =>
{
// Simulate successful validation (optional)
await Task.CompletedTask;
};
});
services.AddAuthorization();
configureServices?.Invoke(services);
})
.Configure(app =>
{
app.UseAuthentication();
app.UseAuthorization();
configureApp(app);
});
})
.Build();
< /code>
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
_tokenServiceMock = new Mock();
_loggerProvider = new TestLoggerProvider();
_testHost = TestHostBuilder.BuildHost(5050, app =>
{
app.UseMiddleware();
app.Run(async ctx =>
{
await ctx.Response.WriteAsync("OK");
});
}, services =>
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton(_tokenServiceMock.Object);
services.AddHttpClient();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton(provider => new JwtValidatorService(new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "Test",
ValidAudience = "Test",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("88888888-4444-4444-4444-222222222222-88888888-4444-4444-4444-222222222222"))
}));
}, _loggerProvider);
await _testHost.StartAsync();
_dataProtectionProvider = _testHost.Services.GetRequiredService();
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... isposed-wh
Мобильная версия