ASP.NET Core 쿠키 인증 구현 및 구성

ASP.NET Core에서 쿠키 기반 인증은 사용자 세션을 관리하고 접근 권한을 제어하는 일반적인 방법 중 하나입니다. 쿠키 인증 시스템을 효과적으로 활용하기 위해서는 몇 가지 핵심 구성 요소를 이해하고 적절히 설정해야 합니다.

쿠키 인증의 주요 경로 설정

쿠키 인증을 구성할 때 다음 네 가지 주요 경로를 정의할 수 있습니다. 이는 사용자의 인증 상태 변화에 따라 리디렉션될 위치를 지정합니다.

  • ReturnUrl: 사용자가 이전에 접근하려던 보호된 리소스의 경로입니다. 로그인 성공 후 이 경로로 리디렉션됩니다.
  • LoginPath: 사용자가 로그인해야 할 때 리디렉션되는 로그인 페이지의 경로입니다. 기본값은 /Account/Login입니다.
  • LogoutPath: 사용자가 로그아웃할 때 리디렉션되는 경로입니다.
  • AccessDeniedPath: 사용자가 특정 리소스에 접근할 권한이 없을 때 리디렉션되는 접근 거부 페이지의 경로입니다. 기본값은 /Account/AccessDenied입니다.

이러한 경로들은 CookieAuthenticationOptions를 통해 설정할 수 있습니다.

인증 스키마 선언의 중요성

쿠키 기반 인증을 올바르게 작동시키기 위해서는 CookieAuthenticationDefaults.AuthenticationScheme을 명확하게 선언하는 것이 필수적입니다. 이는 시스템에 어떤 종류의 인증 메커니즘을 사용할 것인지 알려주는 역할을 합니다.


// ClaimsIdentity 생성 시 인증 스키마 지정
ClaimsIdentity identity = new ClaimsIdentity(userClaims, CookieAuthenticationDefaults.AuthenticationScheme);

// 사용자 로그인 시 인증 스키마 지정
await _httpContextAccessor.HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(identity),
    authProperties
);

사용자 로그인 처리 방법

ASP.NET Core에서 사용자를 로그인시키는 방법은 크게 세 가지가 있습니다. 각 방법은 상황에 따라 장단점이 있습니다.

  1. ControllerBase.SignIn 메서드 활용:

    ControllerBase에 내장된 SignIn 메서드를 사용하는 방법입니다. 이 메서드는 SignInResult를 반환하며, 실제 인증 작업을 수행하기 위해 ExecuteResultAsync를 호출해야 합니다.

    
    Microsoft.AspNetCore.Mvc.SignInResult authResult =
        base.SignIn(new ClaimsPrincipal(userIdentity),
            new AuthenticationProperties { IsPersistent = true },
            CookieAuthenticationDefaults.AuthenticationScheme);
    
    // 실제 인증 작업을 실행
    await authResult.ExecuteResultAsync(base.ControllerContext);
    
  2. HttpContext.SignInAsync 메서드 직접 호출:

    Controller에서 직접 HttpContext를 통해 SignInAsync를 호출하는 방법입니다. 이 방법은 편리하지만, 비동기 다중 스레드 환경에서 잠재적인 스레드 안전성 문제를 일으킬 수 있습니다.

    
    // HttpContext는 스레드 안전하지 않을 수 있음
    await HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(userIdentity),
        new AuthenticationProperties { IsPersistent = true }
    );
    
  3. IHttpContextAccessor 주입을 통한 SignInAsync 호출:

    IHttpContextAccessor 인터페이스를 주입받아 HttpContext에 접근하는 방법입니다. 이 방법은 다중 스레드 환경에서 안전하게 작동하도록 설계되어 가장 권장되는 방식입니다.

    
    // IHttpContextAccessor를 통한 접근은 다중 스레드 환경에서 안전함
    await _httpContextAccessor.HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(userIdentity),
        new AuthenticationProperties { IsPersistent = true }
    );
    

기본적인 쿠키 인증 설정

ASP.NET Core 애플리케이션에서 쿠키 인증을 활성화하려면 Program.cs (또는 Startup.cs) 파일에 다음과 같이 서비스를 추가해야 합니다.


builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
       .AddCookie();

인증 미들웨어 활성화

인증 기능을 실제로 사용하려면 파이프라인에 인증 미들웨어를 추가해야 합니다. 이 미들웨어는 각 HTTP 요청에 대해 인증을 처리합니다.


app.UseAuthentication();
app.UseAuthorization(); // 인증 후 권한 부여를 위해 일반적으로 함께 사용

세부적인 쿠키 인증 옵션 구성

AddCookie 메서드에 옵션 람다를 전달하여 쿠키의 이름, 경로, 만료 시간 등 다양한 설정을 커스터마이징할 수 있습니다.


builder.Services.AddAuthentication(options =>
{
    // 기본 인증 스키마를 쿠키 인증으로 설정
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    // 쿠키 이름 설정
    options.Cookie.Name = "MyAppAuthCookie";
    // 접근 거부 시 리디렉션 경로
    options.AccessDeniedPath = "/Forbidden";
    // 로그인 페이지 경로
    options.LoginPath = "/Auth/SignIn";
    // 로그아웃 페이지 경로
    options.LogoutPath = "/Auth/SignOut";

    // 쿠키의 절대 만료 시간 (20분)
    options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
    // 슬라이딩 만료 활성화 (요청 시 만료 시간 연장)
    options.SlidingExpiration = true;

    // RequireAuthenticatedSignIn 설정 (기본값은 true)
    // 이 값이 true이면, 인증된 요청에 대해서만 ClaimsPrincipal이 생성됩니다.
    // 즉, 사용자가 이미 인증되어야만 인증 미들웨어가 ClaimsPrincipal을 생성합니다.
    // 특별한 경우가 아니라면 기본값(true)을 유지하는 것이 좋습니다.
    // options.RequireAuthenticatedSignIn = true;
});

인증 및 로그아웃 컨트롤러 로직

다음은 사용자가 로그인하고 로그아웃하는 과정을 처리하는 컨트롤러의 예시입니다. HttpContext.SignInAsync를 사용하여 사용자 정보를 쿠키에 저장하고, HttpContext.SignOutAsync를 사용하여 쿠키를 삭제합니다.


using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using MyWebApp.Models; // 모델 네임스페이스 변경

namespace MyWebApp.Controllers
{
    public class AccountController : Controller // 컨트롤러 이름 변경
    {
        private readonly ILogger<AccountController> _logger;
        private readonly IHttpContextAccessor _httpContextAccessor; // 변수명 변경

        public AccountController(ILogger<AccountController> logger, IHttpContextAccessor httpContextAccessor)
        {
            _logger = logger;
            _httpContextAccessor = httpContextAccessor;
        }

        [HttpGet("/SignIn")] // 경로 변경
        public IActionResult SignIn(string? returnUrl = null) // 메서드 이름 변경, returnUrl은 선택 사항
        {
            ViewData["ReturnUrl"] = returnUrl;
            return View();
        }

        [HttpPost("/SignIn")] // 경로 변경
        public async Task<IActionResult> SignInProcess(LoginViewModel credentials) // 메서드 이름 및 모델 변수명 변경
        {
            if (!ModelState.IsValid)
            {
                // 유효성 검사 실패 시 처리
                return View("SignIn", credentials); // 로그인 페이지로 다시 리디렉션
            }

            // 간단한 사용자 검증 (예시)
            if (credentials.Username == "testuser" && credentials.Password == "password") // 사용자 검증 로직 변경
            {
                List<Claim> userClaims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, credentials.Username),
                    new Claim(ClaimTypes.Email, credentials.Username + "@example.com"),
                    new Claim(ClaimTypes.Role, "User") // 역할 추가 예시
                };

                ClaimsIdentity userIdentity = new ClaimsIdentity(userClaims, CookieAuthenticationDefaults.AuthenticationScheme);

                AuthenticationProperties authProperties = new AuthenticationProperties
                {
                    IsPersistent = credentials.RememberMe, // "로그인 유지" 설정
                    ExpiresUtc = credentials.RememberMe ? DateTimeOffset.UtcNow.AddDays(7) : (DateTimeOffset?)null // 영구 쿠키일 경우 만료 시간 설정 (7일)
                };

                await _httpContextAccessor.HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(userIdentity),
                    authProperties
                );

                if (!string.IsNullOrEmpty(credentials.ReturnUrl) && Url.IsLocalUrl(credentials.ReturnUrl))
                {
                    return Redirect(credentials.ReturnUrl);
                }
                return RedirectToAction("Index", "Home"); // 기본적으로 홈 페이지로 리디렉션
            }

            ModelState.AddModelError(string.Empty, "Invalid login attempt."); // 로그인 실패 메시지
            return View("SignIn", credentials); // 로그인 페이지로 다시 리디렉션
        }

        [HttpGet("/SignOut")] // 경로 변경
        public async Task<IActionResult> SignOutUser() // 메서드 이름 변경
        {
            await _httpContextAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return RedirectToAction("Index", "Home");
        }
    }
}

로그인 입력에 사용되는 모델 클래스는 다음과 같이 정의할 수 있습니다.


using System.ComponentModel.DataAnnotations;

namespace MyWebApp.Models // 네임스페이스 변경
{
    /// <summary>
    /// 로그인 요청을 위한 데이터 전송 객체
    /// </summary>
    public class LoginViewModel // 클래스 이름 변경
    {
        [Required(ErrorMessage = "사용자 이름은 필수입니다.")] // 에러 메시지 추가
        [Display(Name = "사용자 이름")] // Display Name 추가
        public string Username { get; set; } // 변수명 변경

        [Required(ErrorMessage = "비밀번호는 필수입니다.")]
        [DataType(DataType.Password)] // DataType 지정
        [Display(Name = "비밀번호")]
        public string Password { get; set; }

        /// <summary>
        /// 로그인 유지 여부
        /// </summary>
        [Display(Name = "로그인 유지")]
        public bool RememberMe { get; set; }

        /// <summary>
        /// 로그인 후 리디렉션될 경로
        /// </summary>
        public string? ReturnUrl { get; set; }
    }
}

이러한 구성을 통해 ASP.NET Core 애플리케이션에서 안전하고 유연한 쿠키 기반 인증 시스템을 구현할 수 있습니다.

태그: ASP.NET Core 쿠키 인증 인증 미들웨어 ClaimsPrincipal IHttpContextAccessor

5월 21일 16:53에 게시됨