共用方式為


從 ASP.NET 成員資格驗證移轉至 ASP.NET Core 2.0 Identity

作者:Isaac Levin

本文示範如何使用成員資格驗證,將 ASP.NET 應用程式的資料庫結構描述移轉至 ASP.NET Core 2.0 Identity。

注意

本文件提供將 ASP.NET 成員資格型應用程式的資料庫結構描述移轉至用於 ASP.NET Core Identity 的資料庫結構描述所需的步驟。 如需從 ASP.NET 成員資格型驗證移轉至 ASP.NET Identity 的詳細資訊,請參閱將現有應用程式從 SQL 成員資格移轉至 ASP.NETIdentity。 如需 ASP.NET Core Identity 的詳細資訊,請參閱 ASP.NET Core 上的 Identity 簡介

檢閱成員資格結構描述

ASP.NET 2.0 之前,開發人員會負責為其應用程式建立整個驗證和授權程式。 透過 ASP.NET 2.0 引進了成員資格,提供一個重複使用的解決方案來處理 ASP.NET 應用程式內的安全性。 開發人員現在可以使用 ASP.NET SQL Server 註冊工具 (Aspnet_regsql.exe)(不再支援) 將結構描述引導至 SQL Server 資料庫。 執行此命令之後,會在資料庫中建立下列資料表。

成員資格資料表

若要將現有的應用程式移轉至 ASP.NET Core 2.0 Identity,這些資料表中的資料必須移轉至新 Identity 結構描述所使用的資料表。

ASP.NET Core Identity 2.0 結構描述

ASP.NET Core 2.0 遵循 Identity ASP.NET 4.5 中引進的準則。 雖然準則是共用的,但架構之間的實作不同,即使在 ASP.NET Core 版本之間也是如此 (請參閱移轉驗證和 Identity ASP.NET Core 2.0)。

檢視 ASP.NET Core 2.0 Identity 架構的最快方式是建立新的 ASP.NET Core 2.0 應用程式。 請在 Visual Studio 2017 中遵循下列步驟:

  1. 選取 [檔案]> [新增]> [專案]

  2. 建立名為 CoreIdentitySample 的新 ASP.NET Core Web 應用程式專案。

  3. 在下拉式清單中選取 [ASP.NET Core 2.0],然後選取 [Web 應用程式]。 此範本會產生 Razor Pages 應用程式。 按一下 [確定] 之前,按一下 [變更驗證]

  4. 範本選擇 [個別使用者帳戶]Identity。 最後,按一下 [確定],然後按一下 [確定]。 Visual Studio 會使用 ASP.NET Core Identity 範本建立專案。

  5. 選取 [工具]>[NuGet 套件管理員]>[套件管理員主控台] 以開啟 [套件管理員主控台] (PMC) 視窗。

  6. 瀏覽至 PMC 中的專案根目錄,然後執行 Entity Framework (EF) CoreUpdate-Database 命令。

    ASP.NET Core 2.0 Identity 會使用 EF Core 與儲存驗證資料的資料庫互動。 為了讓新建立的應用程式能夠正常運作,必須有資料庫來儲存此資料。 建立新的應用程式之後,檢查資料庫環境中結構描述的最快方式是使用 EF Core 移轉來建立資料庫。 此程式會在本機或其他地方建立資料庫,以模擬該結構描述。 如需詳細資訊,請檢閱上述文件。

    EF Core 命令會針對 appsettings.json 中指定的資料庫使用連接字串。 下列連接字串是以 localhost 上名為 asp-net-core-identity 的資料庫為目標。 在此設定中,EF Core 設定為使用 DefaultConnection 連接字串。

    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true"
      }
    }
    

警告

本文說明如何使用 連接字串。 使用本機資料庫時,使用者不需要經過驗證,但在生產環境中,連接字串 有時會包含要驗證的密碼。 資源擁有者密碼認證 (ROPC) 是生產資料庫中應避免的安全性風險。 實際執行應用程式應該使用可用的最安全驗證流程。 如需部署至測試或生產環境之應用程式驗證的詳細資訊,請參閱 保護驗證流程

  1. 選取 [檢視]>[SQL Server 物件總管]。 展開 appsettings.jsonConnectionStrings:DefaultConnection 屬性中所指定資料庫名稱對應的節點。

    Update-Database 命令會使用結構描述和應用程式初始化所需的任何資料,建立指定的資料庫。 下圖描述使用上述步驟建立的資料表結構。

    Identity 資料表

移轉結構描述

成員資格和 ASP.NET Core Identity 的資料表結構和欄位有細微的差異。 使用 ASP.NET 和 ASP.NET Core 應用程式的驗證/授權模式已發生重大變更。 仍然與 Identity 搭配使用的索引鍵物件為 UsersRoles。 以下是 UsersRolesUserRoles 的對應資料表。

使用者

Identity
(dbo.AspNetUsers) 資料行
類型 成員資格
(dbo.aspnet_Users / dbo.aspnet_Membership) 資料行
類型
Id string aspnet_Users.UserId string
UserName string aspnet_Users.UserName string
Email string aspnet_Membership.Email string
NormalizedUserName string aspnet_Users.LoweredUserName string
NormalizedEmail string aspnet_Membership.LoweredEmail string
PhoneNumber string aspnet_Users.MobileAlias string
LockoutEnabled bit aspnet_Membership.IsLockedOut bit

IsLockedOut 不會對應至 LockoutEnabled。 如果使用者登入失敗次數太多,且已鎖定一段時間,則會設定 IsLockedOutLockoutEnabled 啟用鎖定登入嘗試失敗次數過多的使用者。 當使用者登入嘗試失敗次數過多時,LockoutEnd 會設定為未來的日期,且使用者必須等到該日期才能登入。 如果 LockoutEnabled 為 false,則使用者永遠不會因為登入嘗試失敗次數過多而遭到鎖定。 根據 OWASP多次登入嘗試失敗後遭到鎖定的暫時帳戶,對合法使用者來說是一個太容易受到 DoS 攻擊的目標

如需鎖定的詳細資訊,請參閱弱式鎖定機制的 OWASP 測試 (英文)。

移轉至 Identity 並想要啟用失敗登入鎖定的應用程式,應在移轉過程中將 LockoutEnabled 設定為 true。

注意

並非所有欄位對應都類似從成員資格到 ASP.NET Core Identity 的一對一關聯性。 上表採用預設的成員資格使用者結構描述,並將其對應至 ASP.NET Core Identity 結構描述。 用於成員資格的任何其他自訂欄位都必須手動對應。 在此對應中,密碼沒有對應,因為密碼準則和密碼 Salt 不會在兩者之間移轉。 建議將密碼保留為 Null,並要求使用者重設其密碼。 在 ASP.NET Core Identity 中,如果使用者遭到鎖定,LockoutEnd 應設定為將來的某個日期。這會顯示在移轉指令碼中。

Roles

Identity
(dbo.AspNetRoles) 資料行
類型 成員資格
(dbo.aspnet_Roles) 資料行
類型
Id string RoleId string
Name string RoleName string
NormalizedName string LoweredRoleName string

使用者角色

Identity
(dbo.AspNetUserRoles) 資料行
類型 成員資格
(dbo.aspnet_UsersInRoles) 資料行
類型
RoleId string RoleId string
UserId string UserId string

建立 UsersRoles 的移轉指令碼時,請參考上述對應資料表。 下列範例假設資料庫伺服器上有兩個資料庫。 一個資料庫包含現有的 ASP.NET 成員資格結構描述和資料。 另一個 CoreIdentitySample 資料庫是使用前述的步驟建立的。 內嵌註解以取得詳細資料。

-- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
BEGIN TRANSACTION MigrateUsersAndRoles
USE aspnetdb

-- INSERT USERS
INSERT INTO CoreIdentitySample.dbo.AspNetUsers
            (Id,
             UserName,
             NormalizedUserName,
             PasswordHash,
             SecurityStamp,
             EmailConfirmed,
             PhoneNumber,
             PhoneNumberConfirmed,
             TwoFactorEnabled,
             LockoutEnd,
             LockoutEnabled,
             AccessFailedCount,
             Email,
             NormalizedEmail)
SELECT aspnet_Users.UserId,
       aspnet_Users.UserName,
       -- The NormalizedUserName value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Users.UserName),
       -- Creates an empty password since passwords don't map between the 2 schemas
       '',
       /*
        The SecurityStamp token is used to verify the state of an account and
        is subject to change at any time. It should be initialized as a new ID.
       */
       NewID(),
       /*
        EmailConfirmed is set when a new user is created and confirmed via email.
        Users must have this set during migration to reset passwords.
       */
       1,
       aspnet_Users.MobileAlias,
       CASE
         WHEN aspnet_Users.MobileAlias IS NULL THEN 0
         ELSE 1
       END,
       -- 2FA likely wasn't setup in Membership for users, so setting as false.
       0,
       CASE
         -- Setting lockout date to time in the future (1,000 years)
         WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
                                                     Sysutcdatetime())
         ELSE NULL
       END,
       aspnet_Membership.IsLockedOut,
       /*
        AccessFailedAccount is used to track failed logins. This is stored in
        Membership in multiple columns. Setting to 0 arbitrarily.
       */
       0,
       aspnet_Membership.Email,
       -- The NormalizedEmail value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Membership.Email)
FROM   aspnet_Users
       LEFT OUTER JOIN aspnet_Membership
                    ON aspnet_Membership.ApplicationId =
                       aspnet_Users.ApplicationId
                       AND aspnet_Users.UserId = aspnet_Membership.UserId
       LEFT OUTER JOIN CoreIdentitySample.dbo.AspNetUsers
                    ON aspnet_Membership.UserId = AspNetUsers.Id
WHERE  AspNetUsers.Id IS NULL

-- INSERT ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetRoles(Id, Name)
SELECT RoleId, RoleName
FROM aspnet_Roles;

-- INSERT USER ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetUserRoles(UserId, RoleId)
SELECT UserId, RoleId
FROM aspnet_UsersInRoles;

IF @@ERROR <> 0
  BEGIN
    ROLLBACK TRANSACTION MigrateUsersAndRoles
    RETURN
  END

COMMIT TRANSACTION MigrateUsersAndRoles

完成上述指令碼之後,先前建立的 ASP.NET Core Identity 應用程式會填入成員資格使用者。 使用者必須先變更其密碼,才能登入。

注意

如果成員系統中有使用者的使用者名稱與其電子郵件地址不匹配,則需要對先前建立的應用程式進行變更以適應此更改。 預設範本預期 UserNameEmail 是相同的。 若是不同的情況,則必須修改登入程式才能使用 UserName,而不是 Email

在登入頁面的 PageModel 中,從 Email 屬性中移除[EmailAddress] 屬性。 將其重新命名為 UserName。 這需要在 ViewPageModel 中提及 EmailAddress 的位置進行變更。 結果如下所示:

已修正登入

下一步

在本教學課程中,您已了解如何將使用者從 SQL 成員資格移植到 ASP.NET Core 2.0 Identity。 如需 ASP.NET Core Identity 的詳細資訊,請參閱 Identity 簡介