【发布时间】:2019-04-15 17:23:57
【问题描述】:
问题:我调用RoleManager.CreateAsync() 和RoleManager.AddClaimAsync() 来创建角色和相关的角色声明。然后我调用UserManager.AddToRoleAsync() 将用户添加到这些角色。但是当用户登录时,角色和相关声明都不会出现在ClaimsPrincipal(即控制器的User 对象)中。这样做的结果是User.IsInRole()总是返回false,User.Claims返回的Claims集合不包含角色声明,[Authorize(policy: xxx)]注解不起作用。
我还应该补充一点,一种解决方案是从使用新的services.AddDefaultIdentity()(由模板化代码提供)恢复为调用services.AddIdentity().AddSomething().AddSomethingElse()。我不想去那里,因为我在网上看到了太多相互矛盾的故事,关于我需要做什么来为各种用例配置AddIdentity。 AddDefaultIdentity 似乎可以正确完成大多数事情,而无需添加大量流畅的配置。
顺便说一句,我问这个问题的目的是为了回答它......除非有人给我一个比我准备发布的更好的答案。我也在问这个问题,因为经过几周的搜索,我还没有找到在 ASP.NET Core Identity 2 中创建和使用角色和声明的良好端到端示例。希望这个问题中的代码示例可以帮助其他偶然发现它的人......
设置: 我创建了一个新的 ASP.NET Core Web 应用程序,选择 Web 应用程序(模型-视图-控制器),并将身份验证更改为个人用户帐户。在生成的项目中,我执行以下操作:
-
在包管理器控制台中,更新数据库以匹配脚手架迁移:
更新数据库
-
添加一个扩展
IdentityUser的ApplicationUser类。这包括添加类,向ApplicationDbContext添加一行代码,并将项目中的每个<IdentityUser>实例替换为<ApplicationUser>。新的
ApplicationUser类:public class ApplicationUser : IdentityUser { public string FullName { get; set; } }更新后的
ApplicationDbContext类:public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } // Add this line of code public DbSet<ApplicationUser> ApplicationUsers { get; set; } } -
在包管理器控制台中,创建一个新的迁移并更新数据库以合并
ApplicationUsers实体。添加迁移 m_001
更新数据库 -
在
Startup.cs中添加以下代码行启用RoleManagerservices.AddDefaultIdentity<ApplicationUser>() .AddRoles<IdentityRole>() // <-- Add this line .AddEntityFrameworkStores<ApplicationDbContext>(); -
添加一些代码来播种角色、声明和用户。这个示例代码的基本概念是我有两个声明:
can_report允许持有者创建报告,can_test允许持有者运行测试。我有两个角色,Admin和Tester。Tester角色可以运行测试,但不能创建报告。Admin角色可以两者兼得。因此,我将声明添加到角色中,并创建一个Admin测试用户和一个Tester测试用户。首先,我添加一个类,它的唯一目的是包含本示例中其他地方使用的常量:
// Contains constant strings used throughout this example public class MyApp { // Claims public const string CanTestClaim = "can_test"; public const string CanReportClaim = "can_report"; // Role names public const string AdminRole = "admin"; public const string TesterRole = "tester"; // Authorization policy names public const string CanTestPolicy = "can_test"; public const string CanReportPolicy = "can_report"; }接下来,我播种我的角色、声明和用户。我把这段代码放在主登陆页面控制器中只是为了方便;它确实属于“启动”
Configure方法,但那是额外的六行代码......public class HomeController : Controller { const string Password = "QwertyA1?"; const string AdminEmail = "admin@example.com"; const string TesterEmail = "tester@example.com"; private readonly RoleManager<IdentityRole> _roleManager; private readonly UserManager<ApplicationUser> _userManager; // Constructor (DI claptrap) public HomeController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager) { _roleManager = roleManager; _userManager = userManager; } public async Task<IActionResult> Index() { // Initialize roles if (!await _roleManager.RoleExistsAsync(MyApp.AdminRole)) { var role = new IdentityRole(MyApp.AdminRole); await _roleManager.CreateAsync(role); await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, "")); await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanReportClaim, "")); } if (!await _roleManager.RoleExistsAsync(MyApp.TesterRole)) { var role = new IdentityRole(MyApp.TesterRole); await _roleManager.CreateAsync(role); await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, "")); } // Initialize users var qry = _userManager.Users; IdentityResult result; if (await qry.Where(x => x.UserName == AdminEmail).FirstOrDefaultAsync() == null) { var user = new ApplicationUser { UserName = AdminEmail, Email = AdminEmail, FullName = "Administrator" }; result = await _userManager.CreateAsync(user, Password); if (!result.Succeeded) throw new InvalidOperationException(string.Join(" | ", result.Errors.Select(x => x.Description))); result = await _userManager.AddToRoleAsync(user, MyApp.AdminRole); if (!result.Succeeded) throw new InvalidOperationException(string.Join(" | ", result.Errors.Select(x => x.Description))); } if (await qry.Where(x => x.UserName == TesterEmail).FirstOrDefaultAsync() == null) { var user = new ApplicationUser { UserName = TesterEmail, Email = TesterEmail, FullName = "Tester" }; result = await _userManager.CreateAsync(user, Password); if (!result.Succeeded) throw new InvalidOperationException(string.Join(" | ", result.Errors.Select(x => x.Description))); result = await _userManager.AddToRoleAsync(user, MyApp.TesterRole); if (!result.Succeeded) throw new InvalidOperationException(string.Join(" | ", result.Errors.Select(x => x.Description))); } // Roles and Claims are in a cookie. Don't expect to see them in // the same request that creates them (i.e., the request that // executes the above code to create them). You need to refresh // the page to create a round-trip that includes the cookie. var admin = User.IsInRole(MyApp.AdminRole); var claims = User.Claims.ToList(); return View(); } [Authorize(policy: MyApp.CanTestPolicy)] public IActionResult Test() { return View(); } [Authorize(policy: MyApp.CanReportPolicy)] public IActionResult Report() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } }我在“启动”
ConfigureServices例程中注册了我的身份验证策略,就在调用services.AddMvc之后// Register authorization policies services.AddAuthorization(options => { options.AddPolicy(MyApp.CanTestPolicy, policy => policy.RequireClaim(MyApp.CanTestClaim)); options.AddPolicy(MyApp.CanReportPolicy, policy => policy.RequireClaim(MyApp.CanReportClaim)); });
哇。现在,(假设我已经注意到上面添加到项目中的所有适用代码),当我运行应用程序时,我注意到我的“内置”测试用户都不能访问/home/Test或/home/Report 页面。此外,如果我在 Index 方法中设置断点,我会看到 User 对象中不存在我的角色和声明。但我可以查看数据库并查看所有角色和声明。
【问题讨论】:
-
用户如何认证?您的任何代码都没有表明应用程序如何知道当前用户是谁。
-
用户登录。这是我从模板中得到的部分代码。
-
是否有包含用户 ID 的声明?
-
当用户登录时,
User.Claims列表包含nameidentifier(带标点符号 [破折号] 的 GUID)、name(用户名)和SecurityStamp(不带标点符号的 GUID )。但是,因为我已将角色与用户相关联,并且该角色具有关联的声明,所以我还应该在集合中看到这些声明。 (您必须等待答案才能找出我的自定义声明不存在的原因。;) -
您是否尝试添加自定义声明
标签: asp.net-core asp.net-core-mvc asp.net-identity