Идентификатор ASP.NET, добавьте другого пользователя в роль мгновенно (им не нужно выходить из системы и снова)

Прежде всего, я знаю об этом вопросе: MVC 5 AddToRole требует выхода из системы до его работы?

и этот: Что такое IUserSecurityStampStore <TUser> интерфейс?

поэтому, пожалуйста, не отмечайте это как дубликат.

Я пытаюсь добавить другого пользователя к роли (т.е. пользователь, которого мы добавляем в эту роль, не является текущим пользователем. Если это так, ответ на первый вопрос, к которому я привязан, является достаточным.)

Так же:

IdentityResult result = await userManager.AddToRoleAsync(userID, roleName);

Две ситуации, в которых я делаю это, - это: от страницы администратора, где текущий пользователь является администратором; и webhook, обеспеченный базовой аутентификацией (где вообще нет текущего пользователя).

ПРОБЛЕМА: если пользователь, к которому относится это изменение, входит в систему и использует приложение, мне нужно, чтобы изменения "добавить к роли" применились мгновенно. Им не нужно будет снова и снова выходить из системы, чтобы это произошло, и это должно произойти сразу.

Спасибо всем.

EDIT: Кстати, User.IsInRole(roleName) требует выхода из системы и входа в систему, чтобы отразить добавление в новую роль. UserManager.IsInRole(userID, roleName) этого не делает, потому что (я предполагаю) он идет прямо к таблицам базы данных, которые нужно проверить. Но если пользователь нажимает на метод действия, защищенный ролью, к которой они только что добавлены, им все равно придется снова войти в систему, что достаточно справедливо. Все еще любопытно, есть ли способ обойти это.

EDIT: Вот исходный код атрибута Authorize: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4e40cdef9c8a8226685f95ef03b746bc8322aa92/src/System.Web.Mvc/AuthorizeAttribute.cs

В нем используется User.IsInRole, поэтому мы должны снова войти в систему. Кажется, что метод переопределить - AuthorizeCore (HttpContextBase httpContext). Я не храбрый и не достаточно хорош, чтобы покончить с этим прямо сейчас, но если вы хотите, чтобы много людей найдут это полезным.

Ответ 1

Начиная со дна вашего вопроса. User.IsInRole() переходит в файл cookie пользователя и проверяет, какие роли хранятся в этом файле cookie. Следовательно, для перехода к изменениям требуется переход. И да, вы правы, говоря, что UserManager.IsInRole() проверяет с базой данных, а не с файлом cookie.

Чтобы убедиться, что изменения роли применяются немедленно, вам нужно проверить изменение ролей при каждом запросе. Для этого в Startup.Auth.cs найдите эту строку:

OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
    validateInterval: TimeSpan.FromMinutes(0), // <-- This is zero. Check on every request
    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),

Это базовый способ обновления cookie. По умолчанию validateInterval устанавливается в течение 30 минут. Если вы установите его на ноль, система создаст новый файл cookie с обновленными ролями для каждого запроса. Это может быть слишком большой нагрузкой DB, если у вас достаточно пользователей, поражающих вашу систему одновременно. Поэтому я увеличил бы промежуток времени до 30 секунд - 1-2 минуты.

Эта функция была создана как способ выйти из всех сеансов с помощью одного изменения пароля. Но также хорошо работает для ваших целей.

Ответ 2

В ядре ASP.NET SignInManager.RefreshSignInAsync() решает это.

Ответ 3

Для ASP.NET Core Identity 2 решение должно использовать:

services.Configure<SecurityStampValidatorOptions>(options =>
{
    options.ValidationInterval = TimeSpan.FromMinutes(1);
});

Чтобы принудительно обновлять каждую минуту или использовать TimeSpan.Zero для принудительного обновления каждый раз, когда пользователь заходит на страницу (обратите внимание, что каждый раз, когда выполняется запрос к базе данных).

Также убедитесь, что если вы переписываете cookie файлы, не используйте:

        services.ConfigureApplicationCookie(options =>
        {
            options.Events = new CookieAuthenticationEvents(){
            ...
            };
        }

Но перезаписать нужные вам события напрямую, так как в противном случае проверка не вызывается:

        services.ConfigureApplicationCookie(options =>
        {
            options.Events.OnRedirectToLogin = ctx => {
            ...
            };
        }