. NET Базовый API JwtBearerEvents.TokenValidated - состояние гонки между несколькими запросами API - PullRequest
1 голос
/ 29 апреля 2020

Я переопределяю JwtBearerEvents.TokenValidated, чтобы сопоставить пользователя в моей базе данных с аутентифицированным пользователем (из сторонней службы аутентификации - через JWT), а затем заполнил некоторые пользовательские заявки для использования в моем API (пользователь уровень подписки, например).

Этот код переходит из Startup.cs и вызывается для каждого запроса API. В TokenValidated я пытаюсь загрузить пользователя, связанного с токеном, и, если он не существует, я создаю пользователя в базе данных.

Однако у меня есть сценарий, когда клиент будет делать несколько запросов против моего API, и если это пользователь, которого еще нет, API создает несколько пользователей - по одному на каждый раз, когда TokenValidated вызывается параллельно.

Код:

public class CustomJwtBearerEvents : JwtBearerEvents
{
    private IUsersRepository _usersRepository;

    public CustomJwtBearerEvents(IUsersRepository usersRepository)
    {
        this._usersRepository = usersRepository;
    }

    public override async Task TokenValidated(TokenValidatedContext context)
    {
        var subject = context.Principal.FindFirstValue("user_id");

        var dbUser = await _usersRepository.GetOrCreateUser(subject, context.Principal);

        // Populate custom claims
    }
}

Я уверен, что подхожу к этому неправильно, но я не уверен, как лучше всего решить эту проблему - поэтому это мой вопрос:

Как мне сопоставить сторонних пользователей аутентификации с пользователями в моя база данных, в то же время разрешая одновременные запросы API?

1 Ответ

1 голос
/ 29 апреля 2020

Есть несколько способов подойти к этому. Наиболее прямолинейным может быть реализация lock вокруг части кода, которая вызывает _usersRepository.GetOrCreateUser(subject, context.Principal);, которая обеспечила бы доступ только одной нити за раз к этой строке кода, предотвращая любые грязные чтения и дублирующие вставки.

Этот код будет выглядеть примерно так

public class CustomJwtBearerEvents : JwtBearerEvents
{
    private readonly object _key = new object();
    private IUsersRepository _usersRepository;

    public CustomJwtBearerEvents(IUsersRepository usersRepository)
    {
        this._usersRepository = usersRepository;
    }

    public override async Task TokenValidated(TokenValidatedContext context)
    {
        var subject = context.Principal.FindFirstValue("user_id");

        lock(_key)
        {
            var dbUser = await _usersRepository.GetOrCreateUser(subject, context.Principal);
        }

        // Populate custom claims
    }
}

Как вы можете догадаться, это не будет наиболее эффективным, фактически заставляя каждый из ваших вызовов API ждать всех остальных вызовов fini sh.

Чтобы немного оптимизировать, вы можете подумать о добавлении GetUser() метода к вашему IUsersRepository, который можно использовать за пределами lock ed части кода.

Это позволит выполнять любые запросы пользователей, которые уже существуют, без ожидания и блокировки кода только при создании нового пользователя.

public class CustomJwtBearerEvents : JwtBearerEvents
{
    private readonly object _key = new object();
    private IUsersRepository _usersRepository;

    public CustomJwtBearerEvents(IUsersRepository usersRepository)
    {
        this._usersRepository = usersRepository;
    }

    public override async Task TokenValidated(TokenValidatedContext context)
    {
        var subject = context.Principal.FindFirstValue("user_id");

        var dbUser = await _usersRepository.GetUser(subject, context.Principal);

        if (dbUser is null) 
        {
            lock(_key)
            {
                dbUser = await _usersRepository.GetOrCreateUser(subject, context.Principal);
            }
        }

        // Populate custom claims
    }
}

Все это говорит Microsoft.As pNet .Identity Framework отлично справляется с управлением внешними учетными записями пользователей, если вы заинтересованы в этом.

Вот ссылка на их документы по теме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...