custom Middleware 활용하여 Asp.Net Core Minimal APIs 보안 처리하기
이번 포스팅에서는 minimal API 를 활용하기 위한 커스텀 미들웨어를 만들어 보겠습니다.
Authorization
클라이언트 요청이 데이터에 안전하게 액세스하도록 하기 위해 API 는 authorization 을 사용합니다.authentication (인증)이 올바른 신원을 증명하는 것을 의미한다면, authorization (권한 부여)는 특정 Action 을 Allow(허용) 하는 것을 의미합니다.
authorization (권한 부여) 프로세스에는 요청 발신자를 인증하고 관련 데이터에 액세스하거나 조작할 수 있는 권한이 있는지 확인하는 작업이 포함될 수 있습니다.
API Key, Bearer token, Basic auth, OAuth 등과 같은 다양한 authorization모델이 있습니다.
Api Key
API 는 일반적으로 Request Header 또는 Query parameters 에 포함하는 key-value 쌍입니다. API 키는 주로 API 호출(API 사용을 인증하는 것)을 하는 사람을 식별하는 방법으로 사용합니다.
Middleware
미들웨어는 requests and responses(요청 및 응답) 을 처리하는 데 사용되는 애플리케이션 파이프라인의 component (클래스)입니다.
각 component :
파이프라인의 다음 구성 요소에 요청을 전달할지 여부를 선택합니다.
파이프라인의 다음 구성 요소 앞 뒤로 작업을 수행할 수 있습니다.
다음 다이어그램은 미들웨어 파이프라인이 작동하는 방식을 보여줍니다. 각 미들웨어는 다음 작업 전후에 작업을 수행할 수 있습니다.
ASP.Net Core Web API가 생성 되면 일부 기본 미들웨어를 사용합니다. 다음 다이어그램은 전체 요청 처리 파이프라인을 보여줍니다. 기존 미들웨어의 주문 방식과 사용자 지정 미들웨어를 추가할 수 있는 위치를 보여줍니다.
custom middleware 생성
Middleware 는 일반적으로 클래스에 캡슐화되고 extension method 로 노출되며 다음을 포함해야 합니다.
RequestDelegate 유형의 매개변수가 있는 public constructor.
Invoke 또는 InvokeAsync라는 public method 입니다.이 method은 다음을 충족해야 합니다.
Task 을 리합니다.
HttpContext 유형의 첫 번째 매개변수를 Accept(수락) 합니다.
이 사용자 정의 미들웨어의 경우 api Keys 값 목록도 parameters 로 전달됩니다.
InvokeAsync 메서드에서 코드는 헤더에 ApiKey가 포함되어 있는지 확인합니다. 그렇지 않으면 다음 미들웨어가 호출되지 않고 오류 401이 발생합니다.
public class CustomApiKeyMiddleware
{
private readonly RequestDelegate _next;
private readonly List<string> _apiKeys;
public CustomApiKeyMiddleware(RequestDelegate next, string apiKey)
: this(next, new List<string> { apiKey })
{
}
public CustomApiKeyMiddleware(RequestDelegate next, List<string> apiKeys)
{
_next = next;
_apiKeys = apiKeys;
}
public async Task InvokeAsync(HttpContext context)
{
string header = context.Request.Headers[Constants.ApiKeyHeaderName].ToString();
if ((context.Features.Get<IEndpointFeature>()?.Endpoint?.Metadata.Any((m) => m is AllowAnonymousAttribute)).GetValueOrDefault() ||
!string.IsNullOrWhiteSpace(header) && _apiKeys.Any((k) => k == header))
{
await _next(context);
return;
}
context.Response.StatusCode = 401;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("ApiKey is invalid.");
}
}
Configuration
이제 파이프라인에 custom middleware 를 추가할 차례입니다.이것은 appsettings에 정의된 Api 키의 유효성을 검사하는 데 사용되는 middleware이며, 다른 모든 middleware이후에 기술해야 하며, UseEndpoints 미들웨어는 그 이전에 기술되어야 합니다. (코드 순서 기술 중요)
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseMiddleware<CustomApiKeyMiddleware>(app.Configuration.GetValue<string>("TodoApiKey"));
app.UseHttpsRedirection();
app.Run();
다음 코드는 swagger에 대한 보안 정의를 추가합니다. 이렇게 하면 승인 버튼이 표시되고 사용자가 엔드포인트를 실행하기 전에 ApiKey를 추가할 수 있습니다.
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TodoServiceApi", Version = "v1" });
c.AddSecurityDefinition(Constants.ApiKeyHeaderName, new OpenApiSecurityScheme
{
Description = "Api key needed to access the endpoints. ApiKey: ApiKey",
In = ParameterLocation.Header,
Name = Constants.ApiKeyHeaderName,
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Name = Constants.ApiKeyHeaderName,
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = Constants.ApiKeyHeaderName,
},
},
new string[] {}
}
});
});
Postman 로 테스트
ApiKey를 제공하지 않고 엔드포인트를 실행하면 다음 이미지와 같이 무단 오류가 발생합니다.
Authorization 탭에서 ApiKey를 추가할 수 있으며 Type 목록에서 API Key를 선택하고 Api 설정에서 정의한 동일한 키와 값으로 값을 추가할 수 있습니다.
Swagger 로 테스트하기
API 키가 제공되지 않으면 swagger 에서도 동일한 오류가 발생합니다.
코드를 통해 swagger 설정에 보안 정의가 추가되었으므로 이제 인증 페이지를 사용할 수 있습니다.페이지 상단의 Authorize 버튼 또는 각 endpoint 의 header 잠금 아이콘을 통해 페이지를 표시할 수 있습니다.