ASP.NET Core 프로젝트에 JWT 인증 통합
이 가이드는 ASP.NET Core 8.0 MVC 프로젝트에 JSON Web Token(JWT) 인증을 추가하고, 특정 Web API에 JWT 인증을 사용하며 나머지 Web API에는 기존의 ASP.NET Core Identity 인증을 사용할 수 있도록 설정하는 과정을 Top Level Statement를 사용하여 단계별로 설명합니다.
VisualAcademy 프로젝트 생성
ASP.NET Core 8.0 MVC 프로젝트를 인증이 포함된 형태로 만드는 절차는 다음과 같습니다.
- 프로젝트 생성: Visual Studio 또는
dotnet new mvc
명령어를 사용하여 새 ASP.NET Core 8.0 MVC 프로젝트를 만듭니다. 프로젝트 이름을VisualAcademy
로 지정합니다. - 인증 추가: 프로젝트 생성 시 '개별 계정' 인증 옵션을 선택하여 ASP.NET Core Identity를 포함시킵니다. 이를 통해 사용자 관리와 로그인 기능을 손쉽게 추가할 수 있습니다.
- 데이터베이스 설정: 프로젝트에
ApplicationDbContext
를 추가하고,appsettings.json
에서 데이터베이스 연결 문자열을 설정합니다. Entity Framework Core를 사용하여 데이터베이스를 구성합니다. - 사용자 인터페이스 구성: 로그인, 등록, 로그아웃 등의 사용자 인터페이스를 구성합니다. ASP.NET Core Identity는 기본적인 사용자 인터페이스를 제공하므로, 필요에 따라 커스터마이즈할 수 있습니다.
- 인증 테스트: 로컬 개발 환경에서 프로젝트를 실행하고, 사용자 등록, 로그인, 로그아웃 기능이 정상적으로 작동하는지 테스트합니다.
프로젝트 초기 설정
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using VisualAcademy.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddControllersWithViews();
// JWT 인증 설정 추가
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YourSuperSecretKeyHere")), // 안전하게 관리 필요
ValidateIssuer = false,
ValidateAudience = false
};
}).AddIdentityCookies();
var app = builder.Build();
HTTP 요청 파이프라인 구성
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); // 인증 미들웨어 추가
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
JWT 토큰 발행 API 구현
api/logintest
경로에 POST 요청을 받아 사용자 인증 정보를 확인하고 JWT 토큰을 발행하는 컨트롤러 메서드를 구현합니다.- 사용자 인증 로직을 적용하고, 유효한 인증 정보에 대해 서명된 JWT 토큰을 생성하여 반환합니다.
인증 방식 선택적 적용
- 특정 컨트롤러에 JWT 인증 적용:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
속성을 추가하여 JWT 인증을 요구합니다. - 나머지 Web API에 기존 인증 적용:
[Authorize]
속성을 추가하여 기본 인증(ASP.NET Core Identity)을 사용합니다.
테스트 및 검증
- API 테스트 도구(예: Postman)를 사용하여
api/logintest
엔드포인트에 접근하고 JWT 토큰을 받아냅니다. - 받은 JWT 토큰을 이용해 인증이 필요한 컨트롤러의 엔드포인트에 접근하여 인증이 정상적으로 작동하는지 확인합니다.
발행된 토큰 검증하기
토큰이 정상적으로 발행되었는지 확인하기 위해, 발행된 JWT 토큰을 jwt.io
또는 jwt.ms
와 같은 사이트에 넣어서 검증할 수 있습니다. 이 단계는 토큰의 구조와 클레임을 확인하고, 서명이 올바른지 검증하는 데 유용합니다.
- 토큰 복사: 앞서 테스트 절차에서 얻은 토큰을 복사합니다.
- 검증 사이트 방문: 브라우저에서
https://jwt.io
또는https://jwt.ms
를 방문합니다. - 토큰 입력: 사이트의 'Encoded' 섹션에 복사한 토큰을 붙여넣습니다.
- 결과 확인: 토큰의 헤더, 페이로드, 서명 부분이 올바르게 해석되는지 확인합니다. 유효한 서명과 클레임을 통해 토큰의 유효성을 검증할 수 있습니다.
Bearer 토큰을 POSTMAN에서 테스트하기
POSTMAN을 사용하여 Bearer 토큰으로 보호된 API 엔드포인트를 테스트하는 절차는 다음과 같습니다:
- POSTMAN 열기: POSTMAN 응용 프로그램을 열고 새 요청을 만듭니다.
- 요청 타입 선택: API 엔드포인트에 맞는 HTTP 메서드 (GET, POST 등)를 선택합니다.
- URL 입력: 테스트할 API의 URL을 입력합니다.
- 인증 탭 설정: 요청 설정에서 'Authorization' 탭을 선택합니다.
- 인증 유형 선택: 'Type' 드롭다운 메뉴에서 'Bearer Token'을 선택합니다.
- 토큰 입력: 'Token' 필드에 유효한 Bearer 토큰을 입력합니다.
- 요청 보내기: 'Send' 버튼을 클릭하여 요청을 보냅니다.
- 결과 확인: 응답 탭에서 API의 응답을 확인합니다.
.NET 8.0의 .http 파일에서 Bearer 토큰 테스트하기
Visual Studio 2022 이상에서는 .http 파일을 사용하여 HTTP 요청을 직접 테스트할 수 있습니다. Bearer 토큰을 사용하여 요청을 보내는 방법은 다음과 같습니다:
- .http 파일 생성: 프로젝트에 새 .http 파일을 생성합니다 (예: `APITest.http`).
- 요청 정의: 다음 형식을 사용하여 요청을 정의합니다.
'YOUR_TOKEN_HERE'를 실제 Bearer 토큰으로 교체합니다.### 토큰을 사용한 API 테스트 GET https://localhost:5001/api/protected Authorization: Bearer YOUR_TOKEN_HERE
- 요청 실행: .http 파일 내의 요청 위에 나타나는 'Send Request' 링크를 클릭합니다.
- 결과 확인: 같은 창에서 응답 내용을 확인할 수 있습니다.
이 단계들을 통해 Bearer 토큰을 사용하여 인증이 필요한 API 엔드포인트에 대한 요청을 POSTMAN 또는 Visual Studio의 .http 파일을 사용하여 쉽게 테스트할 수 있습니다.
추가 사항
- 보안: HTTPS 사용, JWT 키 관리, CORS 설정 등 보안 고려사항을 철저히 검토합니다.
- 오류 처리: 인증 실패, 잘못된 요청, 서버 오류 등 다양한 시나리오에 대한 적절한 오류 처리를 구현합니다.
AuthenticationDemoController 구현
AuthenticationDemoController
에서는 아이디와 암호와 상관없이 무조건 토큰을 생성하는 메서드를 구현합니다. 이 컨트롤러는 단순한 데모 목적으로 사용되며, 실제 어플리케이션에서는 보안을 강화한 인증 메커니즘을 사용해야 합니다.
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
[ApiController]
[Route("api/[controller]")]
public class AuthenticationDemoController : ControllerBase
{
[HttpPost]
[Route("logintest")]
public IActionResult LoginTest()
{
// 사용자 인증 정보는 확인하지 않고 무조건 토큰 발행 (실제 어플리케이션에는 적합하지 않음)
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("YourSuperSecretKeyHere");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "DemoUser")
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new { Token = tokenString });
}
}
이 컨트롤러는 /api/authenticationdemo/logintest
엔드포인트에 POST 요청을 보내면, 사용자 인증 정보와 상관없이 무조건 JWT 토큰을 생성하고 반환합니다. 이 토큰은 클라이언트가 인증이 필요한 다른 API 엔드포인트에 접근할 때 사용할 수 있습니다.
순수 HTML과 JavaScript를 사용한 인증 테스트 절차
순수 HTML 문서에서 JavaScript fetch API를 사용하여 JWT 토큰을 전달하고 인증된 결과를 확인하는 절차를 설명합니다. 이 테스트는 두 부분으로 나뉩니다: 토큰을 획득하는 부분과 토큰을 사용하여 인증이 필요한 API에 접근하는 부분.
HTML 문서 생성
test.html
파일을 만들고 다음 내용을 포함시킵니다.
<!DOCTYPE html>
<html>
<head>
<title>JWT Authentication Test</title>
</head>
<body>
<h2>JWT Authentication Test</h2>
<button id="getToken">Get Token</button>
<button id="testAuth">Test Authenticated API</button>
<p id="tokenArea">Token will appear here.</p>
<p id="apiResponse">API Response will appear here.</p>
<script src="script.js"></script>
</body>
</html>
JavaScript 파일 생성
script.js
파일을 만들고 다음 내용을 포함시킵니다.
document.getElementById('getToken').addEventListener('click', getToken);
document.getElementById('testAuth').addEventListener('click', testAuth);
function getToken() {
fetch('/api/authenticationdemo/logintest', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
document.getElementById('tokenArea').textContent = 'Token: ' + data.Token;
localStorage.setItem('jwtToken', data.Token);
})
.catch(error => console.error('Error fetching token:', error));
}
function testAuth() {
const token = localStorage.getItem('jwtToken');
fetch('/api/protected', { // '/api/protected'는 인증이 필요한 API 엔드포인트를 가정함
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(response => {
if (response.ok) {
return response.text();
} else {
throw new Error('Failed to authenticate');
}
})
.then(data => {
document.getElementById('apiResponse').textContent = 'Authenticated API response: ' + data;
})
.catch(error => {
document.getElementById('apiResponse').textContent = error.message;
});
}
테스트 절차
- 토큰 획득:
test.html
페이지를 열고 'Get Token' 버튼을 클릭합니다. 이것은/api/authenticationdemo/logintest
에 POST 요청을 보내 토큰을 획득하고, 화면에 표시합니다. - 인증된 API 테스트: 'Test Authenticated API' 버튼을 클릭합니다. 이것은 획득한 토큰을 사용하여 인증이 필요한 API(
/api/protected
)에 접근을 시도하고, 결과를 화면에 표시합니다.
주의사항
- 실제 어플리케이션에서는 HTTPS를 사용하여 통신해야 합니다.
script.js
의/api/protected
경로는 실제 어플리케이션의 인증이 필요한 API 엔드포인트로 대체해야 합니다.- 이 스크립트는 데모 목적으로만 사용되며, 실제 어플리케이션에서는 보다 복잡한 인증 흐름과 보안 조치가 필요합니다.
이 절차를 통해 순수 HTML 문서와 JavaScript를 사용하여 JWT 토큰을 전달하고 인증이 통과되는 내용을 테스트할 수 있습니다. 토큰이 없거나 유효하지 않은 경우, 인증이 실패하며 이에 대한 처리도 확인할 수 있습니다.
ValidateIssuer와 ValidateAudience 설정 시 추가 절차
ValidateIssuer
와 ValidateAudience
를 true
로 설정했다면, JWT 토큰의 발행자(issuer)와 대상자(audience)가 유효한지 검증하도록 설정한 것입니다. 이 설정을 활성화한 후에 진행해야 할 추가적인 절차는 다음과 같습니다:
Issuer와 Audience 정의: 토큰의 발행자(issuer)와 대상자(audience)를 명확히 정의해야 합니다. 이 정보는 토큰을 생성할 때 지정되어야 하며, 일반적으로 어플리케이션의 신뢰할 수 있는 URL이나 식별자가 사용됩니다.
Issuer와 Audience 설정:
TokenValidationParameters
에ValidIssuer
와ValidAudience
속성을 설정하여, 검증할 issuer와 audience 값을 지정합니다. 예를 들어:options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YourSuperSecretKeyHere")), // 안전하게 관리 필요 ValidateIssuer = true, ValidIssuer = "YourIssuer", // 발행자 이름을 지정 ValidateAudience = true, ValidAudience = "YourAudience", // 대상자 이름을 지정 };
토큰 발행 시 주의: 토큰을 발행할 때
Issuer
와Audience
클레임을 토큰에 포함시켜야 합니다.JwtSecurityToken
생성자 또는SecurityTokenDescriptor
에서 이 값을 설정할 수 있습니다.테스트 및 검증: 변경 사항을 적용한 후에는 토큰 발행과 검증 절차가 여전히 정상적으로 작동하는지 테스트해야 합니다. 유효한 issuer와 audience를 가진 토큰이 성공적으로 인증되고, 잘못된 issuer나 audience를 가진 토큰은 거부되는지 확인합니다.
보안 고려사항:
ValidateIssuer
와ValidateAudience
를 활성화하면 토큰의 신뢰성이 향상되지만, 이러한 값들을 안전하게 관리하고 노출되지 않도록 주의해야 합니다. 또한, 키와 관련된 보안 조치도 철저히 해야 합니다.
이러한 절차를 통해, 토큰의 발행자와 대상자를 정확하게 검증함으로써 보안 수준을 한층 더 높일 수 있습니다. 이 설정들은 특히 여러 시스템이나 애플리케이션 간에 토큰을 공유할 때 중요합니다.
마무리
이 가이드를 통해 기존의 ASP.NET Core 8.0 MVC 프로젝트에 JWT 인증을 통합하고, 특정 컨트롤러에는 JWT 인증을, 나머지 컨트롤러에는 기존 인증을 적용하는 방법을 이해할 수 있습니다. 보안 및 오류 처리에 주의를 기울이고, 프로젝트의 요구사항에 맞게 설정을 조정하는 것이 중요합니다.