將現有的網站從 SQL 成員資格移轉至 ASP.NET Identity

作者 :Rick AndersonSuhas Joshi

本教學課程說明如何使用 SQL 成員資格建立的現有 Web 應用程式,將現有的 Web 應用程式移轉至新的 ASP.NET 身分識別系統。 此方法牽涉到將現有的資料庫架構變更為 ASP.NET 身分識別所需的架構,並在舊/新類別中連結至該架構。 採用此方法之後,一旦移轉資料庫,未來的身分識別更新將會輕鬆處理。

在本教學課程中,我們將採用使用 Visual Studio 2010 (Web Form) 建立的 Web 應用程式範本,以建立使用者和角色資料。 接著,我們將使用 SQL 腳本,將現有的資料庫移轉至身分識別系統所需的資料表。 接下來,我們將安裝必要的 NuGet 套件,並新增使用身分識別系統進行成員資格管理的新帳戶管理頁面。 為了測試移轉,使用 SQL 成員資格建立的使用者應該能夠登入,而且新使用者應該能夠註冊。 您可以在這裡找到完整的範例。 另請參閱 從 ASP.NET 成員資格移轉至 ASP.NET 身分識別

開始使用

使用 SQL 成員資格建立應用程式

  1. 我們必須從使用 SQL 成員資格並具有使用者和角色資料的現有應用程式開始。 基於本文的目的,讓我們在 Visual Studio 2010 中建立 Web 應用程式。

    在 Visual Studio 2010 中建立 Web 應用程式的螢幕擷取畫面。

  2. 使用 ASP.NET 組態工具,建立 2 個使用者: oldAdminUseroldUser。

    A S P 的螢幕擷取畫面。N E T 組態工具可建立兩個使用者。

    用來管理所有安全性的網站管理工具螢幕擷取畫面。

  3. 建立名為 管理員 的角色,並將 'oldAdminUser' 新增為該角色中的使用者。

    建立為該角色中使用者管理員角色的螢幕擷取畫面。

  4. 使用 Default.aspx 建立網站的管理員區段。 在web.config檔案中設定授權標籤,只允許管理員角色中的使用者存取。 如需詳細資訊,請參閱這裡 https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    網站管理員區段的螢幕擷取畫面。

  5. 在 [伺服器總管] 中檢視資料庫,以瞭解 SQL 成員資格系統所建立的資料表。 使用者登入資料會儲存在aspnet_Users和aspnet_Membership資料表中,而角色資料則會儲存在aspnet_Roles資料表中。 aspnet_UsersInRoles 資料表中儲存哪些使用者的角色相關資訊。 若要進行基本成員資格管理,就足以將上述表格中的資訊移植到 ASP.NET 身分識別系統。

    [伺服器總管] 的螢幕擷取畫面,可檢視資料庫以瞭解 S Q L 成員資格系統所建立的資料表。

移轉至Visual Studio 2013

  1. 安裝 Visual Studio Express 2013 for Web 或 Visual Studio 2013 以及最新的更新

  2. 在已安裝的 Visual Studio 版本中開啟上述專案。 如果電腦上未安裝SQL Server Express,當您開啟專案時會顯示提示,因為連接字串會使用 SQL Express。 您可以選擇安裝 SQL Express,或因應將連接字串變更為 LocalDb。 在本文中,我們會將其變更為 LocalDb。

  3. 開啟web.config,然後從 變更連接字串。SQLExpress to (LocalDb) v11.0。 從連接字串中移除 'User Instance=true'。

    變更要移轉至Visual Studio 2013之連接字串的螢幕擷取畫面。

  4. 開啟 [伺服器總管],並確認可以觀察資料表架構和資料。

  5. ASP.NET 身分識別系統適用于 4.5 版或更高版本的架構。 將應用程式重定為 4.5 或更高版本。

    A S P 的螢幕擷取畫面。N E T 身分識別系統。

    建置專案以確認沒有任何錯誤。

安裝 Nuget 套件

  1. 在 方案總管中,以滑鼠右鍵按一下 [管理 NuGet 套件] 專案 > 。 在搜尋方塊中,輸入 「Asp.net Identity」。 在結果清單中選取套件,然後按一下 [安裝]。 按一下 [我接受] 按鈕,以接受授權合約。 請注意,此套件會安裝相依性套件:EntityFramework 和 Microsoft ASP.NET Identity Core。 同樣地,如果您不想啟用 OAuth 登入) ,請安裝下列套件 (略過最後 4 個 OWIN 套件:

    • Microsoft.AspNet.Identity.Owin

    • Microsoft.Owin.Host.SystemWeb

    • Microsoft.Owin.Security.Facebook

    • Microsoft.Owin.Security.Google

    • Microsoft.Owin.Security.MicrosoftAccount

    • Microsoft.Owin.Security.Twitter

      在 方案總管 中安裝 Nuget 套件的螢幕擷取畫面。

將資料庫移轉至新的身分識別系統

下一個步驟是將現有的資料庫移轉至 ASP.NET 身分識別系統所需的架構。 為了達成此目的,我們會執行具有一組命令的 SQL 腳本,以建立新的資料表,並將現有的使用者資訊遷移至新的資料表。 您可以 在這裡找到腳本檔案。

此腳本檔案專屬於此範例。 如果自訂或修改使用 SQL 成員資格建立之資料表的架構,則必須據以變更腳本。

如何產生架構移轉的 SQL 腳本

若要 ASP.NET 身分識別類別以現成可用的現有使用者資料,我們必須將資料庫架構移轉至 ASP.NET 身分識別所需的架構。 我們可以藉由新增資料表,並將現有的資訊複製到這些資料表來完成此動作。 根據預設 ASP.NET 身分識別會使用 EntityFramework 將身分識別模型類別對應回資料庫,以儲存/擷取資訊。 這些模型類別會實作定義使用者和角色物件的核心身分識別介面。 資料庫中的資料表和資料行是以這些模型類別為基礎。 Identity v2.1.0 中的 EntityFramework 模型類別及其屬性如下定義

IdentityUser 類型 IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
識別碼 字串 識別碼 RoleId ProviderKey 識別碼
使用者名稱 字串 名稱 UserId UserId ClaimType
PasswordHash string LoginProvider ClaimValue
SecurityStamp string User_Id
電子郵件 string
EmailConfirmed bool
PhoneNumber string
PhoneNumberConfirmed bool
LockoutEnabled bool
LockoutEndDate Datetime
AccessFailedCount int

我們需要針對每個模型建立資料表,其中每個模型都有對應至屬性的資料行。 類別和資料表之間的對應定義于 的 方法中 OnModelCreatingIdentityDBContext 。 這稱為組態的 Fluent API 方法,如需詳細資訊,請參閱 這裡。 類別的組態如下所述

類別 Table 主要金鑰 外鍵
IdentityUser AspnetUsers 識別碼
IdentityRole AspnetRoles 識別碼
IdentityUserRole AspnetUserRole UserId + RoleId User_Id-AspnetUsers > RoleId-AspnetRoles >
IdentityUserLogin AspnetUserLogins ProviderKey+UserId + LoginProvider UserId-AspnetUsers >
IdentityUserClaim AspnetUserClaims 識別碼 User_Id-AspnetUsers >

透過這項資訊,我們可以建立 SQL 語句來建立新的資料表。 我們可以個別撰寫每個語句,或使用 EntityFramework PowerShell 命令產生整個腳本,然後我們可以視需要編輯。 若要這樣做,請在 VS 中,從 [檢視] 或 [工具] 功能表開啟[套件管理員主控台]

  • 執行命令 「Enable-Migrations」 以啟用 EntityFramework 移轉。
  • 執行命令 「Add-migration initial」,以建立初始安裝程式碼,以在 C#/VB 中建立資料庫。
  • 最後一個步驟是執行 「Update-Database –Script」 命令,以根據模型類別產生 SQL 腳本。

如果應用程式使用 SQLite 作為其身分識別資料存放區,則不支援某些命令。 由於資料庫引擎的限制, Alter 命令會擲回下列例外狀況:

「System.NotSupportedException:SQLite 不支援此移轉作業。」

作為因應措施,請在資料庫上執行 Code First 移轉來變更資料表。

此資料庫產生腳本可用來做為開始,我們將進行其他變更來新增資料行和複製資料。 優點是,當模型類別在未來版本的 Identity 版本變更時,我們會產生 _MigrationHistory EntityFramework 用來修改資料庫架構的資料表。

除了身分識別使用者模型類別中的屬性之外,SQL 成員資格使用者資訊還有其他屬性,也就是電子郵件、密碼嘗試、上次登入日期、上次鎖定日期等。這是有用的資訊,我們想要將其傳遞給身分識別系統。 您可以將其他屬性新增至使用者模型,並將其對應回資料庫中的資料表資料行來完成。 我們可以藉由新增子類別 IdentityUser 化模型的類別來執行此動作。 我們可以將屬性新增至這個自訂類別,並編輯 SQL 腳本,以在建立資料表時加入對應的資料行。 本文會進一步說明此類別的程式碼。 新增新屬性之後建立 AspnetUsers 資料表的 SQL 腳本會是

CREATE TABLE [dbo].[AspNetUsers] (
    [Id]            NVARCHAR (128) NOT NULL,
    [UserName]      NVARCHAR (MAX) NULL,
    [PasswordHash]  NVARCHAR (MAX) NULL,
    [SecurityStamp] NVARCHAR (MAX) NULL,
    [EmailConfirmed]       BIT            NOT NULL,
    [PhoneNumber]          NVARCHAR (MAX) NULL,
    [PhoneNumberConfirmed] BIT            NOT NULL,
    [TwoFactorEnabled]     BIT            NOT NULL,
    [LockoutEndDateUtc]    DATETIME       NULL,
    [LockoutEnabled]       BIT            NOT NULL,
    [AccessFailedCount]    INT            NOT NULL,
    [ApplicationId]                          UNIQUEIDENTIFIER NOT NULL,
    [LegacyPasswordHash]  NVARCHAR (MAX) NULL,
    [LoweredUserName]  NVARCHAR (256)   NOT NULL,
    [MobileAlias]      NVARCHAR (16)    DEFAULT (NULL) NULL,
    [IsAnonymous]      BIT              DEFAULT ((0)) NOT NULL,
    [LastActivityDate] DATETIME2         NOT NULL,
    [MobilePIN]                              NVARCHAR (16)    NULL,
    [Email]                                  NVARCHAR (256)   NULL,
    [LoweredEmail]                           NVARCHAR (256)   NULL,
    [PasswordQuestion]                       NVARCHAR (256)   NULL,
    [PasswordAnswer]                         NVARCHAR (128)   NULL,
    [IsApproved]                             BIT              NOT NULL,
    [IsLockedOut]                            BIT              NOT NULL,
    [CreateDate]                             DATETIME2               NOT NULL,
    [LastLoginDate]                          DATETIME2         NOT NULL,
    [LastPasswordChangedDate]                DATETIME2         NOT NULL,
    [LastLockoutDate]                        DATETIME2         NOT NULL,
    [FailedPasswordAttemptCount]             INT              NOT NULL,
    [FailedPasswordAttemptWindowStart]       DATETIME2         NOT NULL,
    [FailedPasswordAnswerAttemptCount]       INT              NOT NULL,
    [FailedPasswordAnswerAttemptWindowStart] DATETIME2         NOT NULL,
    [Comment]                                NTEXT            NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC),
    FOREIGN KEY ([ApplicationId]) REFERENCES [dbo].[aspnet_Applications] ([ApplicationId]),
);

接下來,我們需要將現有資訊從 SQL 成員資格資料庫複製到新加入的身分識別資料表。 這可以透過 SQL 來完成,方法是將資料直接從一個資料表複製到另一個資料表。 若要將資料加入資料表的資料列,我們會使用 INSERT INTO [Table] 建構。 若要從另一個資料表複製,我們可以搭配 語句使用 INSERT INTOSELECT 語句。 若要取得所有使用者資訊,我們需要查詢 aspnet_Usersaspnet_Membership 資料表,並將資料複製到 AspNetUsers 資料表。 我們會使用 INSERT INTO 和 和 SELECTJOINLEFT OUTER JOIN 語句。 如需在資料表之間查詢和複製資料的詳細資訊,請參閱 連結。 此外,AspnetUserLogins 和 AspnetUserClaims 資料表是空的,因為 SQL 成員資格中預設沒有對應至此專案的資訊。 唯一複製的資訊適用于使用者和角色。 針對在先前步驟中建立的專案,將資訊複製到使用者資料表的 SQL 查詢會是

INSERT INTO AspNetUsers(Id,UserName,PasswordHash,SecurityStamp,EmailConfirmed,
PhoneNumber,PhoneNumberConfirmed,TwoFactorEnabled,LockoutEndDateUtc,LockoutEnabled,AccessFailedCount,
ApplicationId,LoweredUserName,MobileAlias,IsAnonymous,LastActivityDate,LegacyPasswordHash,
MobilePIN,Email,LoweredEmail,PasswordQuestion,PasswordAnswer,IsApproved,IsLockedOut,CreateDate,
LastLoginDate,LastPasswordChangedDate,LastLockoutDate,FailedPasswordAttemptCount,
FailedPasswordAnswerAttemptWindowStart,FailedPasswordAnswerAttemptCount,FailedPasswordAttemptWindowStart,Comment)
SELECT aspnet_Users.UserId,aspnet_Users.UserName,(aspnet_Membership.Password+'|'+CAST(aspnet_Membership.PasswordFormat as varchar)+'|'+aspnet_Membership.PasswordSalt),NewID(),
'true',NULL,'false','true',aspnet_Membership.LastLockoutDate,'true','0',
aspnet_Users.ApplicationId,aspnet_Users.LoweredUserName,
aspnet_Users.MobileAlias,aspnet_Users.IsAnonymous,aspnet_Users.LastActivityDate,aspnet_Membership.Password,
aspnet_Membership.MobilePIN,aspnet_Membership.Email,aspnet_Membership.LoweredEmail,aspnet_Membership.PasswordQuestion,aspnet_Membership.PasswordAnswer,
aspnet_Membership.IsApproved,aspnet_Membership.IsLockedOut,aspnet_Membership.CreateDate,aspnet_Membership.LastLoginDate,aspnet_Membership.LastPasswordChangedDate,
aspnet_Membership.LastLockoutDate,aspnet_Membership.FailedPasswordAttemptCount, aspnet_Membership.FailedPasswordAnswerAttemptWindowStart,
aspnet_Membership.FailedPasswordAnswerAttemptCount,aspnet_Membership.FailedPasswordAttemptWindowStart,aspnet_Membership.Comment
FROM aspnet_Users
LEFT OUTER JOIN aspnet_Membership ON aspnet_Membership.ApplicationId = aspnet_Users.ApplicationId 
AND aspnet_Users.UserId = aspnet_Membership.UserId;

在上述 SQL 語句中,來自 aspnet_Usersaspnet_Membership 資料表的每個使用者資訊都會複製到 AspnetUsers 資料表的資料行。 這裡唯一完成的修改是複製密碼時。 由於 SQL 成員資格中密碼的加密演算法使用 'PasswordSalt' 和 'PasswordFormat',因此我們也會複製該密碼以及雜湊密碼,以便用來依身分識別解密密碼。 連結自訂密碼時,本文會進一步說明這點。

此腳本檔案專屬於此範例。 對於具有其他資料表的應用程式,開發人員可以遵循類似的方法,在使用者模型類別上新增其他屬性,並將其對應至 AspnetUsers 資料表中的資料行。 若要執行腳本,

  1. 開啟 [伺服器總管]。 展開 [ApplicationServices] 連線以顯示資料表。 以滑鼠右鍵按一下 [資料表] 節點,然後選取 [新增查詢] 選項

    開啟伺服器總管的螢幕擷取畫面,展開 [應用程式服務] 連線以顯示資料表。

  2. 在查詢視窗中,從 Migrations.sql 檔案複製並貼上整個 SQL 腳本。 按一下 [執行] 箭號按鈕,以執行腳本檔案。

    要複製並貼上整個 S Q L 腳本的查詢視窗螢幕擷取畫面。

    重新整理 [伺服器總管] 視窗。 資料庫中會建立五個新的資料表。

    重新整理 [伺服器總管] 視窗以在資料庫中建立五個新資料表的螢幕擷取畫面。

    資料連線中資料表的螢幕擷取畫面。

    以下是 SQL 成員資格資料表中的資訊如何對應至新的身分識別系統。

    aspnet_Roles -- > AspNetRoles

    asp_netUsers和asp_netMembership -- > AspNetUsers

    aspnet_UserInRoles -- > AspNetUserRoles

    如上一節所述,AspNetUserClaims 和 AspNetUserLogins 資料表是空的。 AspNetUser 資料表中的 [鑒別子] 欄位應該符合定義為下一個步驟的模型類別名稱。 此外,PasswordHash 資料行的格式為 'encrypted password |password salt|password format'。 這可讓您使用特殊的 SQL 成員資格密碼編譯邏輯,以便重複使用舊的密碼。 本文稍後會加以說明。

建立模型和成員資格頁面

如先前所述,身分識別功能會使用 Entity Framework 與資料庫通訊,以預設儲存帳戶資訊。 若要使用資料表中的現有資料,我們需要建立模型類別,以對應回資料表,並在身分識別系統中連結它們。 作為身分識別合約的一部分,模型類別應該實作 Identity.Core dll 中定義的介面,也可以擴充 Microsoft.AspNet.Identity.EntityFramework 中這些介面的現有實作。

在我們的範例中,AspNetRoles、AspNetUserClaims、AspNetLogins 和 AspNetUserRole 資料表的資料行與身分識別系統的現有實作類似。 因此,我們可以重複使用現有的類別來對應到這些資料表。 AspNetUser 資料表有一些額外的資料行,可用來儲存 SQL 成員資格資料表中的其他資訊。 建立可擴充現有 'IdentityUser' 實作的模型類別,並新增其他屬性,即可對應此專案。

  1. 在專案中建立 Models 資料夾,並新增 User 類別。 類別的名稱應該符合在 'AspnetUsers' 資料表的 「鑒別子」資料行中新增的資料。

    在專案中建立 Models 資料夾並新增類別 User 的螢幕擷取畫面。

    User 類別應該擴充 在 Microsoft.AspNet.Identity.EntityFramework dll 中找到的 IdentityUser 類別。 宣告類別中對應回 AspNetUser 資料行的屬性。 Id、Username、PasswordHash 和 SecurityStamp 屬性定義于 IdentityUser 中,因此會省略。 以下是具有所有屬性之 User 類別的程式碼

    public class User : IdentityUser
    {
        public User()
        {
            CreateDate = DateTime.Now;
            IsApproved = false;
            LastLoginDate = DateTime.Now;
            LastActivityDate = DateTime.Now;
            LastPasswordChangedDate = DateTime.Now;
            LastLockoutDate = DateTime.Parse("1/1/1754");
            FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754");
            FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754");
        }
    
        public System.Guid ApplicationId { get; set; }
        public string MobileAlias { get; set; }
        public bool IsAnonymous { get; set; }
        public System.DateTime LastActivityDate { get; set; }
        public string MobilePIN { get; set; }
        public string LoweredEmail { get; set; }
        public string LoweredUserName { get; set; }
        public string PasswordQuestion { get; set; }
        public string PasswordAnswer { get; set; }
        public bool IsApproved { get; set; }
        public bool IsLockedOut { get; set; }
        public System.DateTime CreateDate { get; set; }
        public System.DateTime LastLoginDate { get; set; }
        public System.DateTime LastPasswordChangedDate { get; set; }
        public System.DateTime LastLockoutDate { get; set; }
        public int FailedPasswordAttemptCount { get; set; }
        public System.DateTime FailedPasswordAttemptWindowStart { get; set; }
        public int FailedPasswordAnswerAttemptCount { get; set; }
        public System.DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
        public string Comment { get; set; }
    }
    
  2. 需要 Entity Framework DbCoNtext 類別,才能將模型中的資料保存回資料表,並從資料表擷取資料以填入模型。 Microsoft.AspNet.Identity.EntityFramework dll 會定義 IdentityDbCoNtext 類別,此類別會與 Identity 資料表互動以擷取和儲存資訊。 IdentityDbCoNtext < tuser > 採用 'TUser' 類別,可以是任何擴充 IdentityUser 類別的類別。

    建立新的類別 ApplicationDBCoNtext,以在 'Models' 資料夾下擴充 IdentityDbCoNtext,並傳入在步驟 1 中建立的 'User' 類別

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. 新身分識別系統中的使用者管理是使用Microsoft.AspNet.Identity.EntityFramework dll 中定義的 UserManager < tuser > 類別來完成。 我們需要建立可擴充 UserManager 的自訂類別,並傳入在步驟 1 中建立的 'User' 類別。

    在 Models 資料夾中,建立可擴充 UserManager 使用者的新類別 UserManager <>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. 應用程式使用者的密碼會加密並儲存在資料庫中。 SQL 成員資格中使用的密碼編譯演算法與新身分識別系統中的演算法不同。 若要重複使用舊密碼,我們需要在舊使用者使用 SQL 成員資格演算法登入時選擇性地解密密碼,同時在新使用者的身分識別中使用密碼編譯演算法。

    UserManager 類別具有屬性 'PasswordHasher',可儲存實作 'IPasswordHasher' 介面之類別的實例。 這是用來在使用者驗證交易期間加密/解密密碼。 在步驟 3 中定義的 UserManager 類別中,建立新的類別 SQLPasswordHasher 並複製下列程式碼。

    public class SQLPasswordHasher : PasswordHasher
    {
        public override string HashPassword(string password)
        {
            return base.HashPassword(password);
        }
    
        public override PasswordVerificationResult VerifyHashedPassword(string  hashedPassword, string providedPassword)
        {
            string[] passwordProperties = hashedPassword.Split('|');
            if (passwordProperties.Length != 3)
            {
                return base.VerifyHashedPassword(hashedPassword, providedPassword);
            }
            else
            {
                string passwordHash = passwordProperties[0];
                int passwordformat = 1;
                string salt = passwordProperties[2];
                if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
                {
                    return PasswordVerificationResult.SuccessRehashNeeded;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
            }
        }
    
        //This is copied from the existing SQL providers and is provided only for back-compat.
        private string EncryptPassword(string pass, int passwordFormat, string salt)
        {
            if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                return pass;
    
            byte[] bIn = Encoding.Unicode.GetBytes(pass);
            byte[] bSalt = Convert.FromBase64String(salt);
            byte[] bRet = null;
    
            if (passwordFormat == 1)
            { // MembershipPasswordFormat.Hashed 
                HashAlgorithm hm = HashAlgorithm.Create("SHA1");
                if (hm is KeyedHashAlgorithm)
                {
                    KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
                    if (kha.Key.Length == bSalt.Length)
                    {
                        kha.Key = bSalt;
                    }
                    else if (kha.Key.Length < bSalt.Length)
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
                        kha.Key = bKey;
                    }
                    else
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        for (int iter = 0; iter < bKey.Length; )
                        {
                            int len = Math.Min(bSalt.Length, bKey.Length - iter);
                            Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
                            iter += len;
                        }
                        kha.Key = bKey;
                    }
                    bRet = kha.ComputeHash(bIn);
                }
                else
                {
                    byte[] bAll = new byte[bSalt.Length + bIn.Length];
                    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                    bRet = hm.ComputeHash(bAll);
                }
            }
    
            return Convert.ToBase64String(bRet);
        }
    

    匯入 System.Text 和 System.Security.Cryptography 命名空間來解決編譯錯誤。

    EncodePassword 方法會根據預設 SQL 成員資格加密實作來加密密碼。 這是取自 System.Web dll。 如果舊應用程式使用自訂實作,則應該在此反映。 我們需要定義兩個 其他方法 HashPasswordVerifyHashedPassword ,以使用 EncodePassword 方法來雜湊指定的密碼,或使用資料庫中現有的密碼來驗證純文字密碼。

    SQL 成員資格系統使用 PasswordHash、PasswordSalt 和 PasswordFormat 來雜湊使用者註冊或變更密碼時所輸入的密碼。 在移轉期間,這三個欄位都會儲存在 AspNetUser 資料表的 PasswordHash 資料行中,並以 '|' 字元分隔。 當使用者登入且密碼具有這些欄位時,我們會使用 SQL 成員資格加密來檢查密碼;否則,我們會使用身分識別系統的預設密碼編譯來驗證密碼。 如此一來,舊使用者就不需要在應用程式移轉後變更其密碼。

  5. 宣告 UserManager 類別的建構函式,並將這個 作為 SQLPasswordHasher 傳遞給建構函式中的 屬性。

    public UserManager()
                : base(new UserStore<User>(new ApplicationDbContext()))
    {
                this.PasswordHasher = new SQLPasswordHasher();
    }
    

建立新的帳戶管理頁面

移轉的下一個步驟是新增帳戶管理頁面,讓使用者註冊並登入。 SQL 成員資格中的舊帳戶頁面會使用不適用於新身分識別系統的控制項。 若要新增使用者管理頁面,請遵循本連結 https://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project 中的教學課程,從「新增Web Form以將使用者註冊至您的應用程式」步驟開始,因為我們已經建立專案並新增 NuGet 套件。

我們需要對範例進行一些變更,才能使用我們在這裡擁有的專案。

  • Register.aspx.cs 和 Login.aspx.cs 程式碼後置類別會使用 UserManager Identity 套件中的 來建立使用者。 在此範例中,請遵循稍早所述的步驟,使用 Models 資料夾中新增的 UserManager。

  • 使用在 Register.aspx.cs 和 Login.aspx.cs 程式碼後置類別中建立的 User 類別,而不是 IdentityUser。 這會在我們的自訂使用者類別中連結至身分識別系統。

  • 您可以略過建立資料庫的元件。

  • 開發人員需要為新使用者設定 ApplicationId,以符合目前的應用程式識別碼。 您可以在 Register.aspx.cs 類別中建立使用者物件之前查詢此應用程式的 ApplicationId,並在建立使用者之前設定它來完成。

    範例:

    在 Register.aspx.cs 頁面中定義方法來查詢aspnet_Applications資料表,並根據應用程式名稱取得應用程式識別碼

    private Guid GetApplicationID()
    {
        using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
        {
            string queryString = "SELECT ApplicationId from aspnet_Applications WHERE ApplicationName = '/'"; //Set application name as in database
    
            SqlCommand command = new SqlCommand(queryString, connection);
            command.Connection.Open();
    
            var reader = command.ExecuteReader();
            while (reader.Read())
            {
                return reader.GetGuid(0);
            }
    
            return Guid.NewGuid();
        }
    }
    

    現在,在使用者物件上設定此專案

    var currentApplicationId = GetApplicationID();
    
    User user = new User() { UserName = Username.Text,
    ApplicationId=currentApplicationId, …};
    

使用舊的使用者名稱和密碼來登入現有的使用者。 使用 [註冊] 頁面來建立新的使用者。 也請確認使用者如預期般位於角色中。

移植到身分識別系統可協助使用者將 Open Authentication (OAuth) 新增至應用程式。 請參閱 此處 已啟用 OAuth 的範例。

後續步驟

在本教學課程中,我們示範如何將使用者從 SQL 成員資格移植到 ASP.NET 身分識別,但並未移植設定檔資料。 在下一個教學課程中,我們將探討如何將設定檔資料從 SQL 成員資格移植到新的身分識別系統。

您可以在本文底部留下意見反應。

感謝 Tom Dykstra 和 Rick Anderson 檢閱文章。