ASP.NET Identity, add another user to role instant

2019-03-18 17:30发布

问题:

First of all, I'm aware of this question: MVC 5 AddToRole requires logout before it works?

and this one: What is ASP.NET Identity's IUserSecurityStampStore<TUser> interface?

so please don't mark this as a duplicate.

I'm trying to add another user to a role (i.e. the user we're adding to the role is not the current user. If they are, the answer to first question I linked to is sufficient.)

Like so:

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

The two situations I'm doing this in are: from an admin page, where the current user is the administrator; and a webhook secured with basic authentication (where there is no current user at all).

THE PROBLEM: if the user that this change applies to is logged in and using the app, I need the "add to role" change to apply instantly. They shouldn't have to log out and in again for the change to happen, and it needs to happen straight away.

Thanks everyone.

EDIT: By the way, User.IsInRole(roleName) requires logout and login to reflect being added to the new role. UserManager.IsInRole(userID, roleName) does not, because (I assume) it goes straight to the database tables to check. But if the user hits an action method secured with the role they've just been added to, they still have to log in again, which is fair enough. Still curious if there's a way around this.

EDIT: Here is the source code for the Authorize attribute: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4e40cdef9c8a8226685f95ef03b746bc8322aa92/src/System.Web.Mvc/AuthorizeAttribute.cs

It uses User.IsInRole, which is essentially why we need to log in again. It seems the method to override is AuthorizeCore(HttpContextBase httpContext). I'm not brave or good enough to mess with this right now but if you want to have a go lots of people will find this helpful.

回答1:

Starting from the bottom of your question. User.IsInRole() goes into user cookie and checks what roles are stored in that cookie. Hence it requires relogin for changes to take effect. And yes, you are correct in saying that UserManager.IsInRole() checks with database, not with the cookie.

To make sure role changes are applied immediately you need to check for change in roles on every request. To do that in Startup.Auth.cs find this line:

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

This is a framework's way of updating cookie. By default validateInterval is set for 30 minutes. If you set it to zero, the system will create a new cookie with updated roles on every request. This might be too much DB-load if you have enough users hitting your system at the same time. So I'd increase the timespan to 30-seconds-1-2minutes.

This feature was built as a way to logout all sessions by a single password change. But also works well for your purposes.



回答2:

In ASP.NET Core, SignInManager.RefreshSignInAsync() solves this.