gpt4 book ai didi

asp.net-core - ASP.NET Core Identity 2 : User. IsInRole 始终返回 false

转载 作者:行者123 更新时间:2023-12-01 17:26:49 26 4
gpt4 key购买 nike

问题:我调用RoleManager.CreateAsync()RoleManager.AddClaimAsync()创建角色和关联的角色声明。然后我打电话UserManager.AddToRoleAsync()将用户添加到这些角色。但是当用户登录时,角色和关联的声明都不会显示在 ClaimsPrincipal 中。 (即 Controller 的 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 应用程序(模型- View - Controller ),并将身份验证更改为个人用户帐户。在最终的项目中,我执行以下操作:

  • 在程序包管理器控制台中,更新数据库以匹配支架迁移:​​

    update-database

  • 添加 ApplicationUser扩展类 IdentityUser 。这涉及到添加类,将一行代码添加到 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实体。

    add-migration m_001
    update-database

  • Startup.cs 中添加以下代码行启用RoleManager

    services.AddDefaultIdentity<ApplicationUser>()
    .AddRoles<IdentityRole>() // <-- Add this line
    .AddEntityFrameworkStores<ApplicationDbContext>();
  • 向种子角色、声明和用户添加一些代码。此示例代码的基本概念是我有两个声明: can_report允许持有人创建报告,并且can_test允许持有者运行测试。我有两个角色,AdminTesterTester角色可以运行测试,但无法创建报告。 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";
    }

    接下来,我播种我的角色、声明和用户。我将此代码放在主登陆页面 Controller 中只是为了方便;它确实属于“启动”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 中。目的。但我可以查看数据库并看到所有角色和声明都在那里。

最佳答案

因此,回顾一下,问题是为什么 ASP.NET Core Web 应用程序模板提供的代码在用户登录时不将角色或角色声明加载到 cookie 中。

经过多次谷歌搜索和实验后,似乎必须对模板化代码进行两项修改才能使角色和角色声明发挥作用:

首先,您必须在 Startup.cs 中添加以下代码行以启用 RoleManager。 (OP 中提到了这一点魔法。)

services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();

但是等等,还有更多!根据this discussion on GitHub ,让角色和声明显示在 cookie 中需要恢复到 service.AddIdentity 初始化代码,或者坚持使用 service.AddDefaultIdentity并将此行代码添加到 ConfigureServices:

// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();

如果您阅读上面引用的讨论,您会发现角色和角色声明显然已被弃用,或者至少没有得到热切支持。就我个人而言,我发现将声明分配给角色,将角色分配给用户,然后根据声明(根据用户的角色授予用户)做出授权决策非常有用。这为我提供了一种简单的声明性方式,例如允许多个角色访问一个函数(即包含用于启用该函数的声明的所有角色)。

但是您确实需要注意身份验证 cookie 中携带的角色和声明数据的数量。更多数据意味着每个请求发送到服务器的字节数更多,而且我不知道当您遇到 cookie 大小的某种限制时会发生什么。

关于asp.net-core - ASP.NET Core Identity 2 : User. IsInRole 始终返回 false,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53271496/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com