本教程
Create an ASP.NET Core app with user data protected by authorization on docs.microsoft.com
教我们使用角色来处理授权。
但是,关于角色,我注意到了一件事:
如果用户在登录时添加到角色,则更改在注销之前似乎不可见。
那就是:如果我在登录这个特定的identityUser
时调用它:
userManager.AddToRoleAsync(idenityUser, role)
然后教程中显示的操作检查当前用户是否在角色中:
context.User.IsInRole(role)
即使User
指的是这个特定的identityUser
,也返回FALSE。并且它一直返回false,直到用户注销并再次注销。
我实际上尝试在教程中增强应用程序以添加Promote功能,即允许管理员将常规用户提升为Managers。是的,被提升的用户必须退出并在晋升后最终成为经理。
现在要求用户在推广时这样做似乎是一个不必要的不便。但对于像放下banhammer这样的问题,这显然是不可接受的。被禁止的用户可能不会及时退出。
是否有任何方法强制将用户添加到角色立即生效,即使所述用户此刻已登录?
我在使用JWT身份验证的Node.js应用程序中经历了类似的情况。在这种情况下,我已经设置了一个HTTP管道拦截器,如果某些用户声明发生了变化,它将刷新JWT令牌。
考虑到这一点,我找到了this other stack overflow post。看起来你做的就像我上面做的那样。您调整用户主体的声明并将身份验证管理器调用为reauth。如果用户没有意识到他刚刚退出/进入,你应该可以做到这一切。
当用户登录时,声明(包括角色)会保存在cookie或令牌中。这是当时索赔的副本。所以你看两个不同的来源。 ClaimsIdentity(来自cookie /令牌):
context.User.IsInRole(role)
和数据库:
userManager.AddToRoleAsync(idenityUser, role)
只要cookie或令牌有效,就不会访问数据库,也不会注意到更改。强制用户再次登录是刷新声明标识的唯一方法。
在JWT的情况下,您可以使用短期令牌,并在访问令牌到期时使用刷新令牌来检索新令牌。这样,声明将在令牌的生命周期内自动刷新。对于cookie,可能还有方法。
但您面临的主要问题是身份验证与授权混合在一起。这就是它出错的地方。
这个角色不应该是声明身份的一部分,当然也不应该是它经常变化的时候。请注意,(身份)声明应用于模拟身份(身份验证)。
解决此问题的最佳方法是将身份验证与授权分开。这样您就可以撤销授权,该授权可以立即生效,而用户无需再次登录。
我不会在这里详细说明,因为我在重复自己。但如果您有兴趣,请访问我的blog。
正确的答案是使用UserManager.IsInRoleAsync(identityUser, role)
(docs)而不是ClaimsPrincipal.IsInRole(role)
(在Razor视图中是`User.IsInRole(角色),在theSignalR hub中使用Context.User.IsInRole(角色))。
IdentityUser
可以通过ClaimsPrincipal
(UserManager.GetUserAsync(user)
)从docs获得。
所以,完整的代码将是这样的。在剃刀:
var user = await userManager.GetUserAsync(User);
if (user != null)
if(await userManager.IsInRoleAsync(user))
// ...
在SignalR中心:
var user = await userManager.GetUserAsync(Context.User);
if(user != null)
if(await userManager.IsInRoleAsync(user))
// ...
(不确定是否需要空检查,但不会受到伤害)
不幸的是,用户需要注销然后重新登录以刷新他们的角色,因为它们被存储为声明。更新他们的角色之后,您可以使他们的登录失效,这将迫使他们再次登录: