角色'permissions'转换角色时留下
Role 'permissions' stay when changing role
我有一个 ASP.NET 核心 MVC 站点,带有 SQLite 数据连接,使用 ASP.NET 核心授权,如下所示:
// Startup.ConfigureServices
services.AddAuthorization(e =>
{
e.AddPolicy(Policies.UserRead, b => b.RequireRole("Editor", "Root"));
}
这是限制访问包含用户信息的站点的策略之一。 (Policies.UserRead
是常数 string
)。然后将此策略应用于这样的视图:
[Authorize(Policies.UserRead)]
public async Task<IActionResult> Index()
{
}
效果很好,只有具有 Editor
或 Root
角色的用户才能访问该视图。但是当登录时更改用户角色时会出现问题。例如
- 用户 A(角色
Editor
)登录并使用 Index()
- 成功
- 用户 B(角色
Root
)登录并从用户 A 中删除角色 Editor
- 用户 A 使用
Index()
- 仍然成功
您会认为用户 A 不能再访问 Index()
,因为他不再是角色 Editor
。但他仍然可以——只要他不注销并重新登录,因为重新登录可以解决这个问题。似乎有人(我认为 ClaimsPrincipal
是这里的罪魁祸首)缓存了这个角色 - 如果我知道如何使缓存无效,这将是 OK
...
角色转换代码:
// get the user whos role is changed
var targetUser = await _context.Users.SingleOrDefaultAsync(m => m.Id == model.Id);
if (targetUser == null) return NotFound();
// get the user who changes the role
var sourceUser = await _userManager.GetUserAsync(User);
if (sourceUser == null) return RedirectToAction("Index");
// remove the current role
await _userManager.RemoveFromRoleAsync(targetUser, targetUser.Role.ToString());
// add to the new role
await _userManager.AddToRoleAsync(targetUser, model.Role.ToString());
// update & save the changes
_context.Update(targetUser);
await _context.SaveChangesAsync();
这基本上是我用来更改用户角色的代码(我删除了 view/model 部分,因为它们不相关)。备注:
targetUser
和 sourceUser
都是 ApplicationUser
(实现了 IdentityUser
)。
_userManger
是 - 谁会想到 - 类型 UserManager<ApplicationManger>
我尝试使用 SignInManger<>
重新登录用户,但似乎您只能注销当前用户 - 这将是正在更改角色的用户,而不是角色将被更改的用户。
我错过了什么?如果用户无需执行任何操作(例如重新登录)即可 "refresh" 用户角色,那就太好了。
问题是用户的角色声明存储在 cookie 中(aspnet 身份的默认实现),因此除非用户注销,否则即使用户的角色发生变化,授权结果也不会改变。解决方案是使用 ValidateAsync
事件。 Example 存在于官方文档中。
另一种可能的解决方案是从 cookie 中排除角色声明并使用声明转换。
为此,您需要覆盖 UserClaimsPrincipalFactory
的 CreateAsync
方法,请参阅此 article how to change claims. Then you can use claims transformation 添加角色声明。
我有一个 ASP.NET 核心 MVC 站点,带有 SQLite 数据连接,使用 ASP.NET 核心授权,如下所示:
// Startup.ConfigureServices
services.AddAuthorization(e =>
{
e.AddPolicy(Policies.UserRead, b => b.RequireRole("Editor", "Root"));
}
这是限制访问包含用户信息的站点的策略之一。 (Policies.UserRead
是常数 string
)。然后将此策略应用于这样的视图:
[Authorize(Policies.UserRead)]
public async Task<IActionResult> Index()
{
}
效果很好,只有具有 Editor
或 Root
角色的用户才能访问该视图。但是当登录时更改用户角色时会出现问题。例如
- 用户 A(角色
Editor
)登录并使用Index()
- 成功 - 用户 B(角色
Root
)登录并从用户 A 中删除角色 - 用户 A 使用
Index()
- 仍然成功
Editor
您会认为用户 A 不能再访问 Index()
,因为他不再是角色 Editor
。但他仍然可以——只要他不注销并重新登录,因为重新登录可以解决这个问题。似乎有人(我认为 ClaimsPrincipal
是这里的罪魁祸首)缓存了这个角色 - 如果我知道如何使缓存无效,这将是 OK
...
角色转换代码:
// get the user whos role is changed
var targetUser = await _context.Users.SingleOrDefaultAsync(m => m.Id == model.Id);
if (targetUser == null) return NotFound();
// get the user who changes the role
var sourceUser = await _userManager.GetUserAsync(User);
if (sourceUser == null) return RedirectToAction("Index");
// remove the current role
await _userManager.RemoveFromRoleAsync(targetUser, targetUser.Role.ToString());
// add to the new role
await _userManager.AddToRoleAsync(targetUser, model.Role.ToString());
// update & save the changes
_context.Update(targetUser);
await _context.SaveChangesAsync();
这基本上是我用来更改用户角色的代码(我删除了 view/model 部分,因为它们不相关)。备注:
targetUser
和sourceUser
都是ApplicationUser
(实现了IdentityUser
)。_userManger
是 - 谁会想到 - 类型UserManager<ApplicationManger>
我尝试使用 SignInManger<>
重新登录用户,但似乎您只能注销当前用户 - 这将是正在更改角色的用户,而不是角色将被更改的用户。
我错过了什么?如果用户无需执行任何操作(例如重新登录)即可 "refresh" 用户角色,那就太好了。
问题是用户的角色声明存储在 cookie 中(aspnet 身份的默认实现),因此除非用户注销,否则即使用户的角色发生变化,授权结果也不会改变。解决方案是使用 ValidateAsync
事件。 Example 存在于官方文档中。
另一种可能的解决方案是从 cookie 中排除角色声明并使用声明转换。
为此,您需要覆盖 UserClaimsPrincipalFactory
的 CreateAsync
方法,请参阅此 article how to change claims. Then you can use claims transformation 添加角色声明。