none
WCF Basic аутентификация RRS feed

  • Вопрос

  • Привет! Я делаю basic-авторизацию в WCF. 

    Я в этом новичок и мои попытки основаны на серии статей http://leastprivilege.com/2008/01/11/http-basic-authentication-against-non-windows-accounts-in-iisasp-net-part-0-intro/ 
    Я использую привязку webHttpBinding и HTTPS включен

    Главная идея - реализовать интерфейс IHttpModule следующим образом

    1. Когда пользователь отправляет запрос, модуль проверяет присутствует ли в запросе заголовок Authorization
    2. Если Authorization присутствует, модуль извлекает его значение, декодирует логин и пароль из Base64 и проверяет совпадение
    3. Если Authorization отсутствует, модуль возвращает код 401 с заголовком WWW-Authenticate

    Вот моя реализация модуля:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Management;
    using System.Text;
    
    namespace MyProj_A
    {
        public class MyHTTPModule : IHttpModule
        {
    
            void IHttpModule.Dispose()
            {
            }
    
            void IHttpModule.Init(HttpApplication context)
            {
                context.BeginRequest += Context_BeginRequest;
                context.AuthenticateRequest += OnEnter;
                context.EndRequest += OnLeave;
            }
    
    
            private void Context_BeginRequest(object sender, EventArgs e)
            {
                HttpContext context = HttpContext.Current;
                context.Response.Write("BeginRequest");
            }
    
            void OnEnter(object sender, EventArgs e)
            {
                HttpContext context = HttpContext.Current;
                if (IsHeaderPresent())
                {
                    if (!AuthenticateUser())
                    {
                        DenyAccess();
                    }
                }
                else
                {
                    // if anonymous requests are not allowed - end the request
                    DenyAccess();
                }
            }
    
            bool IsHeaderPresent()
            {
                return HttpContext.Current.Request.Headers["Authorization"] != null;
            }
    
            bool AuthenticateUser()
            {
                string username = "", password = "";
                string authHeader = HttpContext.Current.Request.Headers["Authorization"];
                if (authHeader != null && authHeader.StartsWith("Basic"))
                {
                    // extract credentials from header
                    string[] credentials = ExtractCredentials(authHeader);
                    username = credentials[0];
                    password = credentials[1];
                    if (username.CompareTo("tikskit") == 0 && password.CompareTo("") == 0)
                    {
                        return true;
                    } else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
    
    
            private static void DenyAccess()
            {
                HttpContext context = HttpContext.Current;
    
                context.Response.StatusCode = 401;
                context.Response.End();
            }
    
            void OnLeave(object sender, EventArgs e)
            {
                // check if module is enabled
                if (HttpContext.Current.Response.StatusCode == 401)
                {
                    SendAuthenticationHeader();
                }
            }
    
            private void SendAuthenticationHeader()
            {
                HttpContext context = HttpContext.Current;
    
                context.Response.StatusCode = 401;
                context.Response.AddHeader(
                    "WWW-Authenticate",
                    "Basic realm=\"yo-ho-ho\""
                    );
    
            }
        }
    
    }
    

    И вот мой web.config

    <?xml version="1.0"?>
    <configuration>
    
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.5.2" />
        <httpRuntime targetFramework="4.5.2"/>
        <customErrors mode="Off" />
      </system.web>
    
    
      <system.serviceModel>
        <behaviors>
    
          <endpointBehaviors>
            <behavior name="webBehavior">
              <webHttp automaticFormatSelectionEnabled="false"/>
            </behavior>
          </endpointBehaviors>
    
           <serviceBehaviors>
            <behavior name="Default" >
              <serviceMetadata httpGetEnabled="false" />
              <serviceMetadata httpsGetEnabled="false"/>
              <serviceAuthenticationManager authenticationSchemes="Basic"/>
              <serviceCredentials>
              </serviceCredentials>
    
            </behavior>
          </serviceBehaviors>
    
        </behaviors>
    
        <bindings>      
          <webHttpBinding>
            <binding name="MyBinding">
              <security mode="TransportCredentialOnly">
                <transport clientCredentialType="Basic"/>
              </security>
            </binding>
          </webHttpBinding>
        </bindings>
    
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    
        <services>
          <service name="MyProj_A.Service1">
            <endpoint address="" binding="webHttpBinding" contract="MyProj_A.IService1"
                      behaviorConfiguration="webBehavior"/>
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/" />
              </baseAddresses>
            </host>
          </service>
        </services>
        <diagnostics>
          <endToEndTracing activityTracing="false" messageFlowTracing="true" propagateActivity="true"></endToEndTracing>
        </diagnostics>
      </system.serviceModel>
    
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
          <add name="MyHTTPModule"
            type="MyProj_A.MyHTTPModule,MyProj-A"/>
        </modules>
    
        <directoryBrowse enabled="false"/>
      </system.webServer>
    
    </configuration>
    

    Я компилирую и публикую проект под IIS 7.5 на удаленном компьютере и подключаюсь к нему из Visual Studio через remote debugger, предварительно расставив брейкпойнты в Context_BeginRequest, OnEnter и OnLeave.

    Потом я отправляю запрос к моей WCF из браузера, используя URL. И вот что происходит:

    1. После того, как я жму в браузере Enter, срабатывает Context_BeginRequest
    2. Там я могу видеть из VS, что заголовок Authorization в запросе отсутствует
    3. Срабатывает OnEnter, где в конце концов ответу присваивается код 401
    4. Срабатывает OnLeave, где в ответ добавляется заголовок WWW-Authenticate
    5. В браузере отображается стандартное окно запроса пароля и логина
    6. Ввожу в этом окне логин и пароль и жму ОК
    7. Срабатывает Context_BeginRequest, где я вижу, что заголовок Authorization присутствует с значением "Basic <логин и пароль в Base64>"
    8. На этот раз OnEnter не срабатывает
    9. Выполнение попадает в OnLeave, где значение HttpContext.Current.Response.StatusCode уже почему-то равно 401

    У меня следующие вопросы:

    1. Почему OnEnter не срабатывает во второй раз, когда браузер присылает запрос с Authorization в заголовках?Вот тут (https://msdn.microsoft.com/en-us/library/system.web.httpapplication.authenticaterequest.aspx) сказано, что AuthenticateRequest срабатывает тогда, когда пользователь уже прошел аутентификацию. Тогда я не понимаю, почему OnEnter срабатывает в первый раз, когда никто еще не вводил никакого пароля? У меня каким-то образом включена анонимная аутентификация? Я в IIS удалил все модули аутентификации, кроме BasicAuthenticationModule! Манипуляции с <authentication mode="Forms"/> в <system.web> тоже ничего не дали
    2. Почему, когда браузер присылает запрос с Authorization, в OnLeave код ответа уже установлен 401? Что и где это устанавливает?
    3. Как мне правильно настроить мой web.config и IIS и как правильно реализовать Basic-аутентификацию?


    Спасибо!





    19 января 2016 г. 10:34

Ответы

  • Вопрос закрыт

    Забыл сослаться на конфигурацию binding в конфигурировании endpoint
    <endpoint address="" binding="webHttpBinding" contract="MyProj_A.IService1"                  behaviorConfiguration="webBehavior"                  bindingConfiguration="MyBinding"/>


    • Предложено в качестве ответа YatajgaEditor 22 января 2016 г. 18:43
    • Помечено в качестве ответа YatajgaEditor 22 января 2016 г. 18:43
    21 января 2016 г. 9:37