ASP.NET Core身份2:User.IsInRole始终返回false [英] ASP.NET Core Identity 2: User.IsInRole always returns false

查看:66
本文介绍了ASP.NET Core身份2:User.IsInRole始终返回false的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:我打电话给RoleManager.CreateAsync()RoleManager.AddClaimAsync()来创建角色和相关的角色声明.然后,我呼叫UserManager.AddToRoleAsync()将用户添加到那些角色.但是,当用户登录时,角色和关联的声明都不会显示在ClaimsPrincipal中(即Controller的User对象).结果是User.IsInRole()总是返回false,并且User.Claims返回的Claims集合不包含角色声明,并且[Authorize(policy: xxx)]注释不起作用.

The question: I call RoleManager.CreateAsync() and RoleManager.AddClaimAsync() to create roles and associated role claims. Then I call UserManager.AddToRoleAsync() to add users to those roles. But when the user logs in, neither the roles nor the associated claims show up in the ClaimsPrincipal (i.e. the Controller's User object). The upshot of this is that User.IsInRole() always returns false, and the collection of Claims returned by User.Claims doesn't contain the role claims, and the [Authorize(policy: xxx)] annotations don't work.

我还应该补充一点,一种解决方案是将使用新的services.AddDefaultIdentity()(由模板代码提供)恢复为调用services.AddIdentity().AddSomething().AddSomethingElse().我不想去那里,因为我在网上看到太多相互矛盾的故事,这些故事说明我需要为各种用例配置AddIdentity时需要做什么. AddDefaultIdentity似乎可以正确地执行大多数操作,而无需增加很多流利的配置.

I should also add that one solution is to revert from using the new services.AddDefaultIdentity() (which is provided by the templated code) back to calling services.AddIdentity().AddSomething().AddSomethingElse(). I don't want to go there, because I've seen too many conflicting stories online about what I need to do to configure AddIdentity for various use cases. AddDefaultIdentity seems to do most things correctly without a lot of added fluent configuration.

顺便说一句,我问这个问题的目的是要回答它……除非别人给我比我准备发布的答案更好的答案.我也在问这个问题,因为经过几周的搜索,我还没有找到一个很好的端到端示例,该示例在ASP.NET Core Identity 2中创建和使用角色和声明.希望这个问题中的代码示例可以对偶然发现它的人有所帮助...

BTW, I'm asking this question with the intention of answering it... unless someone else gives me a better answer than the one I'm prepared to post. I'm also asking this question because after several weeks of searching I have yet to find a good end-to-end example of creating and using Roles and Claims in ASP.NET Core Identity 2. Hopefully, the code example in this question might help someone else who stumbles upon it...

设置: 我创建了一个新的ASP.NET Core Web应用程序,选择Web应用程序(模型-视图-控制器"),然后将身份验证"更改为单个用户帐户".在结果项目中,我执行以下操作:

The setup: I created a new ASP.NET Core Web Application, select Web Application (Model-View-Controller), and change the Authentication to Individual User Accounts. In the resultant project, I do the following:

  • 在Package Manager控制台中,更新数据库以匹配支架式迁移:

  • In Package Manager Console, update the database to match the scaffolded migration:

更新数据库

update-database

  • 添加扩展IdentityUserApplicationUser类.这涉及添加类,在ApplicationDbContext中添加一行代码,并在项目中的每个地方都将<IdentityUser>的每个实例替换为<ApplicationUser>.

  • Add an ApplicationUser class that extends IdentityUser. This involves adding the class, adding a line of code to the ApplicationDbContext and replacing every instance of <IdentityUser> with <ApplicationUser> everywhere in the project.

    新的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; }
    }
    

  • 在Package Manager控制台中,创建一个新的迁移并更新数据库以合并ApplicationUsers实体.

    添加迁移m_001
    更新数据库

    add-migration m_001
    update-database

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

  • Add the following line of code in Startup.cs to enable RoleManager

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

  • 向种子角色,声明和用户添加一些代码.此示例代码的基本概念是我有两个主张:can_report允许持有人创建报告,而can_test允许持有人运行测试.我有两个角色,AdminTester. Tester角色可以运行测试,但不能创建报告. Admin角色可以同时执行.因此,我将声明添加到角色中,并创建一个Admin测试用户和一个Tester测试用户.

  • Add some code to seed roles, claims, and users. The basic concept for this sample code is that I have two claims: can_report allows the holder to create reports, and can_test allows the holder to run tests. I have two Roles, Admin and Tester. The Tester role can run tests, but can't create reports. The Admin role can do both. So, I add the claims to the roles, and create one Admin test user and one Tester test user.

    首先,我添加一个类,该类的唯一目的是包含此示例中其他地方使用的常量:

    First, I add a class whose sole purpose in life is to contain constants used elsewhere in this example:

    // 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方法,但是那是额外的六行代码...

    Next, I seed my roles, claims, and users. I put this code in the main landing page controller just for expedience; it really belongs in the "startup" Configure method, but that's an extra half-dozen lines of code...

    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 });
        }
    }
    

    ,在调用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对象中不存在我的角色和声明.但是我可以查看数据库,并查看所有角色和声明.

    Whew. Now, (assuming I've noted all of the applicable code I've added to the project, above), when I run the app, I notice that neither of my "built-in" test users can access either the /home/Test or /home/Report page. Moreover, if I set a breakpoint in the Index method, I see that my roles and claims do not exist in the User object. But I can look at the database and see all of the roles and claims are there.

    推荐答案

    因此,回顾一下,该问题询问为什么ASP.NET Core Web应用程序模板提供的代码在以下情况下不会将角色或角色声明加载到Cookie中:用户登录.

    So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.

    经过大量的Google搜索和实验之后,似乎必须对模板化代码进行两项修改才能使Roles和Role Claims起作用:

    After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:

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

    First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)

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

    但是,等等,还有更多!根据在GitHub上进行的讨论,该角色和要求显示出来了Cookie中的 要么恢复为service.AddIdentity初始化代码,要么坚持使用service.AddDefaultIdentity并将这行代码添加到ConfigureServices:

    But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity initialization code, or sticking with service.AddDefaultIdentity and adding this line of code to 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>>();
    

    如果您阅读了上面提到的讨论,您会发现Roles和Role Claims显然已被弃用,或者至少不被急切支持.就个人而言,我发现将权利分配给角色,将角色分配给用户,然后根据声明(根据用户的角色授予用户)做出授权决策确实很有用.这为我提供了一种简单的声明式方法,例如,允许多个角色(即,包含用于启用该功能的声明的所有角色)访问一个功能.

    If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).

    但是您确实要注意角色的数量,并声明auth cookie中携带的数据.更多的数据意味着每个请求将更多的字节发送给服务器,而且我不知道当您遇到某种Cookie大小限制时会发生什么情况.

    But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.

    这篇关于ASP.NET Core身份2:User.IsInRole始终返回false的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆