ASP.NET Web API 2'de Kimlik Doğrulama Filtreleri

Tarafından Mike Wasson

Kimlik doğrulama filtresi, HTTP isteğinin kimliğini doğrulayan bir bileşendir. Web API 2 ve MVC 5'in her ikisi de kimlik doğrulama filtrelerini destekler, ancak çoğunlukla filtre arabirimi adlandırma kurallarında biraz farklılık gösterir. Bu konuda Web API kimlik doğrulama filtreleri açıklanmaktadır.

Kimlik doğrulama filtreleri, tek tek denetleyiciler veya eylemler için bir kimlik doğrulama düzeni ayarlamanıza olanak sağlar. Bu şekilde, uygulamanız farklı HTTP kaynakları için farklı kimlik doğrulama mekanizmalarını destekleyebilir.

Bu makalede, üzerinde https://github.com/aspnet/samplesTemel Kimlik Doğrulaması örneğinden kod göstereceğim. Örnekte HTTP Temel Erişim Kimlik Doğrulaması şemasını (RFC 2617) uygulayan bir kimlik doğrulama filtresi gösterilmektedir. Filtre adlı IdentityBasicAuthenticationAttributebir sınıfta uygulanır. Örnekteki tüm kodları değil, yalnızca kimlik doğrulama filtresinin nasıl yazıldığını gösteren bölümleri göstermeyeceğim.

Kimlik Doğrulama Filtresi Ayarlama

Diğer filtreler gibi kimlik doğrulama filtreleri de denetleyici başına, eylem başına veya genel olarak tüm Web API denetleyicilerine uygulanabilir.

Bir denetleyiciye kimlik doğrulama filtresi uygulamak için denetleyici sınıfını filter özniteliğiyle süsleyin. Aşağıdaki kod, denetleyicinin tüm eylemleri için Temel Kimlik Doğrulaması'nı etkinleştiren bir denetleyici sınıfında filtreyi ayarlar [IdentityBasicAuthentication] .

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

Filtreyi tek bir eyleme uygulamak için eylemi filtreyle süsleyin. Aşağıdaki kod, denetleyicinin [IdentityBasicAuthentication]Post yönteminde filtreyi ayarlar.

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

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

Filtreyi tüm Web API denetleyicilerine uygulamak için GlobalConfiguration.Filters'a ekleyin.

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

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

Web API Kimlik Doğrulama Filtresi Uygulama

Web API'sinde, kimlik doğrulama filtreleri System.Web.Http.Filters.IAuthenticationFilter arabirimini uygular. Ayrıca öznitelik olarak uygulanabilmeleri için System.Attribute'tan devralmaları gerekir.

IAuthenticationFilter arabiriminin iki yöntemi vardır:

  • AuthenticateAsync , varsa istekteki kimlik bilgilerini doğrulayarak isteğin kimliğini doğrular.
  • ChallengeAsync gerekirse HTTP yanıtına bir kimlik doğrulama sınaması ekler.

Bu yöntemler RFC 2612 ve RFC 2617'de tanımlanan kimlik doğrulama akışına karşılık gelir:

  1. İstemci kimlik bilgilerini Yetkilendirme üst bilgisinde gönderir. Bu durum genellikle istemci sunucudan 401 (Yetkisiz) yanıtı aldıktan sonra gerçekleşir. Ancak, bir istemci yalnızca 401 aldıktan sonra değil, herhangi bir istekle kimlik bilgileri gönderebilir.
  2. Sunucu kimlik bilgilerini kabul etmiyorsa, bir 401 (Yetkisiz) yanıtı döndürür. Yanıt, bir veya daha fazla zorluk içeren bir Www-Authenticate üst bilgisi içerir. Her sınama, sunucu tarafından tanınan bir kimlik doğrulama şeması belirtir.

Sunucu ayrıca anonim bir istekten 401 döndürebilir. Aslında, kimlik doğrulama işlemi genellikle böyle başlatılır:

  1. İstemci anonim bir istek gönderir.
  2. Sunucu 401 döndürür.
  3. İstemciler isteği kimlik bilgileriyle yeniden gönderebilir.

Bu akış hem kimlik doğrulaması hem de yetkilendirme adımlarını içerir.

  • Kimlik doğrulaması, istemcinin kimliğini kanıtlar.
  • Yetkilendirme, istemcinin belirli bir kaynağa erişip erişemeyeceğini belirler.

Web API'sinde kimlik doğrulama filtreleri kimlik doğrulamasını işler ancak yetkilendirmeyi işlemez. Yetkilendirme, yetkilendirme filtresi tarafından veya denetleyici eyleminin içinde yapılmalıdır.

Web API 2 işlem hattındaki akış aşağıda verilmiştir:

  1. Bir eylemi çağırmadan önce, Web API'si bu eylem için kimlik doğrulama filtrelerinin listesini oluşturur. Buna eylem kapsamı, denetleyici kapsamı ve genel kapsama sahip filtreler dahildir.
  2. Web API'si listedeki her filtrede AuthenticateAsync'i çağırır. Her filtre istekteki kimlik bilgilerini doğrulayabilir. Herhangi bir filtre kimlik bilgilerini başarıyla doğrularsa, filtre bir IPrincipal oluşturur ve isteğine ekler. Bir filtre bu noktada bir hatayı da tetikleyebilir. Bu durumda işlem hattının geri kalanı çalışmaz.
  3. Hata olmadığını varsayarsak istek işlem hattının geri kalanından geçer.
  4. Son olarak, Web API'si her kimlik doğrulama filtresinin ChallengeAsync yöntemini çağırır. Filtreler gerekirse yanıta bir sınama eklemek için bu yöntemi kullanır. Genellikle (ancak her zaman değil) bir 401 hatasına yanıt olarak gerçekleşir.

Aşağıdaki diyagramlarda iki olası durum gösterilmiştir. İlkinde, kimlik doğrulama filtresi isteğin kimliğini başarıyla doğrular, yetkilendirme filtresi isteği yetkilendirir ve denetleyici eylemi 200 döndürür (Tamam).

Başarılı kimlik doğrulaması diyagramı

İkinci örnekte kimlik doğrulama filtresi isteğin kimliğini doğrular ancak yetkilendirme filtresi 401 (Yetkisiz) değerini döndürür. Bu durumda denetleyici eylemi çağrılmıyor. Kimlik doğrulama filtresi yanıta bir Www-Authenticate üst bilgisi ekler.

Yetkisiz kimlik doğrulaması diyagramı

Diğer birleşimler de mümkündür; örneğin, denetleyici eylemi anonim isteklere izin veriyorsa, kimlik doğrulama filtreniz olabilir ancak yetkilendirmeniz olmayabilir.

AuthenticateAsync Yöntemini Uygulama

AuthenticateAsync yöntemi isteğin kimliğini doğrulamayı dener. Yöntem imzası şöyledir:

Task AuthenticateAsync(
    HttpAuthenticationContext context,
    CancellationToken cancellationToken
)

AuthenticateAsync yöntemi aşağıdakilerden birini yapmalıdır:

  1. Hiçbir şey (çalışmadan).
  2. Bir IPrincipal oluşturun ve istekte ayarlayın.
  3. Bir hata sonucu ayarlayın.

(1) seçeneği, isteğin filtrenin anladığı hiçbir kimlik bilgisi olmadığı anlamına gelir. Seçenek (2), filtrenin isteğin kimliğini başarıyla doğrulamış olduğu anlamına gelir. Seçenek (3), isteğin geçersiz kimlik bilgilerine (yanlış parola gibi) sahip olduğu anlamına gelir ve bu da bir hata yanıtı tetikler.

AuthenticateAsync'i uygulamak için genel bir ana hat aşağıdadır.

  1. İstekte kimlik bilgilerini arayın.
  2. Kimlik bilgileri yoksa, hiçbir şey yapma ve geri dönme (işlem yapılmaz).
  3. Kimlik bilgileri varsa ancak filtre kimlik doğrulama düzenini tanımıyorsa, hiçbir şey yapma ve geri dönme (işlem yapılmaz). İşlem hattındaki başka bir filtre şemayı anlayabilir.
  4. Filtrenin anladığı kimlik bilgileri varsa kimliklerini doğrulamayı deneyin.
  5. Kimlik bilgileri hatalıysa ayarını context.ErrorResultyaparak 401 değerini döndürin.
  6. Kimlik bilgileri geçerliyse, bir IPrincipal oluşturun ve ayarlayın context.Principal.

Aşağıdaki kod, Temel Kimlik Doğrulaması örneğindeki AuthenticateAsync yöntemini gösterir. Açıklamalar her adımı gösterir. Kod çeşitli hata türlerini gösterir: Kimlik bilgisi olmayan yetkilendirme üst bilgisi, hatalı biçimlendirilmiş kimlik bilgileri ve hatalı kullanıcı adı/parola.

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;
    }

}

Hata Sonucu Ayarlama

Kimlik bilgileri geçersizse, filtrenin hata yanıtı oluşturan bir IHttpActionResult olarak ayarlanması context.ErrorResult gerekir. IHttpActionResult hakkında daha fazla bilgi için bkz. Web API 2'de Eylem Sonuçları.

Temel Kimlik Doğrulaması örneği, bu amaca uygun bir AuthenticationFailureResult sınıf içerir.

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 Uygulama

ChallengeAsync yönteminin amacı, gerekirse yanıta kimlik doğrulama sınamaları eklemektir. Yöntem imzası şöyledir:

Task ChallengeAsync(
    HttpAuthenticationChallengeContext context,
    CancellationToken cancellationToken
)

yöntemi, istek işlem hattındaki her kimlik doğrulama filtresinde çağrılır.

ChallengeAsync'in HTTP yanıtı oluşturulmadan önce ve denetleyici eylemi çalışmadan önce bile çağrıldığını anlamak önemlidir. ChallengeAsync çağrıldığında, context.Result daha sonra HTTP yanıtını oluşturmak için kullanılan bir IHttpActionResult içerir. Dolayısıyla ChallengeAsync çağrıldığında HTTP yanıtı hakkında henüz bir bilginiz yoktur. ChallengeAsync yöntemi özgün değerini context.Result yeni bir IHttpActionResult ile değiştirmelidir. Bu IHttpActionResult özgün context.Resultöğesini sarmalamalıdır.

ChallengeAsync diyagramı

Özgün IHttpActionResult öğesini iç sonucu ve yeni IHttpActionResult'udış sonucu çağıracağım. Dış sonuç aşağıdakileri yapmalıdır:

  1. HTTP yanıtını oluşturmak için iç sonucu çağırın.
  2. Yanıtı inceleyin.
  3. Gerekirse yanıta bir kimlik doğrulama sınaması ekleyin.

Aşağıdaki örnek, Temel Kimlik Doğrulaması örneğinden alınmıştır. Dış sonuç için bir IHttpActionResult tanımlar.

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;
    }
}

özelliği, InnerResultIHttpActionResult öğesini barındırıyor. Challenge özelliği bir Www-Authentication üst bilgisini temsil eder. ExecuteAsync'in ilk olarak HTTP yanıtını oluşturmak için çağırdığını InnerResult.ExecuteAsync ve gerekirse sınamayı eklediğine dikkat edin.

Sınamayı eklemeden önce yanıt kodunu denetleyin. Kimlik doğrulama düzenlerinin çoğu, burada gösterildiği gibi yalnızca yanıt 401 olduğunda bir sınama ekler. Ancak, bazı kimlik doğrulama düzenleri başarı yanıtına bir sınama ekler. Örneğin, bkz . Anlaşma (RFC 4559).

sınıfı göz AddChallengeOnUnauthorizedResult önünde bulundurulduğunda, ChallengeAsync'teki gerçek kod basittir. Sonucu oluşturup öğesine eklemeniz gerekir 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);
}

Not: Temel Kimlik Doğrulaması örneği, bu mantığı bir uzantı yöntemine yerleştirerek biraz soyutlar.

Kimlik Doğrulama Filtrelerini Host-Level Kimlik Doğrulaması ile Birleştirme

"Konak düzeyinde kimlik doğrulaması", istek Web API çerçevesine ulaşmadan önce konak (IIS gibi) tarafından gerçekleştirilen kimlik doğrulamasıdır.

Çoğu zaman, uygulamanızın geri kalanı için konak düzeyinde kimlik doğrulamasını etkinleştirmek, ancak Web API denetleyicileriniz için devre dışı bırakmak isteyebilirsiniz. Örneğin, tipik bir senaryo, Form Kimlik Doğrulamasını konak düzeyinde etkinleştirmek, ancak Web API'si için belirteç tabanlı kimlik doğrulaması kullanmaktır.

Web API işlem hattı içinde konak düzeyinde kimlik doğrulamasını devre dışı bırakmak için yapılandırmanızda çağrısı config.SuppressHostPrincipal() yapın. Bu, Web API'sinin Web API işlem hattına giren tüm isteklerden IPrincipal'ı kaldırmasına neden olur. Etkili bir şekilde isteğin "kimlik doğrulamasını kaldırıyor".

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

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

Ek Kaynaklar

ASP.NET Web API Güvenlik Filtreleri (MSDN Dergisi)