Condividi tramite


Modifica della chiave primaria per gli utenti in ASP.NET Identity

di Tom FitzMacken

In Visual Studio 2013, l'applicazione Web predefinita usa un valore stringa per la chiave per gli account utente. ASP.NET Identity consente di modificare il tipo della chiave per soddisfare i requisiti dei dati. Ad esempio, è possibile modificare il tipo della chiave da una stringa a un intero.

Questo argomento illustra come iniziare con l'applicazione Web predefinita e modificare la chiave dell'account utente in un intero. È possibile usare le stesse modifiche per implementare qualsiasi tipo di chiave nel progetto. Illustra come apportare queste modifiche nell'applicazione Web predefinita, ma è possibile applicare modifiche simili a un'applicazione personalizzata. Mostra le modifiche necessarie quando si usa MVC o Web Forms.

Versioni software usate nell'esercitazione

  • Visual Studio 2013 con l'aggiornamento 2 (o versione successiva)
  • ASP.NET Identity 2.1 o versioni successive

Per eseguire la procedura descritta in questa esercitazione, è necessario avere Visual Studio 2013 Update 2 (o versione successiva) e un'applicazione Web creata dal modello di applicazione Web ASP.NET. Il modello è stato modificato nell'aggiornamento 3. Questo argomento illustra come modificare il modello nell'aggiornamento 2 e nell'aggiornamento 3.

In questo argomento sono incluse le sezioni seguenti:

Modificare il tipo della chiave nella classe utente Identity

Nel progetto creato dal modello di applicazione Web ASP.NET specificare che la classe ApplicationUser usa un intero per la chiave per gli account utente. In IdentityModels.cs modificare la classe ApplicationUser per ereditare da IdentityUser con un tipo di int per il parametro generico TKey. Si passano anche i nomi di tre classi personalizzate che non sono ancora state implementate.

public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, 
    CustomUserClaim> 
{ 
    ...

Per impostazione predefinita, è stato modificato il tipo della chiave, ma, per impostazione predefinita, il resto dell'applicazione presuppone che la chiave sia una stringa. È necessario indicare in modo esplicito il tipo della chiave nel codice che presuppone una stringa.

Nella classe ApplicationUser modificare il metodo GenerateUserIdentityAsync per includere int, come illustrato nel codice evidenziato di seguito. Questa modifica non è necessaria per i progetti Web Forms con il modello Update 3.

public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
    UserManager<ApplicationUser, int> manager) 
{ 
    // Note the authenticationType must match the one defined in
    // CookieAuthenticationOptions.AuthenticationType 
    var userIdentity = await manager.CreateIdentityAsync(
        this, DefaultAuthenticationTypes.ApplicationCookie); 
    // Add custom user claims here 
    return userIdentity; 
}

Aggiungere classi identity personalizzate che usano il tipo di chiave

Le altre classi Identity, ad esempio IdentityUserRole, IdentityUserClaim, IdentityUserLogin, IdentityRole, UserStore, RoleStore, sono ancora configurate per usare una chiave stringa. Creare nuove versioni di queste classi che specificano un intero per la chiave. Non è necessario fornire molto codice di implementazione in queste classi, è principalmente necessario impostare int come chiave.

Aggiungere le classi seguenti al file IdentityModels.cs.

public class CustomUserRole : IdentityUserRole<int> { } 
public class CustomUserClaim : IdentityUserClaim<int> { } 
public class CustomUserLogin : IdentityUserLogin<int> { } 

public class CustomRole : IdentityRole<int, CustomUserRole> 
{ 
    public CustomRole() { } 
    public CustomRole(string name) { Name = name; } 
} 

public class CustomUserStore : UserStore<ApplicationUser, CustomRole, int, 
    CustomUserLogin, CustomUserRole, CustomUserClaim> 
{ 
    public CustomUserStore(ApplicationDbContext context) 
        : base(context) 
    { 
    } 
} 

public class CustomRoleStore : RoleStore<CustomRole, int, CustomUserRole> 
{ 
    public CustomRoleStore(ApplicationDbContext context) 
        : base(context) 
    { 
    } 
}

Modificare la classe di contesto e gestione utenti per usare il tipo di chiave

In IdentityModels.cs modificare la definizione della classe ApplicationDbContext per usare le nuove classi personalizzate e un int per la chiave, come illustrato nel codice evidenziato.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, 
    int, CustomUserLogin, CustomUserRole, CustomUserClaim> 
{ 
    ...

Il parametro ThrowIfV1Schema non è più valido nel costruttore. Modificare il costruttore in modo che non passi un valore ThrowIfV1Schema.

public ApplicationDbContext() 
    : base("DefaultConnection") 
{ 
}

Aprire IdentityConfig.cs e modificare la classe ApplicationUserManger per usare la nuova classe dell'archivio utente per rendere persistenti i dati e un int per la chiave.

public class ApplicationUserManager : UserManager<ApplicationUser, int> 
{ 
    public ApplicationUserManager(IUserStore<ApplicationUser, int> store) 
        : base(store) 
    { 
    } 

    public static ApplicationUserManager Create(
        IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)  
    { 
        var manager = new ApplicationUserManager(
            new CustomUserStore(context.Get<ApplicationDbContext>())); 
        // Configure validation logic for usernames 
        manager.UserValidator = new UserValidator<ApplicationUser, int>(manager) 
        { 
            AllowOnlyAlphanumericUserNames = false, 
            RequireUniqueEmail = true 
        }; 
        // Configure validation logic for passwords 
        manager.PasswordValidator = new PasswordValidator 
        { 
            RequiredLength = 6, 
            RequireNonLetterOrDigit = true, 
            RequireDigit = true, 
            RequireLowercase = true, 
            RequireUppercase = true, 
        }; 
        // Register two factor authentication providers. This application uses Phone 
        // and Emails as a step of receiving a code for verifying the user 
        // You can write your own provider and plug in here. 
        manager.RegisterTwoFactorProvider("PhoneCode", 
            new PhoneNumberTokenProvider<ApplicationUser, int> 
        { 
            MessageFormat = "Your security code is: {0}" 
        }); 
        manager.RegisterTwoFactorProvider("EmailCode", 
            new EmailTokenProvider<ApplicationUser, int> 
        { 
            Subject = "Security Code", 
            BodyFormat = "Your security code is: {0}" 
        }); 
        manager.EmailService = new EmailService(); 
        manager.SmsService = new SmsService(); 
        var dataProtectionProvider = options.DataProtectionProvider; 
        if (dataProtectionProvider != null) 
        { 
            manager.UserTokenProvider = 
                new DataProtectorTokenProvider<ApplicationUser, int>(
                    dataProtectionProvider.Create("ASP.NET Identity")); 
        } 
        return manager; 
    } 
}

Nel modello Update 3 è necessario modificare la classe ApplicationSignInManager.

public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{ ... }

Modificare la configurazione di avvio per usare il tipo di chiave

In Startup.Auth.cs sostituire il codice OnValidateIdentity, come evidenziato di seguito. Si noti che la definizione getUserIdCallback analizza il valore stringa in un intero.

app.UseCookieAuthentication(new CookieAuthenticationOptions 
    { 
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
        LoginPath = new PathString("/Account/Login"), 
        Provider = new CookieAuthenticationProvider 
        { 
            OnValidateIdentity = SecurityStampValidator
                .OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>( 
                    validateInterval: TimeSpan.FromMinutes(30), 
                    regenerateIdentityCallback: (manager, user) => 
                        user.GenerateUserIdentityAsync(manager), 
                    getUserIdCallback:(id)=>(id.GetUserId<int>()))
        } 
    });

Se il progetto non riconosce l'implementazione generica del metodo GetUserId , potrebbe essere necessario aggiornare il pacchetto NuGet identità ASP.NET alla versione 2.1

Sono state apportate molte modifiche alle classi di infrastruttura usate da ASP.NET Identity. Se si prova a compilare il progetto, si noteranno molti errori. Fortunatamente, gli errori rimanenti sono tutti simili. La classe Identity prevede un numero intero per la chiave, ma il controller (o Web Form) passa un valore stringa. In ogni caso, è necessario convertire da una stringa a e integer chiamando GetUserId<int>. È possibile usare l'elenco degli errori dalla compilazione o seguire le modifiche riportate di seguito.

Le modifiche rimanenti dipendono dal tipo di progetto creato e dall'aggiornamento installato in Visual Studio. È possibile passare direttamente alla sezione pertinente tramite i collegamenti seguenti

Per MVC con l'aggiornamento 2, modificare AccountController per passare il tipo di chiave

Aprire il file AccountController.cs. È necessario modificare i metodi seguenti.

Metodo ConfirmEmail

public async Task<ActionResult> ConfirmEmail(int userId, string code) 
{ 
    if (userId == default(int) || code == null)  
    { 
        return View("Error"); 
    } 

    IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code); 
    if (result.Succeeded) 
    { 
        return View("ConfirmEmail"); 
    } 
    else 
    { 
        AddErrors(result); 
        return View(); 
    } 
}

Metodo Disassociate

public async Task<ActionResult> Disassociate(string loginProvider, string providerKey) 
{ 
    ManageMessageId? message = null; 
    IdentityResult result = await UserManager.RemoveLoginAsync(
        User.Identity.GetUserId<int>(), 
        new UserLoginInfo(loginProvider, providerKey)); 
    if (result.Succeeded) 
    { 
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>()); 
        await SignInAsync(user, isPersistent: false); 
        message = ManageMessageId.RemoveLoginSuccess; 
    } 
    else 
    { 
        message = ManageMessageId.Error; 
    } 
    return RedirectToAction("Manage", new { Message = message }); 
}

Metodo Manage(ManageUserViewModel)

public async Task<ActionResult> Manage(ManageUserViewModel model) 
{ 
    bool hasPassword = HasPassword(); 
    ViewBag.HasLocalPassword = hasPassword; 
    ViewBag.ReturnUrl = Url.Action("Manage"); 
    if (hasPassword) 
    { 
        if (ModelState.IsValid) 
        { 
            IdentityResult result = await UserManager.ChangePasswordAsync(
                User.Identity.GetUserId<int>(),
                model.OldPassword, 
                model.NewPassword); 
            if (result.Succeeded) 
            { 
                var user = await UserManager.FindByIdAsync(
                  User.Identity.GetUserId<int>()); 
                await SignInAsync(user, isPersistent: false); 
                return RedirectToAction("Manage", new { 
                    Message = ManageMessageId.ChangePasswordSuccess }); 
            } 
            else 
            { 
                AddErrors(result); 
            } 
        } 
    } 
    else 
    { 
        // User does not have a password so remove any validation errors caused 
        // by a missing OldPassword field 
        ModelState state = ModelState["OldPassword"]; 
        if (state != null) 
        { 
            state.Errors.Clear(); 
        } 

        if (ModelState.IsValid) 
        { 
            IdentityResult result = await UserManager.AddPasswordAsync(
                User.Identity.GetUserId<int>(), model.NewPassword); 
            if (result.Succeeded) 
            { 
                return RedirectToAction("Manage", new { 
                    Message = ManageMessageId.SetPasswordSuccess }); 
            } 
            else 
            { 
                AddErrors(result); 
            } 
        } 
    } 

    // If we got this far, something failed, redisplay form 
    return View(model); 
}

LinkLoginCallback , metodo

public async Task<ActionResult> LinkLoginCallback()
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, 
        User.Identity.GetUserId());
    if (loginInfo == null)
    {
        return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
    }
    IdentityResult result = await UserManager.AddLoginAsync(
        User.Identity.GetUserId<int>(), loginInfo.Login);
    if (result.Succeeded)
    {
        return RedirectToAction("Manage");
    }
    return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}

RemoveAccountList , metodo

public ActionResult RemoveAccountList()
{
    var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId<int>());
    ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
    return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}

Metodo HasPassword

private bool HasPassword()
{
    var user = UserManager.FindById(User.Identity.GetUserId<int>());
    if (user != null)
    {
        return user.PasswordHash != null;
    }
    return false;
}

È ora possibile eseguire l'applicazione e registrare un nuovo utente.

Per MVC con l'aggiornamento 3, modificare AccountController e ManageController per passare il tipo di chiave

Aprire il file AccountController.cs. È necessario modificare il metodo seguente.

Metodo ConfirmEmail

public async Task<ActionResult> ConfirmEmail(int userId, string code) 
{ 
    if (userId == default(int) || code == null)  
    { 
        return View("Error"); 
    } 

    IdentityResult result = await UserManager.ConfirmEmailAsync(userId, code); 
    return View(result.Succeeded ? "ConfirmEmail" : "Error");
}

Metodo SendCode

public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
{
    var userId = await SignInManager.GetVerifiedUserIdAsync();
    if (userId == default(int))
    {
        return View("Error");
    }
    var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
    var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
    return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
}

Aprire il file ManageController.cs. È necessario modificare i metodi seguenti.

Metodo Index

public async Task<ActionResult> Index(ManageMessageId? message)
{
    ViewBag.StatusMessage =
        message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
        : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
        : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
        : message == ManageMessageId.Error ? "An error has occurred."
        : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
        : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
        : "";

    var model = new IndexViewModel
    {
        HasPassword = HasPassword(),
        PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId<int>()),
        TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId<int>()),
        Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId<int>()),
        BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(
          User.Identity.GetUserId())
    };
    return View(model);
}

Metodi RemoveLogin

public ActionResult RemoveLogin()
{
    var linkedAccounts = UserManager.GetLogins((User.Identity.GetUserId<int>()));
    ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
    return View(linkedAccounts);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
{
    ManageMessageId? message;
    var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId<int>(), 
        new UserLoginInfo(loginProvider, providerKey));
    if (result.Succeeded)
    {
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        message = ManageMessageId.RemoveLoginSuccess;
    }
    else
    {
        message = ManageMessageId.Error;
    }
    return RedirectToAction("ManageLogins", new { Message = message });
}

AddPhoneNumber , metodo

public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    // Generate the token and send it
    var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
        User.Identity.GetUserId<int>(), model.Number);
    if (UserManager.SmsService != null)
    {
        var message = new IdentityMessage
        {
            Destination = model.Number,
            Body = "Your security code is: " + code
        };
        await UserManager.SmsService.SendAsync(message);
    }
    return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}

EnableTwoFactorAuthentication , metodo

public async Task<ActionResult> EnableTwoFactorAuthentication()
{
    await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId<int>(), true);
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
    if (user != null)
    {
        await SignInAsync(user, isPersistent: false);
    }
    return RedirectToAction("Index", "Manage");
}

DisableTwoFactorAuthentication , metodo

public async Task<ActionResult> DisableTwoFactorAuthentication()
{
    await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId<int>(), false);
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
    if (user != null)
    {
        await SignInAsync(user, isPersistent: false);
    }
    return RedirectToAction("Index", "Manage");
}

Metodi VerifyPhoneNumber

public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber)
{
    var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
        User.Identity.GetUserId<int>(), phoneNumber);
    // Send an SMS through the SMS provider to verify the phone number
    return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    var result = await UserManager.ChangePhoneNumberAsync(
        User.Identity.GetUserId<int>(), model.PhoneNumber, model.Code);
    if (result.Succeeded)
    {
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
    }
    // If we got this far, something failed, redisplay form
    ModelState.AddModelError("", "Failed to verify phone");
    return View(model);
}

RemovePhoneNumber , metodo

public async Task<ActionResult> RemovePhoneNumber()
{
    var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId<int>(), null);
    if (!result.Succeeded)
    {
        return RedirectToAction("Index", new { Message = ManageMessageId.Error });
    }
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
    if (user != null)
    {
        await SignInAsync(user, isPersistent: false);
    }
    return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
}

Metodo ChangePassword

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    var result = await UserManager.ChangePasswordAsync(
        User.Identity.GetUserId<int>(), model.OldPassword, model.NewPassword);
    if (result.Succeeded)
    {
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
    }
    AddErrors(result);
    return View(model);
}

Metodo SetPassword

public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId<int>(), model.NewPassword);
        if (result.Succeeded)
        {
            var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
            if (user != null)
            {
                await SignInAsync(user, isPersistent: false);
            }
            return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
        }
        AddErrors(result);
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

Metodo ManageLogins

public async Task<ActionResult> ManageLogins(ManageMessageId? message)
{
    ViewBag.StatusMessage =
        message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
        : message == ManageMessageId.Error ? "An error has occurred."
        : "";
    var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
    if (user == null)
    {
        return View("Error");
    }
    var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId<int>());
    var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
    ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
    return View(new ManageLoginsViewModel
    {
        CurrentLogins = userLogins,
        OtherLogins = otherLogins
    });
}

LinkLoginCallback , metodo

public async Task<ActionResult> LinkLoginCallback()
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
    if (loginInfo == null)
    {
        return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
    }
    var result = await UserManager.AddLoginAsync(User.Identity.GetUserId<int>(), 
        loginInfo.Login);
    return result.Succeeded ? RedirectToAction("ManageLogins") : 
        RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}

Metodo HasPassword

private bool HasPassword()
{
    var user = UserManager.FindById(User.Identity.GetUserId<int>());
    if (user != null)
    {
        return user.PasswordHash != null;
    }
    return false;
}

Metodo HasPhoneNumber

private bool HasPhoneNumber()
{
    var user = UserManager.FindById(User.Identity.GetUserId<int>());
    if (user != null)
    {
        return user.PhoneNumber != null;
    }
    return false;
}

È ora possibile eseguire l'applicazione e registrare un nuovo utente.

Per Web Forms con l'aggiornamento 2, modificare le pagine account per passare il tipo di chiave

Per Web Forms con l'aggiornamento 2, è necessario modificare le pagine seguenti.

Confirm.aspx.cx

protected void Page_Load(object sender, EventArgs e)
{
    string code = IdentityHelper.GetCodeFromRequest(Request);
    string userId = IdentityHelper.GetUserIdFromRequest(Request);
    if (code != null && userId != null)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = manager.ConfirmEmail(Int32.Parse(userId), code);
        if (result.Succeeded)
        {
            StatusMessage = "Thank you for confirming your account.";
            return;
        }
    }

    StatusMessage = "An error has occurred";
}

RegisterExternalLogin.aspx.cs

protected void Page_Load()
{
    // Process the result from an auth provider in the request
    ProviderName = IdentityHelper.GetProviderNameFromRequest(Request);
    if (String.IsNullOrEmpty(ProviderName))
    {
        RedirectOnFail();
        return;
    }
    if (!IsPostBack)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
        if (loginInfo == null)
        {
            RedirectOnFail();
            return;
        }
        var user = manager.Find(loginInfo.Login);
        if (user != null)
        {
            IdentityHelper.SignIn(manager, user, isPersistent: false);
            IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
        }
        else if (User.Identity.IsAuthenticated)
        {
            // Apply Xsrf check when linking
            var verifiedloginInfo = Context.GetOwinContext().Authentication
                .GetExternalLoginInfo(IdentityHelper.XsrfKey, User.Identity.GetUserId());
            if (verifiedloginInfo == null)
            {
                RedirectOnFail();
                return;
            }

            var result = manager.AddLogin(User.Identity.GetUserId<int>(), 
                verifiedloginInfo.Login);
            if (result.Succeeded)
            {
                IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], 
                    Response);
            }
            else
            {
                AddErrors(result);
                return;
            }
        }
        else
        {
            email.Text = loginInfo.Email;
        }
    }
}

Manage.aspx.cs

private bool HasPassword(ApplicationUserManager manager)
{
    return manager.HasPassword(User.Identity.GetUserId<int>());
}

protected void Page_Load()
{
    if (!IsPostBack)
    {
        // Determine the sections to render
         var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        if (HasPassword(manager))
        {
            changePasswordHolder.Visible = true;
        }
        else
        {
            setPassword.Visible = true;
            changePasswordHolder.Visible = false;
        }
        CanRemoveExternalLogins = manager.GetLogins(
            User.Identity.GetUserId<int>()).Count() > 1;

        // Render success message
        var message = Request.QueryString["m"];
        if (message != null)
        {
            // Strip the query string from action
            Form.Action = ResolveUrl("~/Account/Manage");

            SuccessMessage =
                message == "ChangePwdSuccess" ? "Your password has been changed."
                : message == "SetPwdSuccess" ? "Your password has been set."
                : message == "RemoveLoginSuccess" ? "The account was removed."
                : String.Empty;
            successMessage.Visible = !String.IsNullOrEmpty(SuccessMessage);
        }
    }
}

protected void ChangePassword_Click(object sender, EventArgs e)
{
    if (IsValid)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        IdentityResult result = manager.ChangePassword(
            User.Identity.GetUserId<int>(),
            CurrentPassword.Text, 
            NewPassword.Text);
        if (result.Succeeded)
        {
            var user = manager.FindById(User.Identity.GetUserId<int>());
            IdentityHelper.SignIn(manager, user, isPersistent: false);
            Response.Redirect("~/Account/Manage?m=ChangePwdSuccess");
        }
        else
        {
            AddErrors(result);
        }
    }
}

protected void SetPassword_Click(object sender, EventArgs e)
{
    if (IsValid)
    {
        // Create the local login info and link the local account to the user
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        IdentityResult result = manager.AddPassword(User.Identity.GetUserId<int>(), 
            password.Text);
        if (result.Succeeded)
        {
            Response.Redirect("~/Account/Manage?m=SetPwdSuccess");
        }
        else
        {
            AddErrors(result);
        }
    }
}

public IEnumerable<UserLoginInfo> GetLogins()
{
    var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
    var accounts = manager.GetLogins(User.Identity.GetUserId<int>());
    CanRemoveExternalLogins = accounts.Count() > 1 || HasPassword(manager);
    return accounts;
}

public void RemoveLogin(string loginProvider, string providerKey)
{
    var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
    var result = manager.RemoveLogin(User.Identity.GetUserId<int>(), 
        new UserLoginInfo(loginProvider, providerKey));
    string msg = String.Empty;
    if (result.Succeeded)
    {
        var user = manager.FindById(User.Identity.GetUserId<int>());
        IdentityHelper.SignIn(manager, user, isPersistent: false);
        msg = "?m=RemoveLoginSuccess";
    }
    Response.Redirect("~/Account/Manage" + msg);
}

È ora possibile eseguire l'applicazione e registrare un nuovo utente.

Per Web Forms con l'aggiornamento 3, modificare le pagine dell'account per passare il tipo di chiave

Per Web Forms con l'aggiornamento 3, è necessario modificare le pagine seguenti.

Confirm.aspx.cx

protected void Page_Load(object sender, EventArgs e)
{
    string code = IdentityHelper.GetCodeFromRequest(Request);
    string userId = IdentityHelper.GetUserIdFromRequest(Request);
    if (code != null && userId != null)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = manager.ConfirmEmail(Int32.Parse(userId), code);
        if (result.Succeeded)
        {
            StatusMessage = "Thank you for confirming your account.";
            return;
        }
    }

    StatusMessage = "An error has occurred";
}

RegisterExternalLogin.aspx.cs

protected void Page_Load()
{
    // Process the result from an auth provider in the request
    ProviderName = IdentityHelper.GetProviderNameFromRequest(Request);
    if (String.IsNullOrEmpty(ProviderName))
    {
        RedirectOnFail();
        return;
    }
    if (!IsPostBack)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
        if (loginInfo == null)
        {
            RedirectOnFail();
            return;
        }
        var user = manager.Find(loginInfo.Login);
        if (user != null)
        {
            IdentityHelper.SignIn(manager, user, isPersistent: false);
            IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
        }
        else if (User.Identity.IsAuthenticated)
        {
            // Apply Xsrf check when linking
            var verifiedloginInfo = Context.GetOwinContext().Authentication
                .GetExternalLoginInfo(IdentityHelper.XsrfKey, User.Identity.GetUserId());
            if (verifiedloginInfo == null)
            {
                RedirectOnFail();
                return;
            }

            var result = manager.AddLogin(User.Identity.GetUserId<int>(), 
                verifiedloginInfo.Login);
            if (result.Succeeded)
            {
                IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], 
                    Response);
            }
            else
            {
                AddErrors(result);
                return;
            }
        }
        else
        {
            email.Text = loginInfo.Email;
        }
    }
}

Manage.aspx.cs

public partial class Manage : System.Web.UI.Page
{
    protected string SuccessMessage
    {
        get;
        private set;
    }

    private bool HasPassword(ApplicationUserManager manager)
    {
        return manager.HasPassword(User.Identity.GetUserId<int>());
    }

    public bool HasPhoneNumber { get; private set; }

    public bool TwoFactorEnabled { get; private set; }

    public bool TwoFactorBrowserRemembered { get; private set; }

    public int LoginsCount { get; set; }

    protected void Page_Load()
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();

        HasPhoneNumber = String.IsNullOrEmpty(manager.GetPhoneNumber(
            User.Identity.GetUserId<int>()));

        // Enable this after setting up two-factor authentientication
        //PhoneNumber.Text = manager.GetPhoneNumber(User.Identity.GetUserId()) ?? String.Empty;

        TwoFactorEnabled = manager.GetTwoFactorEnabled(User.Identity.GetUserId<int>());

        LoginsCount = manager.GetLogins(User.Identity.GetUserId<int>()).Count;

        var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;

        if (!IsPostBack)
        {
            // Determine the sections to render
            if (HasPassword(manager))
            {
                ChangePassword.Visible = true;
            }
            else
            {
                CreatePassword.Visible = true;
                ChangePassword.Visible = false;
            }

            // Render success message
            var message = Request.QueryString["m"];
            if (message != null)
            {
                // Strip the query string from action
                Form.Action = ResolveUrl("~/Account/Manage");

                SuccessMessage =
                    message == "ChangePwdSuccess" ? "Your password has been changed."
                    : message == "SetPwdSuccess" ? "Your password has been set."
                    : message == "RemoveLoginSuccess" ? "The account was removed."
                    : message == "AddPhoneNumberSuccess" ? "Phone number has been added"
                    : message == "RemovePhoneNumberSuccess" ? "Phone number was removed"
                    : String.Empty;
                successMessage.Visible = !String.IsNullOrEmpty(SuccessMessage);
            }
        }
    }

    private void AddErrors(IdentityResult result)
    {
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError("", error);
        }
    }

    // Remove phonenumber from user
    protected void RemovePhone_Click(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = manager.SetPhoneNumber(User.Identity.GetUserId<int>(), null);
        if (!result.Succeeded)
        {
            return;
        }
        var user = manager.FindById(User.Identity.GetUserId<int>());
        if (user != null)
        {
            IdentityHelper.SignIn(manager, user, isPersistent: false);
            Response.Redirect("/Account/Manage?m=RemovePhoneNumberSuccess");
        }
    }

    // DisableTwoFactorAuthentication
    protected void TwoFactorDisable_Click(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        manager.SetTwoFactorEnabled(User.Identity.GetUserId<int>(), false);

        Response.Redirect("/Account/Manage");
    }

    //EnableTwoFactorAuthentication 
    protected void TwoFactorEnable_Click(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        manager.SetTwoFactorEnabled(User.Identity.GetUserId<int>(), true);

        Response.Redirect("/Account/Manage");
    }
}

VerifyPhoneNumber.aspx.cs

public partial class VerifyPhoneNumber : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var phonenumber = Request.QueryString["PhoneNumber"];
        var code = manager.GenerateChangePhoneNumberToken(
            User.Identity.GetUserId<int>(), phonenumber);           
        PhoneNumber.Value = phonenumber;
    }

    protected void Code_Click(object sender, EventArgs e)
    {
        if (!ModelState.IsValid)
        {
            ModelState.AddModelError("", "Invalid code");
            return;
        }

        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();

        var result = manager.ChangePhoneNumber(
            User.Identity.GetUserId<int>(), PhoneNumber.Value, Code.Text);

        if (result.Succeeded)
        {
            var user = manager.FindById(User.Identity.GetUserId<int>());

            if (user != null)
            {
                IdentityHelper.SignIn(manager, user, false);
                Response.Redirect("/Account/Manage?m=AddPhoneNumberSuccess");
            }
        }

        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "Failed to verify phone");
    }
}

AddPhoneNumber.aspx.cs

public partial class AddPhoneNumber : System.Web.UI.Page
{
    protected void PhoneNumber_Click(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var code = manager.GenerateChangePhoneNumberToken(
            User.Identity.GetUserId<int>(), PhoneNumber.Text);
        if (manager.SmsService != null)
        {
            var message = new IdentityMessage
            {
                Destination = PhoneNumber.Text,
                Body = "Your security code is " + code
            };

            manager.SmsService.Send(message);
        }

        Response.Redirect("/Account/VerifyPhoneNumber?PhoneNumber=" + HttpUtility.UrlEncode(PhoneNumber.Text));
    }
}

ManagePassword.aspx.cs

public partial class ManagePassword : System.Web.UI.Page
{
    protected string SuccessMessage
    {
        get;
        private set;
    }

    private bool HasPassword(ApplicationUserManager manager)
    {
        return manager.HasPassword(User.Identity.GetUserId<int>());
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();

        if (!IsPostBack)
        {
            // Determine the sections to render
            if (HasPassword(manager))
            {
                changePasswordHolder.Visible = true;
            }
            else
            {
                setPassword.Visible = true;
                changePasswordHolder.Visible = false;
            }

            // Render success message
            var message = Request.QueryString["m"];
            if (message != null)
            {
                // Strip the query string from action
                Form.Action = ResolveUrl("~/Account/Manage");
            }
        }
    }

    protected void ChangePassword_Click(object sender, EventArgs e)
    {
        if (IsValid)
        {
            var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
            IdentityResult result = manager.ChangePassword(
                User.Identity.GetUserId<int>(), CurrentPassword.Text, NewPassword.Text);
            if (result.Succeeded)
            {
                var user = manager.FindById(User.Identity.GetUserId<int>());
                IdentityHelper.SignIn(manager, user, isPersistent: false);
                Response.Redirect("~/Account/Manage?m=ChangePwdSuccess");
            }
            else
            {
                AddErrors(result);
            }
        }
    }

    protected void SetPassword_Click(object sender, EventArgs e)
    {
        if (IsValid)
        {
            // Create the local login info and link the local account to the user
            var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
            IdentityResult result = manager.AddPassword(
                User.Identity.GetUserId<int>(), password.Text);
            if (result.Succeeded)
            {
                Response.Redirect("~/Account/Manage?m=SetPwdSuccess");
            }
            else
            {
                AddErrors(result);
            }
        }
    }

    private void AddErrors(IdentityResult result)
    {
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError("", error);
        }
    }
}

ManageLogins.aspx.cs

public partial class ManageLogins : System.Web.UI.Page
{
    protected string SuccessMessage
    {
        get;
        private set;
    }
    protected bool CanRemoveExternalLogins
    {
        get;
        private set;
    }

    private bool HasPassword(ApplicationUserManager manager)
    {
        return manager.HasPassword(User.Identity.GetUserId<int>());
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        CanRemoveExternalLogins = manager.GetLogins(
            User.Identity.GetUserId<int>()).Count() > 1;

        SuccessMessage = String.Empty;
        successMessage.Visible = !String.IsNullOrEmpty(SuccessMessage);
    }

    public IEnumerable<UserLoginInfo> GetLogins()
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var accounts = manager.GetLogins(User.Identity.GetUserId<int>());
        CanRemoveExternalLogins = accounts.Count() > 1 || HasPassword(manager);
        return accounts;
    }

    public void RemoveLogin(string loginProvider, string providerKey)
    {
        var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = manager.RemoveLogin(
            User.Identity.GetUserId<int>(), new UserLoginInfo(loginProvider, providerKey));
        string msg = String.Empty;
        if (result.Succeeded)
        {
            var user = manager.FindById(User.Identity.GetUserId<int>());
            IdentityHelper.SignIn(manager, user, isPersistent: false);
            msg = "?m=RemoveLoginSuccess";
        }
        Response.Redirect("~/Account/ManageLogins" + msg);
    }
}

TwoFactorAuthenticationSignIn.aspx.cs

public partial class TwoFactorAuthenticationSignIn : System.Web.UI.Page
{
    private ApplicationSignInManager signinManager;
    private ApplicationUserManager manager;

    public TwoFactorAuthenticationSignIn()
    {
        manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        var userId = signinManager.GetVerifiedUserId<ApplicationUser, int>();
        if (userId == default(int))
        {
            Response.Redirect("/Account/Error", true);
        }
        var userFactors = manager.GetValidTwoFactorProviders(userId);
        Providers.DataSource = userFactors.Select(x => x).ToList();
        Providers.DataBind();            
    }

    protected void CodeSubmit_Click(object sender, EventArgs e)
    {
        bool rememberMe = false;
        bool.TryParse(Request.QueryString["RememberMe"], out rememberMe);
            
        var result = signinManager.TwoFactorSignIn<ApplicationUser, int>(SelectedProvider.Value, Code.Text, isPersistent: rememberMe, rememberBrowser: RememberBrowser.Checked);
        switch (result)
        {
            case SignInStatus.Success:
                IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
                break;
            case SignInStatus.LockedOut:
                Response.Redirect("/Account/Lockout");
                break;
            case SignInStatus.Failure:
            default:
                FailureText.Text = "Invalid code";
                ErrorMessage.Visible = true;
                break;
        }
    }

    protected void ProviderSubmit_Click(object sender, EventArgs e)
    {
        if (!signinManager.SendTwoFactorCode(Providers.SelectedValue))
        {
            Response.Redirect("/Account/Error");
        }

        var user = manager.FindById(signinManager.GetVerifiedUserId<ApplicationUser, int>());
        if (user != null)
        {
            var code = manager.GenerateTwoFactorToken(user.Id, Providers.SelectedValue);
        }

        SelectedProvider.Value = Providers.SelectedValue;
        sendcode.Visible = false;
        verifycode.Visible = true;
    }
}

Eseguire l'applicazione

Sono state completate tutte le modifiche necessarie al modello applicazione Web predefinito. Eseguire l'applicazione e registrare un nuovo utente. Dopo aver registrato l'utente, si noterà che la tabella AspNetUsers ha una colonna ID che è un numero intero.

nuova chiave primaria

Se in precedenza sono state create le tabelle ASP.NET Identity con una chiave primaria diversa, è necessario apportare alcune modifiche aggiuntive. Se possibile, eliminare semplicemente il database esistente. Il database verrà ricreato con la progettazione corretta quando si esegue l'applicazione Web e si aggiungerà un nuovo utente. Se l'eliminazione non è possibile, eseguire le migrazioni code first per modificare le tabelle. Tuttavia, la nuova chiave primaria integer non verrà configurata come proprietà SQL IDENTITY nel database. È necessario impostare manualmente la colonna ID come IDENTITÀ.

Altre risorse