如何使用 Identity ASP.NET Core 通过代码优先迁移为用户和角色播种

How to Seed Users and Roles with Code First Migration using Identity ASP.NET Core

我创建了一个干净的新 asp.net 5 项目 (rc1-final)。使用身份验证,我只有 ApplicationDbContext.cs 和以下代码:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        // On event model creating
        base.OnModelCreating(builder);
    }
}

请注意 ApplicationDbContext 使用 IdentityDbContext 而不是 DbContext。

有任何IdentityConfig.cs。如果经典的 protected override void Seed 不存在,我需要在哪里创建角色和用户?

这是not yet implemented。作为解决方法,只需编写您自己的 class 来检查数据库中是否存在您的实体,如果它们不存在则添加它们,然后从您的 Startup.cs 中调用此 class .

我的方法是在模型命名空间中创建一个 class。

public class SampleData
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        var context = serviceProvider.GetService<ApplicationDbContext>();

        string[] roles = new string[] { "Owner", "Administrator", "Manager", "Editor", "Buyer", "Business", "Seller", "Subscriber" };

        foreach (string role in roles)
        {
            var roleStore = new RoleStore<IdentityRole>(context);

            if (!context.Roles.Any(r => r.Name == role))
            {
                roleStore.CreateAsync(new IdentityRole(role));
            }
        }


        var user = new ApplicationUser
        {
            FirstName = "XXXX",
            LastName = "XXXX",
            Email = "xxxx@example.com",
            NormalizedEmail = "XXXX@EXAMPLE.COM",
            UserName = "Owner",
            NormalizedUserName = "OWNER",
            PhoneNumber = "+111111111111",
            EmailConfirmed = true,
            PhoneNumberConfirmed = true,
            SecurityStamp = Guid.NewGuid().ToString("D")
        };


        if (!context.Users.Any(u => u.UserName == user.UserName))
        {
            var password = new PasswordHasher<ApplicationUser>();
            var hashed = password.HashPassword(user,"secret");
            user.PasswordHash = hashed;

            var userStore = new UserStore<ApplicationUser>(context);
            var result = userStore.CreateAsync(user);

        }

        AssignRoles(serviceProvider, user.Email, roles);

        context.SaveChangesAsync();
    }

    public static async Task<IdentityResult> AssignRoles(IServiceProvider services, string email, string[] roles)
    {
        UserManager<ApplicationUser> _userManager = services.GetService<UserManager<ApplicationUser>>();
        ApplicationUser user = await _userManager.FindByEmailAsync(email);
        var result = await _userManager.AddToRolesAsync(user, roles);

        return result;
    }

}

到 运行 启动时的代码。在路由配置之后的配置方法末尾的 Startup.cs 中,按照 Stafford Williams 之前所说的添加以下代码。

SampleData.Initialize(app.ApplicationServices);

以下行在 AspNetRoles table 中创建条目,但不填充 NormalizedName 列。

用以下内容替换要填充的此列:

RoleManager<IdentityRole> roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
roleManager.CreateAsync(new IdentityRole(role));

截至撰写本文时,还没有用于植入数据库的插件,但您可以创建一个 class 并将其添加到您的容器中,以便在应用程序启动时执行相同的操作,此处我是这样做的,首先创建一个 class:

public class YourDbContextSeedData
{
    private YourDbContext _context;

    public YourDbContextSeedData(YourDbContext context)
    {
        _context = context;
    }

    public async void SeedAdminUser()
    {
        var user = new ApplicationUser
        {
            UserName = "Email@email.com",
            NormalizedUserName = "email@email.com",
            Email = "Email@email.com",
            NormalizedEmail = "email@email.com",
            EmailConfirmed = true,
            LockoutEnabled = false,
            SecurityStamp = Guid.NewGuid().ToString()
        };

        var roleStore = new RoleStore<IdentityRole>(_context);

        if (!_context.Roles.Any(r => r.Name == "admin"))
        {
            await roleStore.CreateAsync(new IdentityRole { Name = "admin", NormalizedName = "admin" });
        }

        if (!_context.Users.Any(u => u.UserName == user.UserName))
        {
            var password = new PasswordHasher<ApplicationUser>();
            var hashed = password.HashPassword(user, "password");
            user.PasswordHash = hashed;
            var userStore = new UserStore<ApplicationUser>(_context);
            await userStore.CreateAsync(user);
            await userStore.AddToRoleAsync(user, "admin");
        }

        await _context.SaveChangesAsync();
    }

Startup.csConfigureServices 方法中注册类型 class:

services.AddTransient<YourDbContextSeedData>();

接下来将 YourDbContextSeedData class 传递给 Startup.cs class 的 Configure 方法并使用它:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, YourDbContextSeedData seeder)
{
  seeder.SeedAdminUser();
}

在模型命名空间中添加以下内容 class。它适用于添加多个用户和角色,并且还将为现有用户添加角色(例如 facbook 登录)。从 startup.cs

中这样称呼它 app.SeedUsersAndRoles();
    using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;

namespace MyApplication.Models
{
    public static class DataSeeder
    {
        public static async void SeedUsersAndRoles(this IApplicationBuilder app)
        {
            var context = app.ApplicationServices.GetService<ApplicationDbContext>();
            UserWithRoles[] usersWithRoles = {
                new UserWithRoles("Admin", new string[] { "Administrator" , "Distributor" },"somepassword"),//user and optional roles and password you want to seed 
                new UserWithRoles("PlainUser"),
                new UserWithRoles("Jojo",new string[]{"Distributor" }) //seed roles to existing users (e.g. facebook login).
            };

            foreach (var userWithRoles in usersWithRoles)
            {
                foreach (string role in userWithRoles.Roles)
                    if (!context.Roles.Any(r => r.Name == role))
                    {
                        var roleStore = new RoleStore<IdentityRole>(context);
                        await roleStore.CreateAsync(new IdentityRole(role));
                    }
                var ExistingUser = context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName);
                if (ExistingUser == null) //the following syntax: !context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName)) 
                                            //provokes execption:(ExecuteReader requires an open and available Connection.) 
                    await new UserStore<ApplicationUser>(context).CreateAsync(userWithRoles.User);
                await app.AssignRoles(userWithRoles); //assign also to existing users.
            }

            context.SaveChangesAsync();
        }

        public static async Task<IdentityResult> AssignRoles(this IApplicationBuilder app, UserWithRoles uWR)
        {
            UserManager<ApplicationUser> _userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
            ApplicationUser user = await _userManager.FindByNameAsync(uWR.User.NormalizedUserName);
            var result = await _userManager.AddToRolesAsync(user, uWR.Roles);
            return result;
        }
    }
    public class UserWithRoles
    {
        private ApplicationUser user;
        public ApplicationUser User { get { return user; } }
        public string[] Roles { get; set; }
        public UserWithRoles(string name, string[] roles = null, string password = "secret")
        {
            if (roles != null)
                Roles = roles;
            else
                Roles = new string[] { };
            user = new ApplicationUser
            {
                Email = name + "@gmail.com", NormalizedEmail = name.ToUpper() + "@GMAIL.COM",
                UserName = name, NormalizedUserName = name.ToUpper(),
                PhoneNumber = "+1312341234",
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                SecurityStamp = Guid.NewGuid().ToString("D"),
            };
            user.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(user, password);
        }
    }
}

所以这是基于 Muhammad Abdullah 回答的解决方案。包括一些代码改进、提高代码的可读性并让它与 .net core 2 一起工作。

 public class Seed
    {
        public static async Task Initialize(IServiceProvider serviceProvider, IConfiguration configuration)
        {
            var usrName = configuration.GetSection("Admin").GetSection("UserName").Value;
            var email = configuration.GetSection("Admin").GetSection("Email").Value;
            var pass = configuration.GetSection("Admin").GetSection("Pass").Value;
            var roles = new string[4] { OWNER, ADMIN, SENIOR, USER };

            if(await CreateUser(serviceProvider, email, usrName, pass, roles))
            {
                await AddToRoles(serviceProvider, email, roles);
            }
        }

        private static async Task<bool> CreateUser(IServiceProvider serviceProvider, string email, string usrName, string pass, string[] roles)
        {
            var res = false;

            using (var scope = serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetService<BaseContext>();

                if (!context.ApplicationUsers.Any(u => u.NormalizedUserName == usrName.ToUpper()))
                {
                    var roleStore = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();

                    foreach (string role in roles)
                    {
                        if (!context.Roles.Any(r => r.Name == role))
                        {
                            await roleStore.CreateAsync(new IdentityRole(role)).ConfigureAwait(false);
                        }
                    }

                    var user = new ApplicationUser
                    {
                        UserName = usrName,
                        Email = email,
                        EmailConfirmed = true,
                        NormalizedEmail = email.ToUpper(),
                        NormalizedUserName = usrName.ToUpper(),
                        PhoneNumber = null,
                        PhoneNumberConfirmed = true,
                        SecurityStamp = Guid.NewGuid().ToString()
                    };

                    var password = new PasswordHasher<ApplicationUser>();
                    user.PasswordHash = password.HashPassword(user, pass); ;

                    var userStore = new UserStore<ApplicationUser>(context);
                    res = (await userStore.CreateAsync(user).ConfigureAwait(false)).Succeeded;
                }

                return res;
            }
        }

        private static async Task AddToRoles(IServiceProvider serviceProvider, string email, string[] roles)
        {
            using (var scope = serviceProvider.CreateScope())
            {
                var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
                var usr = await userManager.FindByEmailAsync(email).ConfigureAwait(false);
                await userManager.AddToRolesAsync(usr, roles).ConfigureAwait(false);
            }           
        }
    }

如果您遇到异步问题,请尝试以下代码:

    protected override void Seed(ApplicationDbContext context)
    {
        //  This method will be called after migrating to the latest version.

        string[] roles = new string[] { "Admin", "User" };
        foreach (string role in roles)
        {
            if (!context.Roles.Any(r => r.Name == role))
            {
                context.Roles.Add(new IdentityRole(role));
            }
        }

        //create user UserName:Owner Role:Admin
        if (!context.Users.Any(u => u.UserName == "Owner"))
        {
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
            var user = new ApplicationUser
            {
                FirstName = "XXXX",
                LastName = "XXXX",
                Email = "xxxx@example.com",
                UserName = "Owner",
                PhoneNumber = "+111111111111",
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                SecurityStamp = Guid.NewGuid().ToString("D"),
                PasswordHash = userManager.PasswordHasher.HashPassword("secret"),
                LockoutEnabled = true,
            };
            userManager.Create(user);
            userManager.AddToRole(user.Id, "Admin");
        }            

        context.SaveChanges();
    }

似乎这个线程很旧,但它仍然适用于想要在 entityframework 核心中播种身份表数据的人。

您可以简单地尝试以下操作。

modelBuilder.Entity<IdentityUser>().HasData(
               new IdentityUser { Id= "-1", UserName="sagark",PasswordHash="sagark", Email="emailid goes here" }
               );

您可以在 IdentityDbContext.cs 文件内的 OnModelCreating() 方法中播种用户和角色,如下所示。请注意,必须预定义密钥以避免每次执行此方法时都会播种新用户和角色。

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //Seeding a  'Administrator' role to AspNetRoles table
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole {Id = "2c5e174e-3b0e-446f-86af-483d56fd7210", Name = "Administrator", NormalizedName = "ADMINISTRATOR".ToUpper() });


        //a hasher to hash the password before seeding the user to the db
        var hasher = new PasswordHasher<IdentityUser>();


        //Seeding the User to AspNetUsers table
        modelBuilder.Entity<IdentityUser>().HasData(
            new IdentityUser
            {
                Id = "8e445865-a24d-4543-a6c6-9443d048cdb9", // primary key
                UserName = "myuser",
                NormalizedUserName = "MYUSER",
                PasswordHash = hasher.HashPassword(null, "Pa$$w0rd")
            }
        );


        //Seeding the relation between our user and role to AspNetUserRoles table
        modelBuilder.Entity<IdentityUserRole<string>>().HasData(
            new IdentityUserRole<string>
            {
                RoleId = "2c5e174e-3b0e-446f-86af-483d56fd7210", 
                UserId = "8e445865-a24d-4543-a6c6-9443d048cdb9"
            }
        );
        

    }

aspnetcore中有IHostedService的概念。这使得 运行 异步背景成为可能 Task.

@hamid-mosalla 的解决方案可以 async 并从 IHostedService 实现中调用。

种子 class 实现可能类似于

public class IdentityDataSeeder
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;

    public IdentityDataSeeder(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }

    public async Task SeedAsync()
    {
        var superAdminRole = new IdentityRole
        {
            Id = "cac43a6e-f7bb-4448-baaf-1add431ccbbf",
            Name = "SuperAdmin",
            NormalizedName = "SUPERADMIN"
        };
        await CreateRoleAsync(superAdminRole);

        var superAdminUserPassword = "P@ssword1";
        var superAdminUser = new ApplicationUser
        {
            Id = "b8633e2d-a33b-45e6-8329-1958b3252bbd",
            UserName = "admin@example.nl",
            NormalizedUserName = "ADMIN@EXAMPLE.NL",
            Email = "admin@example.nl",
            NormalizedEmail = "ADMIN@EXAMPLE.NL",
            EmailConfirmed = true,
        };
        await CreateUserAsync(superAdminUser, superAdminUserPassword);

        var superAdminInRole = await _userManager.IsInRoleAsync(superAdminUser, superAdminRole.Name);
        if (!superAdminInRole)
            await _userManager.AddToRoleAsync(superAdminUser, superAdminRole.Name);
    }

    private async Task CreateRoleAsync(IdentityRole role)
    {
        var exits = await _roleManager.RoleExistsAsync(role.Name);
        if (!exits)
            await _roleManager.CreateAsync(role);
    }

    private async Task CreateUserAsync(ApplicationUser user, string password)
    {
        var exists = await _userManager.FindByEmailAsync(user.Email);
        if (exists == null)
            await _userManager.CreateAsync(user, password);
    }
}

这可以从 IHostedService:

public class SetupIdentityDataSeeder : IHostedService
{
    private readonly IServiceProvider _serviceProvider;
    public SetupIdentityDataSeeder(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            var seeder = scope.ServiceProvider.GetRequiredService<IdentityDataSeeder>();

            await seeder.SeedAsync();
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

Startup 看起来像:

public void ConfigureServices(IServiceCollection services)
{
    //...

    services.AddHostedService<SetupIdentityDataSeeder>();
}

我的方式:

  1. 在模型文件夹中创建 Class

    public static class ModelBuilderExtensions
      {
    
         public static void Seed(this ModelBuilder builder)
         {
    
        // Seed Roles
    
        List<IdentityRole> roles = new List<IdentityRole>()
        {
            new IdentityRole { Name = "Admin", NormalizedName = "ADMIN" },
            new IdentityRole { Name = "User", NormalizedName = "USER" }
        };
    
        builder.Entity<IdentityRole>().HasData(roles);
    
        // -----------------------------------------------------------------------------
    
        // Seed Users
    
        var passwordHasher = new PasswordHasher<ApplicationUser>();
    
        List<ApplicationUser> users = new List<ApplicationUser>()
        {
             // imporant: don't forget NormalizedUserName, NormalizedEmail 
                     new ApplicationUser {
                        UserName = "user2@hotmail.com",
                        NormalizedUserName = "USER2@HOTMAIL.COM",
                        Email = "user2@hotmail.com",
                        NormalizedEmail = "USER2@HOTMAIL.COM",
                    },
    
                    new ApplicationUser {
                        UserName = "user3@hotmail.com",
                        NormalizedUserName = "USER3@HOTMAIL.COM",
                        Email = "user3@hotmail.com",
                        NormalizedEmail = "USER3@HOTMAIL.COM",
                    },
        };
    
    
        builder.Entity<ApplicationUser>().HasData(users);
    
        ///----------------------------------------------------
    
        // Seed UserRoles
    
    
        List<IdentityUserRole<string>> userRoles = new List<IdentityUserRole<string>>();
    
          // Add Password For All Users
    
            users[0].PasswordHash = passwordHasher.HashPassword(users[0], "User.123");
            users[1].PasswordHash = passwordHasher.HashPassword(users[1], "User.155");
    
             userRoles.Add(new IdentityUserRole<string> { UserId = users[0].Id, RoleId = 
             roles.First(q => q.Name == "User").Id });
    
             userRoles.Add(new IdentityUserRole<string> { UserId = users[1].Id, RoleId = 
             roles.First(q => q.Name == "Admin").Id });
    
    
        builder.Entity<IdentityUserRole<string>>().HasData(userRoles);
    
    }}
    
  2. 在 DBContext

    public class AppDbContext : IdentityDbContext<ApplicationUser>
     {
    
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }
    
    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Use seed method here
        builder.Seed();
        base.OnModelCreating(builder);
    }}