<서버 Side> ㆍ인증에 필요한 클래스 , 메서드등에 Attribute 를 사용하여, 사용전 인증을 거친 클라이언트만 접근 가능하도록 한다. ㆍAttribute 에서 Http 의 Context 사용, 처리 후 다시 라우팅 할 수 있도록 IAsyncActionFilter 를 상속받아서 구현한다.
ㆍ인증이 필요한 메서드(및 클래스등,,) 요청시 사용자 Attribute 가 요청을 가로채고, IAsyncActionFilter 인터페이스의 OnActionExecutionAsync 메서드에서 Context 의 헤더의 ApI-key 의 인증 확인 후 허용 또는 거부처리를 한다. <클라이언트 Side> ㆍ 클라이언트는 Http 헤더에 인증에 필요한 API-Key 와 함께 WebAPI 사용을 요청을 한다.
상속 : Attribute , IAsyncActionFilter 상속 및 OnActionExecutionAsync 구현 설정 : AttributeUsage , AttributeTargets 로 attribute 설정
< 사용자 Attribute – 기본구조 > [AttributeUsage(AttributeTargets.Class , Inherited=false , AllowMultiple=false)] public class MyApiKeyAuthAttribute : Attribute , IAsyncActionFilter { // IAsyncActionFilter 인터페이스의 OnActionExecutionAsync 구현 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // context Header 의 Api key 비교 후 인증 처리 } }
< Web API >ㆍ Class 에 Attribute 사용하여 인증처리할때 [MyApiKeyAuth] // 클래스 접근 전에 Attribute 에서 인증처라, 클래스 모든 메서드/속성 접근 불가/가능 [ApiController] [Route(“Api/[controller]”)] public class MyWebAPIController { [HttpGet] public IEnumable<MyDataList> get() { } [MyApiKeyAuth]// GetForUser 호출시 실행전 MyApiKey가 제어를 가로챈후 attribute 에서 인증 완료후 접근가능 [HttpGet(“{userid}”)] public IEnumable<MyDataList> GetForUser(string userid) { // MyApiKeyAuth attribute인증이 완료되면 get() 메서드를 정상적으로 호출됨 } }
< Authenticaltion Attribute > [MyApiKeyAuth] 로 attribute 를 사용하지만 , 클래스 작성시 MyApiKeyAttribute 로 작성해주며, 사용시 MyApiKey 만 호출 해도 자동으로 MyApiKeyAttribute 와 매핑되며, attribute 를 생략할수있다.
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks;
namespace SecuringWebApiUsingApiKey.Attributes {
[AttributeUsage(validOn: AttributeTargets.Method)] public class MyApiKeyAttribute : Attribute, IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) {
if(context.HttpContext.Response.Headers.TryGetValue(“ApiKey” ,out var clientKeyofHeader)) { string serverKey = string.Empty;
// 의존성 주입(ID) 된 IConfiguration 객체 가져오기(appSetting.json) var appSetting = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>(); serverKey = appSetting[“WEBAPI:apiKey”].ToString().Trim();
if(!clientKeyofHeader.ToString().Equals(serverKey)) { context.Result = new ContentResult() { ContentType = “text/html”, StatusCode = 401, Content = “your api key invalid” };
return; } } else { context.Result = new ContentResult() { StatusCode = 401, ContentType = “text/plain”, Content = “your don’t have APIKey” }; return; } await next(); // 현재 attribute 를 호출한 메서드 또는 클래스등이 next() 가 된다.
} } }
await next(); 가 호출되기 위한 필수조건 ★ Context.Result 는 반드시 null 이어야한다. (기본 null) ★ Context.Result 의 statusCode = 200 (성공) 이어도 InvalidOperationException 예외(오류)가 발생한다 (그러므로 Context.Result 가 null 이 아니면 반드시 return 문을 통해 next() 가 호출되지 않게 해야한다) ★ context.Result의 ContentResult 객체정보를(content) 채워놓으면 , return 시 별도의 객체를 return 하지 않아도, 브라우져에 Context.Result 의 정보를 출력하게되어있다.