Usuarios y roles de semilla de MVC 5

95

He estado jugando con el nuevo MVC 5, tengo algunos modelos, controladores y configuraciones de vistas usando las primeras migraciones de código.

Mi pregunta es ¿cómo puedo sembrar usuarios y roles? Actualmente siembro algunos datos de referencia en mi método Seed en Configuration.cs. Pero me parece que las tablas de usuarios y roles no se crean hasta que algo llega al AccountController.

Actualmente tengo dos cadenas de conexión para poder separar mis datos de mi autenticación en diferentes bases de datos.

¿Cómo puedo hacer que las tablas de usuarios, roles, etc. se llenen junto con las otras? ¿Y no cuando se golpea el controlador de cuenta?

MrBeanzy
fuente

Respuestas:

182

A continuación se muestra un ejemplo del enfoque de semillas habitual:

protected override void Seed(SecurityModule.DataContexts.IdentityDb context)
{
    if (!context.Roles.Any(r => r.Name == "AppAdmin"))
    {
        var store = new RoleStore<IdentityRole>(context);
        var manager = new RoleManager<IdentityRole>(store);
        var role = new IdentityRole { Name = "AppAdmin" };

        manager.Create(role);
    }

    if (!context.Users.Any(u => u.UserName == "founder"))
    {
        var store = new UserStore<ApplicationUser>(context);
        var manager = new UserManager<ApplicationUser>(store);
        var user = new ApplicationUser {UserName = "founder"};

        manager.Create(user, "ChangeItAsap!");
        manager.AddToRole(user.Id, "AppAdmin");
    }
}

Usé el administrador de paquetes "update-database". DB y todas las tablas fueron creadas y sembradas con datos.

Valin
fuente
3
Al método Seed de la clase Configuración. La configuración es el nombre de clase predeterminado para enable-migrations, pero puede cambiarlo.
Valin
3
Debe usar 'enable-migrations' en la consola del administrador de paquetes. Creará una clase de configuración con el método semilla para usted.
Valin
4
@Zapnologica Migrations es muy fácil de usar. También le permite editar sus tablas sin volver a crearlas. Solo hay tres comandos con los que debe familiarizarse con el uso de NuGet Package Manager Console. Habilitar migraciones, agregar migración y actualizar la base de datos. Tranquilo.
yardpenalty.com
10
Literalmente copié y pegué este código en mi método Seed en una nueva aplicación web mvc 5, y luego ejecuté "update-database" en la consola del administrador de paquetes. Agrega el rol (puedo verlo en la tabla AspNetRoles), pero cuando se trata de la línea manager.AddToRole (user.Id, "AppAdmin") aparece el mensaje de error "UserId no encontrado". Si tiene alguna idea de lo que me estoy perdiendo, agradecería mucho la información.
Tom Regan
2
Falta context.Users.Add(user);entre manager.Create(user, "ChangeItAsap!");y manager.AddToRole(user.Id, "AppAdmin");. Entonces, el usuario recién nacido no tiene User.Id.
ApceH Hypocrite
15

Es una pequeña adición, pero para cualquiera que tenga el "UserId no encontrado". mensaje al intentar sembrar: (Tom Regan tenía esta pregunta en los comentarios, y yo me quedé atrapado en ella por un tiempo)

Esto significa que manager.Create (usuario, "ChangeItAsap!") No tuvo éxito. Esto podría tener una razón diferente, pero para mí fue porque mi contraseña no estaba teniendo éxito en su validación.

Tenía un validador de contraseña personalizado, que no se llamaba al sembrar la base de datos, por lo que las reglas de validación a las que estaba acostumbrado (longitud mínima 4 en lugar de 6 por defecto) no se aplicaron. Asegúrese de que su contraseña (y todos los demás campos para el caso) esté pasando la validación.

Kevin
fuente
7
Esto me ayudó porque estaba teniendo el problema "UserId no encontrado". Logré rastrearlo con el siguiente código: IdentityResult result = manager.Create(user, "ChangeItAsap!"); if (result.Succeeded == false) { throw new Exception(result.Errors.First()); }
Steve Wilford
Ese comentario es excelente, me dio 'El nombre de usuario Demo User no es válido, solo puede contener letras o dígitos'. en lugar de simplemente fallar ambiguamente con un ID de usuario faltante
dougajmcdonald
Encontré que mi regla de validación de contraseña tampoco funciona, ¿alguna idea?
user1686407
15

Esta es mi base de método en la respuesta de Valin, he agregado roles en db y agregué contraseña para el usuario. Este código se coloca en Seed()método en Migraciones> Configuraciones.cs.

// role (Const.getRoles() return string[] whit all roles)

    var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
    for (int i = 0; i < Const.getRoles().Length; i++)
    {
        if (RoleManager.RoleExists(Const.getRoles()[i]) == false)
        {
            RoleManager.Create(new IdentityRole(Const.getRoles()[i]));
        }
    }

// user

    var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
    var PasswordHash = new PasswordHasher();
    if (!context.Users.Any(u => u.UserName == "[email protected]"))
    {
        var user = new ApplicationUser
        {
             UserName = "[email protected]",
             Email = "[email protected]",
             PasswordHash = PasswordHash.HashPassword("123456")
         };

         UserManager.Create(user);
         UserManager.AddToRole(user.Id, Const.getRoles()[0]);
    }
Vasil Valchev
fuente
6

Aquí tengo una solución muy fácil, limpia y suave.

 protected override void Seed(UserContext context)
    { 
        //Step 1 Create the user.
        var passwordHasher = new PasswordHasher();
        var user = new IdentityUser("Administrator");
        user.PasswordHash = passwordHasher.HashPassword("Admin12345");
        user.SecurityStamp = Guid.NewGuid().ToString();

        //Step 2 Create and add the new Role.
        var roleToChoose = new IdentityRole("Admin");
        context.Roles.Add(roleToChoose);

        //Step 3 Create a role for a user
        var role = new IdentityUserRole();
        role.RoleId = roleToChoose.Id;
        role.UserId = user.Id;

         //Step 4 Add the role row and add the user to DB)
        user.Roles.Add(role);
        context.Users.Add(user);
    }
Sr. Tangjai
fuente
1
Algo genial, pero te perdiste algo importante. Debe agregar user.SecurityStamp = Guid.NewGuid (). ToString () o obtendrá un error al iniciar sesión.
eddy white
Gracias. No usé esa función, pero la agregué a mi respuesta.
Mr Tangjai
4
protected override void Seed(ApplicationDbContext context)
{
  SeedAsync(context).GetAwaiter().GetResult();
}

private async Task SeedAsync(ApplicationDbContext context)
{
  var userManager = new ApplicationUserManager(new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context));
  var roleManager = new ApplicationRoleManager(new RoleStore<ApplicationRole, int, ApplicationUserRole>(context));

  if (!roleManager.Roles.Any())
  {
    await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AdminRoleName });
    await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AffiliateRoleName });
  }

  if (!userManager.Users.Any(u => u.UserName == "shimmy"))
  {
    var user = new ApplicationUser
    {
      UserName = "shimmy",
      Email = "[email protected]",
      EmailConfirmed = true,
      PhoneNumber = "0123456789",
      PhoneNumberConfirmed = true
    };

    await userManager.CreateAsync(user, "****");
    await userManager.AddToRoleAsync(user.Id, ApplicationRole.AdminRoleName);
  }
}
Shimmy Weitzhandler
fuente
1
Personalicé mi ApplicationUser para tener una propiedad de ID con tipo int. Su enfoque es el único que pude conseguir para trabajar con mi User y RoleStores personalizados, ¡gracias!
Mike Devenney
1
Este bit es completamente incorrecta desde un punto de vista conceptual: Task.Run(async () => { await SeedAsync(context); }).Wait();. Deberías escribir SeedAsync(context).GetAwait().GetResult();cuál es ligeramente mejor.
Tanveer Badar
2

Parece que cambiaron la forma en que funciona la autenticación en MVC5, cambiaron mi Global.asax.cs a lo siguiente, ¡hicieron el truco!

using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

using System.Threading.Tasks;
using MvcAuth.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.Threading;
using Microsoft.AspNet.Identity.EntityFramework;

namespace MvcAuth
{
    public class MvcApplication : System.Web.HttpApplication
    {
        async Task<bool> AddRoleAndUser()
        {
            AuthenticationIdentityManager IdentityManager = new AuthenticationIdentityManager(
                new IdentityStore(new ApplicationDbContext()));

            var role = new Role("Role1");
            IdentityResult result = await IdentityManager.Roles.CreateRoleAsync(role, CancellationToken.None);
            if (result.Success == false)
                return false;

            var user = new ApplicationUser() { UserName = "user1" };
            result = await IdentityManager.Users.CreateLocalUserAsync(user, "Password1");
            if (result.Success == false)
                return false;

            result = await IdentityManager.Roles.AddUserToRoleAsync(user.Id, role.Id, CancellationToken.None);
            return result.Success;
        }

        protected async void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            bool x = await AddRoleAndUser();
        }
    }
}
MrBeanzy
fuente
9
Esta respuesta ya no es relevante ya que la API de identidad de ASP.NET ha cambiado.
Josh McKearin
@Josh McKearin ¿Tiene una mejor solución? por favor comparta
Victor.Uduak
2

escriba este código en su configuración de migración.

nota: Utilice ApplicationDbContext en la clase de configuración.

    internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = false;
    }

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

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data.
                   context.Roles.AddOrUpdate(p =>
            p.Id,
                new IdentityRole { Name = "Admins"},
                new IdentityRole { Name = "PowerUsers" },
                new IdentityRole { Name = "Users" },
                new IdentityRole { Name = "Anonymous" }
            );


    }
}
FatalMan
fuente