ASP.NET Web API 2의 인증 필터Authentication Filters in ASP.NET Web API 2

Mike Wassonby Mike Wasson

인증 필터는 HTTP 요청을 인증 하는 구성 요소입니다.An authentication filter is a component that authenticates an HTTP request. Web API 2 및 MVC 5는 모두 인증 필터를 지원 하지만 대부분 필터 인터페이스에 대 한 명명 규칙에 따라 약간 다릅니다.Web API 2 and MVC 5 both support authentication filters, but they differ slightly, mostly in the naming conventions for the filter interface. 이 항목에서는 Web API 인증 필터에 대해 설명 합니다.This topic describes Web API authentication filters.

인증 필터를 사용 하면 개별 컨트롤러 또는 작업에 대 한 인증 체계를 설정할 수 있습니다.Authentication filters let you set an authentication scheme for individual controllers or actions. 이런 방식으로 앱은 다양 한 HTTP 리소스에 대해 서로 다른 인증 메커니즘을 지원할 수 있습니다.That way, your app can support different authentication mechanisms for different HTTP resources.

이 문서에서는 https://github.com/aspnet/samples에 대 한 기본 인증 샘플의 코드를 표시 합니다.In this article, I'll show code from the Basic Authentication sample on https://github.com/aspnet/samples. 이 샘플에서는 HTTP 기본 액세스 인증 체계 (RFC 2617)를 구현 하는 인증 필터를 보여 줍니다.The sample shows an authentication filter that implements the HTTP Basic Access Authentication scheme (RFC 2617). 필터는 IdentityBasicAuthenticationAttribute이라는 클래스에서 구현 됩니다.The filter is implemented in a class named IdentityBasicAuthenticationAttribute. 예제에서 코드를 모두 표시 하지는 않습니다. 인증 필터를 작성 하는 방법을 보여 주는 부분만 있으면 됩니다.I won't show all of the code from the sample, just the parts that illustrate how to write an authentication filter.

인증 필터 설정Setting an Authentication Filter

다른 필터와 마찬가지로 인증 필터는 포트당, 작업별 또는 모든 웹 API 컨트롤러에 전역적으로 적용 될 수 있습니다.Like other filters, authentication filters can be applied per-controller, per-action, or globally to all Web API controllers.

컨트롤러에 인증 필터를 적용 하려면 컨트롤러 클래스를 필터 특성으로 데코 레이트 합니다.To apply an authentication filter to a controller, decorate the controller class with the filter attribute. 다음 코드는 컨트롤러 클래스에 대 한 [IdentityBasicAuthentication] 필터를 설정 하 여 컨트롤러의 모든 작업에 대해 기본 인증을 사용 하도록 설정 합니다.The following code sets the [IdentityBasicAuthentication] filter on a controller class, which enables Basic Authentication for all of the controller's actions.

[IdentityBasicAuthentication] // Enable Basic authentication for this controller.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
    public IHttpActionResult Get() { . . . }
    public IHttpActionResult Post() { . . . }
}

하나의 작업에 필터를 적용 하려면 필터를 사용 하 여 작업을 데코 레이트 합니다.To apply the filter to one action, decorate the action with the filter. 다음 코드는 컨트롤러의 Post 메서드에 [IdentityBasicAuthentication] 필터를 설정 합니다.The following code sets the [IdentityBasicAuthentication] filter on the controller's Post method.

[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
    public IHttpActionResult Get() { . . . }

    [IdentityBasicAuthentication] // Enable Basic authentication for this action.
    public IHttpActionResult Post() { . . . }
}

모든 Web API 컨트롤러에 필터를 적용 하려면이를 Globalconfiguration. 필터에 추가 합니다.To apply the filter to all Web API controllers, add it to GlobalConfiguration.Filters.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new IdentityBasicAuthenticationAttribute());

        // Other configuration code not shown...
    }
}

Web API 인증 필터 구현Implementing a Web API Authentication Filter

Web API에서 인증 필터는 System.web. p. i o p.In Web API, authentication filters implement the System.Web.Http.Filters.IAuthenticationFilter interface. 또한 특성으로 적용 하기 위해 system.object에서 상속 해야 합니다.They should also inherit from System.Attribute, in order to be applied as attributes.

Iauthenticationfilter 인터페이스에는 두 가지 방법이 있습니다.The IAuthenticationFilter interface has two methods:

  • AuthenticateAsync 는 요청에서 자격 증명의 유효성을 검사 하 여 요청을 인증 합니다 (있는 경우).AuthenticateAsync authenticates the request by validating credentials in the request, if present.
  • ChallengeAsync 은 필요한 경우 HTTP 응답에 인증 챌린지를 추가 합니다.ChallengeAsync adds an authentication challenge to the HTTP response, if needed.

이러한 메서드는 rfc 2612rfc 2617에 정의 된 인증 흐름에 해당 합니다.These methods correspond to the authentication flow defined in RFC 2612 and RFC 2617:

  1. 클라이언트는 인증 헤더에 자격 증명을 보냅니다.The client sends credentials in the Authorization header. 이는 일반적으로 클라이언트가 서버 로부터 401 (권한 없음) 응답을 받은 후에 발생 합니다.This typically happens after the client receives a 401 (Unauthorized) response from the server. 그러나 클라이언트는 401을 받은 후 뿐만 아니라 모든 요청과 함께 자격 증명을 보낼 수 있습니다.However, a client can send credentials with any request, not just after getting a 401.
  2. 서버에서 자격 증명을 허용 하지 않는 경우 401 (권한 없음) 응답을 반환 합니다.If the server does not accept the credentials, it returns a 401 (Unauthorized) response. 응답에는 하나 이상의 과제가 포함 된 Www 인증 헤더가 포함 되어 있습니다.The response includes a Www-Authenticate header that contains one or more challenges. 각 챌린지는 서버에서 인식 하는 인증 체계를 지정 합니다.Each challenge specifies an authentication scheme recognized by the server.

서버는 익명 요청에서 401을 반환할 수도 있습니다.The server can also return 401 from an anonymous request. 실제로 인증 프로세스를 시작 하는 방법은 다음과 같습니다.In fact, that's typically how the authentication process is initiated:

  1. 클라이언트는 익명 요청을 보냅니다.The client sends an anonymous request.
  2. 서버는 401을 반환 합니다.The server returns 401.
  3. 클라이언트는 자격 증명을 사용 하 여 요청을 다시 보냅니다.The clients resends the request with credentials.

이 흐름에는 인증권한 부여 단계가 모두 포함 됩니다.This flow includes both authentication and authorization steps.

  • 인증에서 클라이언트의 id를 증명 합니다.Authentication proves the identity of the client.
  • 권한 부여는 클라이언트가 특정 리소스에 액세스할 수 있는지 여부를 확인 합니다.Authorization determines whether the client can access a particular resource.

Web API에서 인증 필터는 인증을 처리 하지만 권한 부여는 처리 하지 않습니다.In Web API, authentication filters handle authentication, but not authorization. 권한 부여 필터 또는 컨트롤러 작업 내에서 권한 부여를 수행 해야 합니다.Authorization should be done by an authorization filter or inside the controller action.

웹 API 2 파이프라인의 흐름은 다음과 같습니다.Here is the flow in the Web API 2 pipeline:

  1. 웹 API는 동작을 호출 하기 전에 해당 작업에 대 한 인증 필터 목록을 만듭니다.Before invoking an action, Web API creates a list of the authentication filters for that action. 여기에는 작업 범위, 컨트롤러 범위 및 전역 범위를 포함 하는 필터가 포함 됩니다.This includes filters with action scope, controller scope, and global scope.
  2. 웹 API는 목록의 모든 필터에 대해 AuthenticateAsync 를 호출 합니다.Web API calls AuthenticateAsync on every filter in the list. 각 필터는 요청에서 자격 증명의 유효성을 검사할 수 있습니다.Each filter can validate credentials in the request. 자격 증명의 유효성을 검사 하는 필터가 있으면이 필터는 IPrincipal 을 만들어 요청에 연결 합니다.If any filter successfully validates credentials, the filter creates an IPrincipal and attaches it to the request. 이 시점에서 필터는 오류를 트리거할 수도 있습니다.A filter can also trigger an error at this point. 이 경우 나머지 파이프라인은 실행 되지 않습니다.If so, the rest of the pipeline does not run.
  3. 오류가 없는 것으로 가정 하 여 요청은 파이프라인의 나머지 부분을 통해 흐릅니다.Assuming there is no error, the request flows through the rest of the pipeline.
  4. 마지막으로 Web API는 모든 인증 필터의 ChallengeAsync 메서드를 호출 합니다.Finally, Web API calls every authentication filter's ChallengeAsync method. 필터는 필요한 경우이 메서드를 사용 하 여 응답에 챌린지를 추가 합니다.Filters use this method to add a challenge to the response, if needed. 401 오류에 대 한 응답으로 발생 하는 일반적으로는 항상 그렇지는 않습니다.Typically (but not always) that would happen in response to a 401 error.

다음 다이어그램에서는 두 가지 가능한 사례를 보여 줍니다.The following diagrams show two possible cases. 첫 번째는 인증 필터에서 요청을 성공적으로 인증 하 고, 권한 부여 필터에서 요청을 승인 하 고, 컨트롤러 작업에서 200 (OK)를 반환 합니다.In the first, the authentication filter successfully authenticates the request, an authorization filter authorizes the request, and the controller action returns 200 (OK).

두 번째 예제에서는 인증 필터에서 요청을 인증 하지만 권한 부여 필터는 401 (권한 없음)을 반환 합니다.In the second example, the authentication filter authenticates the request, but the authorization filter returns 401 (Unauthorized). 이 경우 컨트롤러 작업은 호출 되지 않습니다.In this case, the controller action is not invoked. 인증 필터는 Www-인증 헤더를 응답에 추가 합니다.The authentication filter adds a Www-Authenticate header to the response.

예를 들어, 컨트롤러 작업에서 익명 요청을 허용 하는 경우 다른 조합을—사용할 수 있습니다. 인증 필터는 있지만 권한 부여는 없을 수 있습니다.Other combinations are possible—for example, if the controller action allows anonymous requests, you might have an authentication filter but no authorization.

AuthenticateAsync 메서드 구현Implementing the AuthenticateAsync Method

AuthenticateAsync 메서드는 요청을 인증 하려고 합니다.The AuthenticateAsync method tries to authenticate the request. 메서드 서명은 다음과 같습니다.Here is the method signature:

Task AuthenticateAsync(
    HttpAuthenticationContext context,
    CancellationToken cancellationToken
)

AuthenticateAsync 메서드는 다음 중 하나를 수행 해야 합니다.The AuthenticateAsync method must do one of the following:

  1. Nothing (no op).Nothing (no-op).
  2. IPrincipal 을 만들고 요청에 대해 설정 합니다.Create an IPrincipal and set it on the request.
  3. 오류 결과를 설정 합니다.Set an error result.

옵션 (1)은 필터에서 인식 하는 자격 증명이 요청에 없음을 의미 합니다.Option (1) means the request did not have any credentials that the filter understands. 옵션 (2)은 필터가 요청을 성공적으로 인증 했음을 의미 합니다.Option (2) means the filter successfully authenticated the request. 옵션 (3)은 요청에 잘못 된 자격 증명 (예: 잘못 된 암호)이 있어 오류 응답을 트리거하는 것을 의미 합니다.Option (3) means the request had invalid credentials (like the wrong password), which triggers an error response.

AuthenticateAsync구현에 대 한 일반적인 개요는 다음과 같습니다.Here is a general outline for implementing AuthenticateAsync.

  1. 요청에서 자격 증명을 찾습니다.Look for credentials in the request.
  2. 자격 증명이 없는 경우 아무 작업도 수행 하지 않고 반환 (op 없음)을 반환 합니다.If there are no credentials, do nothing and return (no-op).
  3. 자격 증명이 있지만 필터에서 인증 체계를 인식 하지 못하는 경우 아무 작업도 수행 하지 않고 반환 (op 없음)을 반환 합니다.If there are credentials but the filter does not recognize the authentication scheme, do nothing and return (no-op). 파이프라인의 다른 필터에서 스키마를 이해할 수 있습니다.Another filter in the pipeline might understand the scheme.
  4. 필터가 이해 하는 자격 증명이 있으면 인증을 시도 합니다.If there are credentials that the filter understands, try to authenticate them.
  5. 자격 증명이 잘못 된 경우 context.ErrorResult설정 하 여 401을 반환 합니다.If the credentials are bad, return 401 by setting context.ErrorResult.
  6. 자격 증명이 유효 하면 IPrincipal 을 만들고 context.Principal를 설정 합니다.If the credentials are valid, create an IPrincipal and set context.Principal.

다음 코드는 기본 인증 샘플의 AuthenticateAsync 메서드를 보여 줍니다.The follow code shows the AuthenticateAsync method from the Basic Authentication sample. 주석은 각 단계를 표시 합니다.The comments indicate each step. 이 코드는 자격 증명이 없는 인증 헤더, 잘못 된 자격 증명, 잘못 된 사용자 이름/암호 등의 여러 가지 오류 유형을 보여 줍니다.The code shows several types of error: An Authorization header with no credentials, malformed credentials, and bad username/password.

public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
    // 1. Look for credentials in the request.
    HttpRequestMessage request = context.Request;
    AuthenticationHeaderValue authorization = request.Headers.Authorization;

    // 2. If there are no credentials, do nothing.
    if (authorization == null)
    {
        return;
    }

    // 3. If there are credentials but the filter does not recognize the 
    //    authentication scheme, do nothing.
    if (authorization.Scheme != "Basic")
    {
        return;
    }

    // 4. If there are credentials that the filter understands, try to validate them.
    // 5. If the credentials are bad, set the error result.
    if (String.IsNullOrEmpty(authorization.Parameter))
    {
        context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
        return;
    }

    Tuple<string, string> userNameAndPassword = ExtractUserNameAndPassword(authorization.Parameter);
    if (userNameAndPassword == null)
    {
        context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
    }

    string userName = userNameAndPassword.Item1;
    string password = userNameAndPassword.Item2;

    IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
    if (principal == null)
    {
        context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
    }

    // 6. If the credentials are valid, set principal.
    else
    {
        context.Principal = principal;
    }

}

오류 결과 설정Setting an Error Result

자격 증명이 유효 하지 않은 경우 필터는 오류 응답을 생성 하는 IHttpActionResultcontext.ErrorResult 설정 해야 합니다.If the credentials are invalid, the filter must set context.ErrorResult to an IHttpActionResult that creates an error response. IHttpActionResult에 대 한 자세한 내용은 Web API 2의 작업 결과를 참조 하세요.For more information about IHttpActionResult, see Action Results in Web API 2.

기본 인증 샘플에는이 용도에 적합 한 AuthenticationFailureResult 클래스가 포함 되어 있습니다.The Basic Authentication sample includes an AuthenticationFailureResult class that is suitable for this purpose.

public class AuthenticationFailureResult : IHttpActionResult
{
    public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
    {
        ReasonPhrase = reasonPhrase;
        Request = request;
    }

    public string ReasonPhrase { get; private set; }

    public HttpRequestMessage Request { get; private set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute());
    }

    private HttpResponseMessage Execute()
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.RequestMessage = Request;
        response.ReasonPhrase = ReasonPhrase;
        return response;
    }
}

ChallengeAsync 구현Implementing ChallengeAsync

ChallengeAsync 메서드의 목적은 필요한 경우 응답에 인증 과제를 추가 하는 것입니다.The purpose of the ChallengeAsync method is to add authentication challenges to the response, if needed. 메서드 서명은 다음과 같습니다.Here is the method signature:

Task ChallengeAsync(
    HttpAuthenticationChallengeContext context,
    CancellationToken cancellationToken
)

메서드는 요청 파이프라인의 모든 인증 필터에서 호출 됩니다.The method is called on every authentication filter in the request pipeline.

ChallengeAsync 는 HTTP 응답을 만들기 전에 호출 되 고, 컨트롤러 작업이 실행 되기 전에도 호출 된다는 것을 이해 하는 것이 중요 합니다.It's important to understand that ChallengeAsync is called before the HTTP response is created, and possibly even before the controller action runs. ChallengeAsync 가 호출 되 면 나중에 HTTP 응답을 만드는 데 사용 되는 IHttpActionResultcontext.Result에 포함 됩니다.When ChallengeAsync is called, context.Result contains an IHttpActionResult, which is used later to create the HTTP response. 따라서 ChallengeAsync 를 호출 하면 HTTP 응답에 대 한 어떠한 정보도 알 수 없습니다.So when ChallengeAsync is called, you don't know anything about the HTTP response yet. ChallengeAsync 메서드는 context.Result의 원래 값을 새 IHttpActionResult바꿉니다.The ChallengeAsync method should replace the original value of context.Result with a new IHttpActionResult. IHttpActionResult 는 원래 context.Result을 래핑해야 합니다.This IHttpActionResult must wrap the original context.Result.

원래 IHttpActionResult 내부 결과를 호출 하 고 새 IHttpActionResult 외부 결과를 호출 합니다.I'll call the original IHttpActionResult the inner result, and the new IHttpActionResult the outer result. 외부 결과는 다음을 수행 해야 합니다.The outer result must do the following:

  1. 내부 결과를 호출 하 여 HTTP 응답을 만듭니다.Invoke the inner result to create the HTTP response.
  2. 응답을 검사 합니다.Examine the response.
  3. 필요한 경우 응답에 인증 챌린지를 추가 합니다.Add an authentication challenge to the response, if needed.

다음 예제는 기본 인증 샘플에서 가져온 것입니다.The following example is taken from the Basic Authentication sample. 외부 결과에 대 한 IHttpActionResult 를 정의 합니다.It defines an IHttpActionResult for the outer result.

public class AddChallengeOnUnauthorizedResult : IHttpActionResult
{
    public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
    {
        Challenge = challenge;
        InnerResult = innerResult;
    }

    public AuthenticationHeaderValue Challenge { get; private set; }

    public IHttpActionResult InnerResult { get; private set; }

    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await InnerResult.ExecuteAsync(cancellationToken);

        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            // Only add one challenge per authentication scheme.
            if (!response.Headers.WwwAuthenticate.Any((h) => h.Scheme == Challenge.Scheme))
            {
                response.Headers.WwwAuthenticate.Add(Challenge);
            }
        }

        return response;
    }
}

InnerResult 속성은 내부 IHttpActionResult를 포함 합니다.The InnerResult property holds the inner IHttpActionResult. Challenge 속성은 Www 인증 헤더를 나타냅니다.The Challenge property represents a Www-Authentication header. ExecuteAsync 는 먼저 InnerResult.ExecuteAsync를 호출 하 여 HTTP 응답을 만든 다음 필요한 경우 챌린지를 추가 합니다.Notice that ExecuteAsync first calls InnerResult.ExecuteAsync to create the HTTP response, and then adds the challenge if needed.

챌린지를 추가 하기 전에 응답 코드를 확인 합니다.Check the response code before adding the challenge. 대부분의 인증 체계는 다음과 같이 응답이 401 인 경우에만 챌린지를 추가 합니다.Most authentication schemes only add a challenge if the response is 401, as shown here. 그러나 일부 인증 스키마는 성공 응답에 챌린지를 추가 합니다.However, some authentication schemes do add a challenge to a success response. 예를 들어 Negotiate (RFC 4559)을 참조 하세요.For example, see Negotiate (RFC 4559).

AddChallengeOnUnauthorizedResult 클래스가 지정 된 경우 ChallengeAsync 의 실제 코드는 간단 합니다.Given the AddChallengeOnUnauthorizedResult class, the actual code in ChallengeAsync is simple. 결과를 만들어 context.Result에 연결 하기만 하면 됩니다.You just create the result and attach it to context.Result.

public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
    var challenge = new AuthenticationHeaderValue("Basic");
    context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
    return Task.FromResult(0);
}

참고: 기본 인증 샘플은 확장 메서드에 배치 하 여이 논리를 비트로 추상화 합니다.Note: The Basic Authentication sample abstracts this logic a bit, by placing it in an extension method.

호스트 수준 인증과 인증 필터 조합Combining Authentication Filters with Host-Level Authentication

"호스트 수준 인증"은 요청이 웹 API 프레임 워크에 도달 하기 전에 호스트 (예: IIS)에서 수행 하는 인증입니다."Host-level authentication" is authentication performed by the host (such as IIS), before the request reaches the Web API framework.

응용 프로그램의 나머지 부분에 대해 호스트 수준 인증을 사용 하도록 설정 하 고 Web API 컨트롤러에 대해 사용 하지 않도록 설정 하는 경우가 종종 있습니다.Often, you may want to enable host-level authentication for the rest of your application, but disable it for your Web API controllers. 예를 들어 일반적인 시나리오는 호스트 수준에서 폼 인증을 사용 하도록 설정 하 고 Web API에는 토큰 기반 인증을 사용 하는 것입니다.For example, a typical scenario is to enable Forms Authentication at the host level, but use token-based authentication for Web API.

웹 API 파이프라인 내에서 호스트 수준 인증을 사용 하지 않도록 설정 하려면 구성에서 config.SuppressHostPrincipal()를 호출 합니다.To disable host-level authentication inside the Web API pipeline, call config.SuppressHostPrincipal() in your configuration. 이렇게 하면 web API는 Web api 파이프라인으로 들어가는 모든 요청에서 IPrincipal 을 제거 합니다.This causes Web API to remove the IPrincipal from any request that enters the Web API pipeline. 효과적으로 요청" 인증을 취소할 ".Effectively, it "un-authenticates" the request.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SuppressHostPrincipal();

        // Other configuration code not shown...
    }
}

추가 리소스Additional Resources

보안 필터 ASP.NET Web API (MSDN Magazine)ASP.NET Web API Security Filters (MSDN Magazine)