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

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

Я использую 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-запрос только для того, чтобы получить правильного пользователя (идентификатор) в новом потоке. Мне бы очень хотелось получить рабочее решение с созданием новой темы.

...