ASP.NET Core запускает новый поток и получает правильный (старый IHttpContextAccessor) для получения ClaimsPrincipal (зарегистрированный пользователь)

Я следил за этим ответом, как правильно запустить новый поток в методе ASP.NET Core 2.0 Controller.
Мне это нужно, потому что у меня есть API, который записывает что-то в базу данных (возвращает результат обратно пользователю) и в то же время инициирует длительную фоновую задачу (некоторые вычисления в Excel), которые возвращаются пользователю через SignalR Core.

Я использую OpenIddict для авторизации / аутентификации с токеном JWT.

Проблема в том, что мне нужно получить IHttpContextAccessor так что я могу войти в систему с идентификатором пользователя:

var serviceScopeFactory = this.ServiceProvider.GetService< IServiceScopeFactory>();
Task.Factory.StartNew(() =>
{
    using (var scope = serviceScopeFactory.CreateScope())
    {
        Thread.Sleep(3000);     //Just for testing to be sure, API request is completed.
        var services = scope.ServiceProvider.GetService<IHttpContextAccessor>();
        var claims = services2.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
        var loggedInUserId = claims != null ? Int32.Parse(claims.Value) : (int?)null;

        var res = ProcessExcel();
        signalrServices.Send(loggedInUserId, res);
    }
});

Мне действительно нужно получить ClaimsPrincipal , Кто-то может подумать, что проблему можно решить с помощью локальной переменной userId, которая устанавливается перед началом нового потока. Но в реальном приложении многие классы создаются путем внедрения зависимостей, и одна локальная переменная не решит мою проблему.

Проблема в том, что services.HttpContext.User.Identity.Claims рассчитывать 0 так что userId является null,

Как я могу получить правильный UserClaims при использовании потоков с IServiceScopeFactory?

Отредактировано:

Я пытался так:

public IActionResult DoSomething(string lang, int id)
{
    DoSomething();

    //Process in new thread
    Task.Factory.StartNewWithUser(this.ServiceProvider, async scope =>
    {
        await scope.ServiceProvider.GetService<LiveCalculations>().Add(lang, id);
    });

    return new JsonResult(true);
}    

public static Task StartNewWithUser(this TaskFactory factory, IServiceProvider serviceProvider, Func<IServiceScope, Task> action)
{
    var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
    var contextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
    var user = contextAccessor.HttpContext.User.Clone();

    return Task.Factory.StartNew(() =>
    {
        using (var scope = scopeFactory.CreateScope())
        {
            Thread.CurrentPrincipal = user;             //https://forums.asp.net/t/1829657.aspx?+How+to+set+HttpContext+User+Identity+for+an+application+manually+
            scope.ServiceProvider.GetService<IHttpContextAccessor>().HttpContext.User = user;
            action(scope).Wait();
        }
    });
}

Claims.Count 15, но в какой-то случайный момент в будущем Claims.Count становится 0. Обычно после некоторого ожидаемого вызова функции. (Не всегда первый).

Плохое решение:

Единственное решение, которое я нашел (и оно мне действительно не нравится), - это создать новый HttpRequest:

public string GetAccessToken()
{
    var bearer = this.Request.Headers["Authorization"].FirstOrDefault();
    if (string.IsNullOrEmpty(bearer))
        return "";
    return bearer.Substring("Bearer ".Length);
}

public IActionResult DoSomething(int id)
{
    DoSomeWork();

    //Instead creating new thread
    var service = this.ServiceProvider.GetService<RequestService>();
    string url = $"{service.Scheme}://{service.Host}/API/excel/";
    HttpClient client = new HttpClient();
#pragma warning disable 4014
    client.PutAsync(url + $"PrceossExcel/{id}?access_token={accessToken}", null);    //notice without await
#pragma warning restore 4014

    return new JsonResult(true);
}

Это не логика для меня, чтобы создать новый запрос Http просто чтобы получить правильный пользователь (идентичность) в новом потоке. Мне бы очень хотелось получить рабочее решение с созданием новой темы.

0 ответов

Другие вопросы по тегам