기존 웹 사이트를 SQL 멤버 자격에서 ASP.NET Identity로 마이그레이션

작성자 : Rick Anderson, Suhas Joshi

이 자습서에서는 SQL 멤버 자격을 사용하여 만든 사용자 및 역할 데이터를 사용하여 기존 웹 애플리케이션을 새 ASP.NET ID 시스템으로 마이그레이션하는 단계를 보여 줍니다. 이 접근 방식에는 기존 데이터베이스 스키마를 ASP.NET ID에 필요한 스키마로 변경하고 이전/새 클래스에 후크를 지정하는 작업이 포함됩니다. 이 방법을 채택한 후 데이터베이스가 마이그레이션되면 향후 ID 업데이트가 쉽게 처리됩니다.

이 자습서에서는 Visual Studio 2010을 사용하여 만든 웹 애플리케이션 템플릿(Web Forms)을 사용하여 사용자 및 역할 데이터를 만듭니다. 그런 다음 SQL 스크립트를 사용하여 기존 데이터베이스를 ID 시스템에 필요한 테이블로 마이그레이션합니다. 다음으로 필요한 NuGet 패키지를 설치하고 멤버 자격 관리에 ID 시스템을 사용하는 새 계정 관리 페이지를 추가합니다. 마이그레이션 테스트로 SQL 멤버 자격을 사용하여 만든 사용자는 로그인할 수 있어야 하며 새 사용자는 등록할 수 있어야 합니다. 전체 샘플은 여기에서 찾을 수 있습니다. ASP.NET 멤버 자격에서 ASP.NET ID로 마이그레이션도 참조하세요.

시작

SQL 멤버 자격을 사용하여 애플리케이션 만들기

  1. SQL 멤버 자격을 사용하고 사용자 및 역할 데이터가 있는 기존 애플리케이션으로 시작해야 합니다. 이 문서의 목적을 위해 Visual Studio 2010에서 웹 애플리케이션을 만들어 보겠습니다.

    Visual Studio 2010에서 웹 애플리케이션을 만드는 스크린샷

  2. ASP.NET 구성 도구를 사용하여 2개의 사용자(oldAdminUseroldUser)를 만듭니다.

    A SP 의 스크린샷 N E T 구성 도구 - 두 명의 사용자를 만듭니다.

    모든 보안을 관리하는 웹 사이트 관리 도구의 스크린샷

  3. 관리 라는 역할을 만들고 해당 역할의 사용자로 'oldAdminUser'를 추가합니다.

    해당 역할에서 사용자로 만든 관리 역할의 스크린샷

  4. Default.aspx를 사용하여 사이트의 관리 섹션을 만듭니다. web.config 파일에서 권한 부여 태그를 설정하여 관리 역할의 사용자만 액세스할 수 있도록 합니다. 자세한 내용은 여기를 참조하세요. https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    사이트의 관리 섹션 스크린샷

  5. 서버 Explorer 데이터베이스를 확인하여 SQL 멤버 자격 시스템에서 만든 테이블을 이해합니다. 사용자 로그인 데이터는 aspnet_Users 및 aspnet_Membership 테이블에 저장되고 역할 데이터는 aspnet_Roles 테이블에 저장됩니다. 역할이 aspnet_UsersInRoles 테이블에 저장되는 사용자에 대한 정보입니다. 기본 멤버 자격 관리의 경우 위의 테이블의 정보를 ASP.NET ID 시스템으로 이식하는 것으로 충분합니다.

    S QL 멤버 자격 시스템에서 만든 테이블을 이해하기 위해 데이터베이스를 보는 서버 Explorer 스크린샷

Visual Studio 2013 마이그레이션

  1. 최신 업데이트와 함께 웹용 Visual Studio Express 2013 또는 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. 서버 Explorer 열고 테이블 스키마 및 데이터를 관찰할 수 있는지 확인합니다.

  5. ASP.NET ID 시스템은 프레임워크 버전 4.5 이상에서 작동합니다. 애플리케이션의 대상을 4.5 이상으로 다시 지정합니다.

    A SP 의 스크린샷 N E T ID 시스템.

    프로젝트를 빌드하여 오류가 없는지 확인합니다.

Nuget 패키지 설치

  1. 솔루션 탐색기 NuGet 패키지 관리 프로젝트를 > 마우스 오른쪽 단추로 클릭합니다. 검색 상자에 "Asp.net ID"를 입력합니다. 결과 목록에서 패키지를 선택하고 설치를 클릭합니다. "동의함" 단추를 클릭하여 사용권 계약에 동의합니다. 이 패키지는 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 패키지를 설치하는 스크린샷

데이터베이스를 새 ID 시스템으로 마이그레이션

다음 단계는 기존 데이터베이스를 ASP.NET ID 시스템에 필요한 스키마로 마이그레이션하는 것입니다. 이를 위해 새 테이블을 만들고 기존 사용자 정보를 새 테이블로 마이그레이션하는 명령 집합이 있는 SQL 스크립트를 실행합니다. 스크립트 파일은 여기에서 찾을 수 있습니다.

이 스크립트 파일은 이 샘플과 관련이 있습니다. SQL 멤버 자격을 사용하여 만든 테이블에 대한 스키마를 사용자 지정하거나 수정한 경우 스크립트를 적절하게 변경해야 합니다.

스키마 마이그레이션을 위한 SQL 스크립트를 생성하는 방법

ASP.NET ID 클래스가 기존 사용자의 데이터로 기본적으로 작동하려면 데이터베이스 스키마를 ASP.NET ID에 필요한 것으로 마이그레이션해야 합니다. 새 테이블을 추가하고 기존 정보를 해당 테이블에 복사하여 이 작업을 수행할 수 있습니다. 기본적으로 ASP.NET ID는 EntityFramework를 사용하여 ID 모델 클래스를 데이터베이스에 다시 매핑하여 정보를 저장/검색합니다. 이러한 모델 클래스는 사용자 및 역할 개체를 정의하는 핵심 ID 인터페이스를 구현합니다. 데이터베이스의 테이블과 열은 이러한 모델 클래스를 기반으로 합니다. Identity v2.1.0의 EntityFramework 모델 클래스 및 해당 속성은 아래에 정의되어 있습니다.

IdentityUser Type IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
Id 문자열 Id RoleId ProviderKey Id
사용자 이름 문자열 이름 UserId UserId ClaimType
PasswordHash 문자열 LoginProvider ClaimValue
SecurityStamp 문자열 User_id
메일 문자열
EmailConfirmed bool
PhoneNumber 문자열
PhoneNumberConfirmed bool
LockoutEnabled bool
LockoutEndDate DateTime
AccessFailedCount int

속성에 해당하는 열이 있는 이러한 각 모델에 대한 테이블이 있어야 합니다. 클래스와 테이블 간의 매핑은 의 IdentityDBContext메서드에 OnModelCreating 정의되어 있습니다. 이를 구성의 흐름 API 메서드라고 하며 자세한 내용은 여기에서 확인할 수 있습니다. 클래스에 대한 구성은 아래와 같습니다.

클래스 테이블 기본 키 외래 키
IdentityUser AspnetUsers Id
IdentityRole AspnetRoles Id
IdentityUserRole AspnetUserRole UserId + RoleId User_Id-AspnetUsers> RoleId-AspnetRoles>
IdentityUserLogin AspnetUserLogins ProviderKey+UserId + LoginProvider UserId-AspnetUsers>
IdentityUserClaim AspnetUserClaims Id User_Id-AspnetUsers>

이 정보를 사용하여 SQL 문을 만들어 새 테이블을 만들 수 있습니다. 각 문을 개별적으로 작성하거나 EntityFramework PowerShell 명령을 사용하여 전체 스크립트를 생성한 다음 필요에 따라 편집할 수 있습니다. 이렇게 하려면 VS에서 보기 또는 도구 메뉴에서 패키지 관리자 콘솔을 엽니다.

  • "Enable-Migrations" 명령을 실행하여 EntityFramework 마이그레이션을 사용하도록 설정합니다.
  • C#/VB에서 데이터베이스를 만드는 초기 설정 코드를 만드는 "추가 마이그레이션 초기" 명령을 실행합니다.
  • 마지막 단계는 모델 클래스를 기반으로 SQL 스크립트를 생성하는 "Update-Database –Script" 명령을 실행하는 것입니다.

앱이 SQLite를 ID 데이터 저장소로 사용하는 경우 일부 명령은 지원되지 않습니다. 데이터베이스 엔진 Alter 의 제한으로 인해 명령은 다음 예외를 throw합니다.

"System.NotSupportedException: SQLite는 이 마이그레이션 작업을 지원하지 않습니다."

해결 방법에서는 데이터베이스에서 Code First 마이그레이션을 실행하여 테이블을 변경합니다.

이 데이터베이스 생성 스크립트는 새 열을 추가하고 데이터를 복사하기 위해 추가로 변경하는 시작으로 사용할 수 있습니다. 이 장점은 이후 버전의 IDENTITY 릴리스에 대해 모델 클래스가 변경될 때 EntityFramework에서 데이터베이스 스키마를 수정하는 데 사용되는 테이블을 생성 _MigrationHistory 한다는 것입니다.

SQL 멤버 자격 사용자 정보에는 ID 사용자 모델 클래스(예: 이메일, 암호 시도, 마지막 로그인 날짜, 마지막 잠금 날짜 등)에 있는 속성 외에 다른 속성이 있습니다. 이는 유용한 정보이며 ID 시스템으로 이 정보를 전달하려고 합니다. 이 작업은 사용자 모델에 추가 속성을 추가하고 데이터베이스의 테이블 열에 다시 매핑하여 수행할 수 있습니다. 모델을 서브클래스하는 클래스를 추가하여 이 작업을 수행할 수 있습니다 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 멤버 자격 데이터베이스의 기존 정보를 ID에 대해 새로 추가된 테이블로 복사해야 합니다. 이 작업은 SQL을 통해 한 테이블에서 다른 테이블로 직접 데이터를 복사하여 수행할 수 있습니다. 테이블 행에 데이터를 추가하려면 구문을 사용합니다 INSERT INTO [Table] . 다른 테이블에서 복사하려면 문과 함께 SELECT 문을 사용할 INSERT INTO 수 있습니다. 모든 사용자 정보를 얻으려면 aspnet_Users 쿼리하고 테이블을 aspnet_Membership데이터를 AspNetUsers 테이블에 복사해야 합니다. 및 SELECT 를 및 LEFT OUTER JOIN 문과 함께 JOIN 사용합니다INSERT INTO. 테이블 간에 데이터를 쿼리하고 복사하는 방법에 대한 자세한 내용은 링크를 참조하세요. 또한 기본적으로 이 에 매핑되는 SQL 멤버 자격에 정보가 없으므로 AspnetUserLogins 및 AspnetUserClaims 테이블은 먼저 비어 있습니다. 복사된 유일한 정보는 사용자 및 역할에 대한 것입니다. 이전 단계에서 만든 프로젝트의 경우 사용자 테이블에 정보를 복사하는 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'을 사용했기 때문에 ID로 암호를 해독하는 데 사용할 수 있도록 해시된 암호와 함께 복사합니다. 사용자 지정 암호 해시를 연결할 때 문서에서 자세히 설명합니다.

이 스크립트 파일은 이 샘플과 관련이 있습니다. 추가 테이블이 있는 애플리케이션의 경우 개발자는 유사한 접근 방식을 따라 사용자 모델 클래스에 속성을 추가하고 AspnetUsers 테이블의 열에 매핑할 수 있습니다. 스크립트를 실행하려면

  1. 서버 탐색기를 엽니다. 'ApplicationServices' 연결을 확장하여 테이블을 표시합니다. 테이블 노드를 마우스 오른쪽 단추로 클릭하고 '새 쿼리' 옵션을 선택합니다.

    테이블을 표시하기 위해 Application Services 연결을 확장하는 Open Server Explorer 스크린샷

  2. 쿼리 창에서 Migrations.sql 파일에서 전체 SQL 스크립트를 복사하여 붙여넣습니다. '실행' 화살표 단추를 눌러 스크립트 파일을 실행합니다.

    전체 S QL 스크립트를 복사하여 붙여넣는 쿼리 창의 스크린샷

    서버 Explorer 창을 새로 고칩니다. 데이터베이스에 5개의 새 테이블이 만들어집니다.

    서버 Explorer 창을 새로 고쳐 데이터베이스에 5개의 새 테이블을 만드는 스크린샷

    데이터 연결의 테이블 스크린샷

    다음은 SQL 멤버 자격 테이블의 정보를 새 ID 시스템에 매핑하는 방법입니다.

    aspnet_Roles --> AspNetRoles

    asp_netUsers 및 asp_netMembership --> AspNetUsers

    aspnet_UserInRoles --> AspNetUserRoles

    위의 섹션에 설명된 대로 AspNetUserClaims 및 AspNetUserLogins 테이블은 비어 있습니다. AspNetUser 테이블의 '판별자' 필드는 다음 단계로 정의된 모델 클래스 이름과 일치해야 합니다. 또한 PasswordHash 열은 '암호화된 암호 |암호 솔트|암호 형식' 형식입니다. 이렇게 하면 이전 암호를 다시 사용할 수 있도록 특수 SQL 멤버 자격 암호화 논리를 사용할 수 있습니다. 이 내용은 문서의 뒷부분에 설명되어 있습니다.

모델 및 멤버 자격 페이지 만들기

앞에서 설명한 것처럼 ID 기능은 Entity Framework를 사용하여 기본적으로 계정 정보를 저장하기 위해 데이터베이스와 통신합니다. 테이블의 기존 데이터를 사용하려면 테이블에 다시 매핑하고 ID 시스템에 연결하는 모델 클래스를 만들어야 합니다. ID 계약의 일부로 모델 클래스는 Identity.Core dll에 정의된 인터페이스를 구현하거나 Microsoft.AspNet.Identity.EntityFramework에서 사용할 수 있는 이러한 인터페이스의 기존 구현을 확장할 수 있습니다.

이 샘플에서 AspNetRoles, AspNetUserClaims, AspNetLogins 및 AspNetUserRole 테이블에는 ID 시스템의 기존 구현과 유사한 열이 있습니다. 따라서 기존 클래스를 다시 사용하여 이러한 테이블에 매핑할 수 있습니다. AspNetUser 테이블에는 SQL 멤버 자격 테이블의 추가 정보를 저장하는 데 사용되는 몇 가지 추가 열이 있습니다. 'IdentityUser'의 기존 구현을 확장하고 추가 속성을 추가하는 모델 클래스를 만들어 매핑할 수 있습니다.

  1. 프로젝트에서 Models 폴더를 만들고 사용자 클래스를 추가합니다. 클래스의 이름은 'AspnetUsers' 테이블의 '판별자' 열에 추가된 데이터와 일치해야 합니다.

    프로젝트에서 Models 폴더를 만들고 사용자 클래스를 추가하는 스크린샷

    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은 ID 테이블과 상호 작용하여 정보를 검색하고 저장하는 IdentityDbContext 클래스를 정의합니다. IdentityDbContext<튜저> 는 IdentityUser 클래스를 확장하는 모든 클래스일 수 있는 'TUser' 클래스를 사용합니다.

    1단계에서 만든 'User' 클래스를 전달하여 'Models' 폴더 아래에 IdentityDbContext를 확장하는 새 클래스 ApplicationDBContext를 만듭니다.

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. 새 ID 시스템의 사용자 관리는 Microsoft.AspNet.Identity.EntityFramework dll에 정의된 UserManager<tuser> 클래스를 사용하여 수행됩니다. 1단계에서 만든 'User' 클래스를 전달하여 UserManager를 확장하는 사용자 지정 클래스를 만들어야 합니다.

    Models 폴더에서 UserManager 사용자를 확장하는 새 클래스 UserManager<를 만듭니다.>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. 애플리케이션 사용자의 암호는 암호화되어 데이터베이스에 저장됩니다. SQL 멤버 자격에 사용되는 암호화 알고리즘은 새 ID 시스템의 암호화 알고리즘과 다릅니다. 이전 암호를 다시 사용하려면 이전 사용자가 새 사용자에 대한 ID의 암호화 알고리즘을 사용하는 동안 SQL 멤버 자격 알고리즘을 사용하여 로그인할 때 암호를 선택적으로 해독해야 합니다.

    UserManager 클래스에는 'IPasswordHasher' 인터페이스를 구현하는 클래스의 instance 저장하는 'PasswordHasher' 속성이 있습니다. 사용자 인증 트랜잭션 중에 암호를 암호화/암호 해독하는 데 사용됩니다. 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에서 가져옵니다. 이전 앱이 사용자 지정 구현을 사용한 경우 여기에 반영되어야 합니다. EncodePassword 메서드를 사용하여 지정된 암호를 해시하거나 데이터베이스에 있는 암호를 사용하여 일반 텍스트 암호를 확인하는 두 가지 다른 메서드 HashPasswordVerifyHashedPassword를 정의해야 합니다.

    SQL 멤버 자격 시스템은 PasswordHash, PasswordSalt 및 PasswordFormat을 사용하여 사용자가 암호를 등록하거나 변경할 때 입력한 암호를 해시했습니다. 마이그레이션하는 동안 세 필드는 모두 '|' 문자로 구분된 AspNetUser 테이블의 PasswordHash 열에 저장됩니다. 사용자가 로그인하고 암호에 이러한 필드가 있는 경우 SQL 멤버 자격 암호화를 사용하여 암호를 검사. 그렇지 않으면 ID 시스템의 기본 암호화를 사용하여 암호를 확인합니다. 이렇게 하면 앱이 마이그레이션되면 이전 사용자가 암호를 변경할 필요가 없습니다.

  5. UserManager 클래스에 대한 생성자를 선언하고 이를 SQLPasswordHasher로 생성자의 속성에 전달합니다.

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

새 계정 관리 페이지 만들기

마이그레이션의 다음 단계는 사용자가 등록하고 로그인할 수 있도록 하는 계정 관리 페이지를 추가하는 것입니다. SQL 멤버 자격의 이전 계정 페이지는 새 ID 시스템에서 작동하지 않는 컨트롤을 사용합니다. 새 사용자 관리 페이지를 추가하려면 프로젝트를 이미 만들고 NuGet 패키지를 추가했으므로 '애플리케이션에 사용자를 등록하기 위한 Web Forms 추가' 단계에서 시작하는 이 링크 https://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project 의 자습서를 따릅니다.

샘플이 여기에 있는 프로젝트와 함께 작동하려면 몇 가지 변경이 필요합니다.

  • Register.aspx.cs 및 Login.aspx.cs 클래스 뒤에 있는 코드는 ID 패키지의 를 사용하여 UserManager 사용자를 만듭니다. 이 예제에서는 앞에서 설명한 단계에 따라 Models 폴더에 추가된 UserManager를 사용합니다.

  • Register.aspx.cs의 IdentityUser 대신 만든 User 클래스와 클래스 뒤에 있는 Login.aspx.cs 코드를 사용합니다. 이렇게 하면 사용자 지정 사용자 클래스가 ID 시스템에 연결됩니다.

  • 데이터베이스를 만들 파트를 건너뛸 수 있습니다.

  • 개발자는 새 사용자가 현재 애플리케이션 ID와 일치하도록 ApplicationId를 설정해야 합니다. 이 작업은 Register.aspx.cs 클래스에서 사용자 개체를 만들기 전에 이 애플리케이션에 대한 ApplicationId를 쿼리하고 사용자를 만들기 전에 설정하여 수행할 수 있습니다.

    예제:

    Register.aspx.cs 페이지에서 메서드를 정의하여 aspnet_Applications 테이블을 쿼리하고 애플리케이션 이름에 따라 애플리케이션 ID를 가져옵니다.

    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, …};
    

기존 사용자 이름 및 암호를 사용하여 기존 사용자를 로그인합니다. 등록 페이지를 사용하여 새 사용자를 만듭니다. 또한 사용자가 예상대로 역할에 있는지 확인합니다.

ID 시스템으로 포팅하면 사용자가 OAuth(Open Authentication)를 애플리케이션에 추가할 수 있습니다. OAuth가 사용하도록 설정된 샘플은 여기 를 참조하세요.

다음 단계

이 자습서에서는 사용자를 SQL 멤버 자격에서 ASP.NET ID로 이식하는 방법을 보여 주었지만 프로필 데이터를 포트하지는 않았습니다. 다음 자습서에서는 SQL 멤버 자격에서 새 ID 시스템으로 프로필 데이터를 포팅하는 방법을 살펴보겠습니다.

이 문서의 맨 아래에 피드백을 남길 수 있습니다.

기사를 검토해 주신 톰 다이크스트라와 릭 앤더슨에게 감사드립니다.