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에서 사용자를 로그인시키는 방법은 크게 세 가지가 있습니다. 각 방법은 상황에 따라 장단점이 있습니다.
-
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); -
HttpContext.SignInAsync메서드 직접 호출:Controller에서 직접HttpContext를 통해SignInAsync를 호출하는 방법입니다. 이 방법은 편리하지만, 비동기 다중 스레드 환경에서 잠재적인 스레드 안전성 문제를 일으킬 수 있습니다.// HttpContext는 스레드 안전하지 않을 수 있음 await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(userIdentity), new AuthenticationProperties { IsPersistent = true } ); -
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 애플리케이션에서 안전하고 유연한 쿠키 기반 인증 시스템을 구현할 수 있습니다.