Usuários e funções iniciais do MVC 5

94

Tenho brincado com o novo MVC 5, tenho alguns modelos, controladores e configurações de visualizações usando migrações iniciais de código.

Minha pergunta é como eu semeio usuários e funções? Atualmente, eu semeio alguns dados de referência em meu método Seed em Configuration.cs. Mas parece-me que as tabelas de usuário e funções não são criadas até que algo acesse o AccountController.

Atualmente, tenho duas strings de conexão para que possa separar meus dados de minha autenticação em bancos de dados diferentes.

Como posso fazer com que as tabelas de usuário, funções, etc. sejam preenchidas junto com minhas outras? E não quando o controlador de conta é atingido?

MrBeanzy
fonte

Respostas:

181

Aqui está um exemplo de abordagem Seed usual:

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

Eu usei o gerenciador de pacotes "update-database". DB e todas as tabelas foram criadas e propagadas com dados.

Valin
fonte
3
Para o método Seed da classe Configuration. A configuração é o nome da classe padrão para enable-migrações, mas você pode alterá-lo.
Valin
3
Você deve usar 'enable-migrations' no console do gerenciador de pacotes. Ele irá criar uma classe de configuração com o método seed para você.
Valin
4
@Zapnologica Migrations é muito fácil de usar. Ele também permite que você edite suas tabelas sem recriá-las. Existem apenas três comandos com os quais você precisa se familiarizar com o uso do Console do gerenciador de pacotes NuGet. Enable-Migrations, Add-Migration, & update-database. Fácil demais.
yardpenalty.com
10
Eu literalmente copiei e colei esse código em meu método Seed em um novo aplicativo da web mvc 5 e, em seguida, executei "update-database" no console do gerenciador de pacotes. Ele adiciona a função (posso ver na tabela AspNetRoles), mas quando se trata do gerenciador de linha.AddToRole (user.Id, "AppAdmin"), recebo a mensagem de erro "UserId não encontrado." Se você tem alguma ideia do que estou perdendo, eu agradeceria muito a informação.
Tom Regan
2
Perdido context.Users.Add(user);entre manager.Create(user, "ChangeItAsap!");e manager.AddToRole(user.Id, "AppAdmin");. Portanto, o usuário recém-nascido não tem User.Id.
ApceH Hypocrite
15

É uma pequena adição, mas para qualquer um que tenha o "UserId não encontrado". mensagem ao tentar semear: (Tom Regan tinha essa pergunta nos comentários, e eu mesmo fiquei preso nela por um tempo)

Isso significa que o manager.Create (usuário, "ChangeItAsap!") Não foi bem-sucedido. Isso pode ter um motivo diferente, mas para mim era porque minha senha não estava sendo validada.

Eu tinha um validador de senha personalizado, que não estava sendo chamado durante a propagação do banco de dados, então as regras de validação com as quais eu estava acostumado (minlength 4 em vez do padrão 6) não se aplicavam. Certifique-se de que sua senha (e todos os outros campos para esse assunto) está passando na validação.

Kevin
fonte
7
Isso me ajudou porque eu estava tendo o problema de "UserId não encontrado". Consegui rastreá-lo com o seguinte código: IdentityResult result = manager.Create(user, "ChangeItAsap!"); if (result.Succeeded == false) { throw new Exception(result.Errors.First()); }
Steve Wilford
Esse comentário é excelente, me deu 'Nome de usuário de demonstração Usuário inválido, só pode conter letras ou dígitos.' em vez de apenas falhar ambiguamente com um userId ausente
dougajmcdonald
Descobri que minha regra de validação de senha também não funciona, alguma ideia?
user1686407
15

Este é o meu método baseado na resposta do Valin, adicionei funções no banco de dados e adicionei a senha do usuário. Este código é colocado no Seed()método em Migrations> Configurations.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
fonte
6

Aqui tenho uma solução muito fácil, limpa e 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
fonte
1
Coisa legal, mas vc perdeu uma coisa importante. Você tem que adicionar user.SecurityStamp = Guid.NewGuid (). ToString () ou você obterá um erro no login.
eddy white
THX. Não usei esse recurso, mas adicionei-o à minha resposta.
Sr. Tangjai
3
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
fonte
1
Eu personalizei meu ApplicationUser para ter uma propriedade de ID digitada int. Sua abordagem é a única que consegui trabalhar com meu usuário e RoleStores personalizados, obrigado!
Mike Devenney
1
Este bit é completamente incorreta de um ponto de vista conceitual: Task.Run(async () => { await SeedAsync(context); }).Wait();. Você deve antes escrever o SeedAsync(context).GetAwait().GetResult();que é um pouco melhor.
Tanveer Badar
2

Parece que eles mudaram a forma como a autenticação funciona no MVC5, mudei meu Global.asax.cs para o seguinte funcionou!

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
fonte
9
Essa resposta não é mais relevante, pois a API de identidade ASP.NET mudou.
Josh McKearin,
@Josh McKearin Você tem uma solução melhor? compartilhe
Victor.Uduak
2

escreva este código em sua configuração de migração.

nota: Use ApplicationDbContext na classe de configuração.

    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
fonte