본문 바로가기

Asp.net (C#.net)

Asp.Net Core 3.1 개별 사용자 계정 (4. Identity Id 컬럼 nvarchar를 int로 변경)

연재글 Asp.Net Core 3.1 개별사용자 계정

2020/03/26 - [Asp.net (C#.net)] - Asp.Net Core 3.1 개별 사용자 계정 (1. 기능 재정의(복원))

2020/04/06 - [Asp.net (C#.net)] - Asp.Net Core 3.1 개별 사용자 계정 (2. 패스워드 정책 변경)

2020/04/07 - [Asp.net (C#.net)] - Asp.Net Core 3.1 개별 사용자 계정 (3. 가입 시 이메일 인증 처리)

2020/04/09 - [Asp.net (C#.net)] - Asp.Net Core 3.1 개별 사용자 계정 (4. Identity Id 컬럼 nvarchar를 int로 변경)

 

 

Visual Studio Community 2019

Asp.Net Core 3.1

Microsoft.AspNetCore.Identity.EntityFrameworkCore 3.1.2 (패키지)

 

 

제발 개별 사용자 계정 사용할때 Id Column 타입이 선택 가능하거나 한방에 바꿀 수 있었으면 좋겠다.

Core를 중간에 안썼더니..

적응하기가 힘들더군요.

특이 이거 바꾸고 설정했더니 여기저기 에러나고...

하나씩 잡으려니 짜증..ㅡ_ㅡ;;

기본적으로 하위 버전의 코어 버전에서는 AccountController가 생성되고 그에 따른 DbContext와 모델이 준비가 되어있었는데 이게 없어지고 변화가되어서 바뀐 부분들은 최대한 정보 취합으로 새로운 방법대로 생성하고 나머지 부족한 부분들은 기존의 형태를 복원해서 작업했습니다.

이게 최선의 방법이 아닐수도 있습니다.

 

 

1. 일단은 데이터베이스를 가볍게 드롭 시켜주고 시작하자. 이미 자료가 들어가 있으면 작업이 용이하지않다. 최초 프로젝트 생성 시 작업한는 걸 추천한다. 만약 이미 사용중인 Db에 적용하려면 미리 컬럼명 수동으로 변경하고 그 안의 문자열 Id값을 인트로 바로 인지하도록 숫자 문자로 바꾸고 컬럼 변경해서 저장하고 연결된 부분도 똑같이 작동하게 다 수정하신 후에 작업하시면 적용을 되실것 같은데 MVC 버전에서는 해봤는데 Core버전에서는 이렇게 사용해본적은 없네요.

도구 - NuGet 패키지 관리 - 패키지 관리자 콘솔을 실행한다.

 

 

2. 패키지 관리자 콘솔에서 아래 그림처럼 명령어를 실행해주자. 잘가라 데이터베이스~

 

PM> Drop-Database

 

 

3. 명령을 실행시키면 진짜 지울래? 라고 물어봅니다. 가볍게 엔터 쳐 줍니다.

 

 

4. 이제는 마이그레이션을 지워줍니다. 똑같이 패키지 관리자 콘솔 창에서 명령어를 쳐 줍니다.

 

PM> Remove-Migration

 

 

5. 마이그레이션 폴더가 깨끗해진걸 확인합니다. 혹시 남은게 있다면 다시 수행해주셔야 합니다. 특히 ApplicationDbContextModelSnapshot.cs가 보이시면 안됩니다.

 

 

 

7. ModelsApplicationUser.cs 추가. 일단 모델 폴더로 가서 마우스 오른쪽 버튼으로 메뉴를 호출한 후에 추가 메뉴에 클래스를 선택해 준다.

 

 

 

8. 새 항목 추가 창이 뜨면 클래스를 선택하고 파일명을 ApplicationUser.cs로 해주고 추가를 눌러준다.

 

 

 

9. ApplicationUser.cs 파일을 아래의 코드처럼 작성해준다. IdentityUser를 사용하려면 using Microsoft.AspNetCore.Identity 가 필요하다. 그리고 우리는 Id 컬럼 타입을 int로 할 예정이기 때문에 <int>를 붙여준다.

 

/Models/ApplicationUser.cs


using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace CoreAuth.Models
{
	public class ApplicationUser : IdentityUser<int>
	{
	}
}

 

 

10.  다음은 /Data/ApplicationDbContext.cs를 아래와같이 변경해준다. 그리고 주석 처리한 부분을 보시면 대충 감 잡으시겠지만 저기가 Identity 테이블에 컬럼을 추가하거나 등 수정하고자할때 이용하는곳입니다.

 

/Data/ApplicationDbContext


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace CoreAuth.Data
{
	public class ApplicationDbContext : IdentityDbContext<Models.ApplicationUser, IdentityRole<int>, int>
	{
		public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
			: base(options)
		{
		}

		protected override void OnModelCreating(ModelBuilder builder)
		{
			base.OnModelCreating(builder);
			// Customize the ASP.NET Identity model and override the defaults if needed.
			// For example, you can rename the ASP.NET Identity table names and more.
			// Add your customizations after calling base.OnModelCreating(builder);
		}
	}
}

 

 

11. Startup.cs 수정입니다. 일부 코드를 올렸는데 저 중에 주석으로 아래 부분을 수정해주셔야 합니다.라고 적힌 부분 아래의 코드처럼 수정해주시면 됩니다.

 

Startup.cs


// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
	services.AddDbContext<ApplicationDbContext>(options =>
		options.UseSqlServer(
			Configuration.GetConnectionString("DefaultConnection")));

	// 아래 부부분을 수정해주셔야 합니다.
	services.AddIdentity<Models.ApplicationUser, IdentityRole<int>>(options => options.SignIn.RequireConfirmedAccount = false)
		.AddEntityFrameworkStores<ApplicationDbContext>()
		.AddDefaultUI()
		.AddDefaultTokenProviders();

	services.Configure<IdentityOptions>(options =>
	{
		// Default Password settings.
		options.Password.RequireDigit = false;				// 숫자 필수 여부
		options.Password.RequireLowercase = false;			// 소문자 필수 여부
		options.Password.RequireNonAlphanumeric = false;		// 알파벳 아닌 문자 여부
		options.Password.RequireUppercase = false;			// 대문자 필수 여부
		options.Password.RequiredLength = 6;				// 최소 길이
		options.Password.RequiredUniqueChars = 0;			// 특수문자 필요 갯수
	});

	services.AddControllersWithViews();
	services.AddRazorPages();
}

 

 

12. 큰 작업은 끝났으나 소소한 작업이 좀 남았습니다. 우리는 MVC 개별 사용자 계정의 템플릿으로 프로젝트를 생성했습니다. /Views/Shared/_LoginPartial.cshtml 파일을 열어봅니다. 그러면 IdentityUser 을 사용하고 있는것이 보이실텐데 우리는 이 부분을 ApplicationUser로 변경해서 사용하는 형태를 취하고 있습니다. 이 부분을 수정해줘야 합니다. 아래처럼 변경해 주십시오.

 

/Views/Shared/_LoginPartial.cshtml


@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

 

 

13. 이제 수정할 부분은 ID 스캐폴드 작업으로 생성된 페이지들 입니다. 저는 일단 등록과 로그인 페이지만 수정할 예정입니다. 추가적으로 더 필요한 페이지가 있으신분들은 응용하면 어렵지않게 변경 가능하실겁니다. 우선은 등록 페이지부터 수정하겠습니다. /Areas/Identity/Pages/Account/Register.cshtml.cs 파일입니다. .cs 파일은 .cshtml 파일 밑에 있습니다. 헷갈리시면 안됩니다. 수정한곳은 몇군데 안되지만 위치상 코드를 전체 첨부합니다.

기본적으로 IdentityUser 라는 문자열을 Models.ApplicationUser 로 변경하신다고 보시면 됩니다.

 

/Areas/Identity/Pages/Account/Register.cshtml.cs


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;

namespace CoreAuth.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<Models.ApplicationUser> _signInManager;
        private readonly UserManager<Models.ApplicationUser> _userManager;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

        public RegisterModel(
            UserManager<Models.ApplicationUser> userManager,
            SignInManager<Models.ApplicationUser> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        [BindProperty]
        public InputModel Input { get; set; }

        public string ReturnUrl { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = new Models.ApplicationUser { UserName = Input.Email, Email = Input.Email };
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

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

 

 

14. 이번에는 /Areas/Identity/Pages/Account/Login.cshtml.cs 을 수정할 차례입니다. 기본 방법은 위와 똑같습니다. IdentityUser 라는 문자열을 Models.ApplicationUser 로 변경합니다.

 

/Areas/Identity/Pages/Account/Login.cshtml.cs


[AllowAnonymous]
public class LoginModel : PageModel
{
    private readonly UserManager<Models.ApplicationUser> _userManager;
    private readonly SignInManager<Models.ApplicationUser> _signInManager;
    private readonly ILogger<LoginModel> _logger;

    public LoginModel(SignInManager<Models.ApplicationUser> signInManager, 
        ILogger<LoginModel> logger,
        UserManager<Models.ApplicationUser> userManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
    }
    
    ...중략...
    
}

 

 

15. 작업이 완료되었습니다. 이제 패키지 관리자 콘솔에서 마이그레이션을 추가해줍니다.

 

PM> Add-Migration [원하는이름]

Ex>
PM> Add-Migration Id_Type_Change_Int32

 

 

16. 마이그레이션이 잘 생성되었는지 확인합니다. 우리가 생성한 마이그레이션과 추가적으로 ApplicationDbContextModelSnapshot.cs가 생성된걸 확인하시면 됩니다.

 

 

 

17. 이제는 우리가 이제껏 작업한것을 Db에 적용을 합니다. 패키지 관리자 콘솔에서 아래와 같이 명령을 줍니다.

 

PM> Update-Database

 

 

18. 아래처럼 Done.이라고 나오면 정상적으로 처리된겁니다.

 

 

 

19. 실행을 시키고 창이 뜨면 Register를 누릅니다.

 

 

 

20. EmailPasswordConfirm password를 제대로 입력하고 Register를 눌러서 가입을 완료해줍니다.

 

 

 

21. 현재 저는 이메일 컨펌으로 가입되는 설정이 아니라서 바로 가입되었습니다. 상단 오른쪽을 보시면 환영 메시지와 함께 로그아웃 버튼이 활성화되었습니다. 정상적으로 가입되고 로그인 되었다는 뜻이지요. 종료하게 다시 비쥬어 스튜디오로 갑니다.

 

 

 

22. 메뉴에서 보기 - SQL Server 개체 탐색기 창을 띄웁니다.

 

 

 

23. SQL Server 개체 탐색기 창에서 AspNetUsers 테이블을 찾아서 마우스 오른쪽으로 메뉴 호출 후 데이터 보기를 하시면 방금 생성된 계정 정보를 확인할 수 있습니다. Id 컬럼에 값이 숫자가 들어간것이 보일것입니다.

 

 

 

24. SQL Server 개체 탐색기 창에서 AspNetUsers 테이블을 확장시켜서 열을 확장해보시면 Id 컬럼이 PK이며 int 타입이며 널을 허용하지않는다고 되어있습니다. 정상적으로 변경이 된것이 확인이 되는 부분입니다.

 

 

 

모든 작업이 완료되었습니다.^^