При аутентификации службы WCF в IIS AuthenticateRequest вызывается только в первый раз

У меня есть рабочая реализация службы OData WCF, которая теперь должна быть опубликована в IIS с базовой пользовательской аутентификацией.

Реализация основана на примере Microsoft OData и прекрасно работает в IIS Express. Когда я публикую его в IIS 7.5 с включенной только базовой аутентификацией, обработчик AuthenticateRequest вызывается только при первоначальном запросе, который возвращает код состояния 401 и запрашивает аутентификацию.

AuthenticateRequest больше не вызывается при последующих запросах. При отладке службы в IIS определенно вызывается BeginRequest, просто AuthenticateRequest отсутствует в конвейере? Оба вызываются каждый запрос в IIS Express.

Конфигурация аутентификации IIS:

Конфигурация аутентификации IIS

IHttpModule code:

public class BasicAuthModule: IHttpModule
{
    // based on http://msdn.microsoft.com/en-gb/data/gg192997.aspx

    public void Init(HttpApplication app)
    {
        app.AuthenticateRequest += AuthenticateRequest;
        app.BeginRequest += BeginRequest;
    }

    private void BeginRequest(object sender, EventArgs e)
    {
        var app = (HttpApplication)sender;
        if(app.Context == null)
        {
            throw new Exception("Will not happen");
        }
    }

    private void AuthenticateRequest(object sender, EventArgs e)
    {
        var app = (HttpApplication)sender;
        if(!app.Request.Headers.AllKeys.Contains("Authorization"))
        {
            CreateNotAuthorizedResponse(app, 401, 1, "Please provide Authorization headers with your request.");
            app.CompleteRequest();
        }
        else if(!BasicAuthProvider.Authenticate(app.Context))
        {
            CreateNotAuthorizedResponse(app, 401, 1, "Logon failed.");
            app.CompleteRequest();
        }
    }

    private static void CreateNotAuthorizedResponse(HttpApplication app, int code, int subCode, string description)
    {
        var response = app.Context.Response;
//      response.Status = "401 Unauthorized";
        response.StatusCode = code;
        response.SubStatusCode = subCode;
        response.StatusDescription = description;
//      response.AppendHeader("WWW-Authenticate", "Basic");
//      response.End();
    }

    public void Dispose()
    {
    }
}

Web.config:

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
      <modules runAllManagedModulesForAllRequests="true">
          <add name="BasicAuthModule" type="WcfTestService.BasicAuthModule"/>
      </modules>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

И вопрос: почему аутентификация работает в отладочном сервере Visual Studio 2012, а не в IIS 7.5? Полный тестовый проект можно скачать здесь.

Редактировать:

Я закомментировал избыточный тестовый код в функции CreateNotAuthorizedResponse и response.End(), который вызвал исключение (я добавил его в последнюю минуту перед публикацией).

При проверке запросов создается впечатление, что все должно работать, за исключением того, что Cookie по какой-то причине может пропустить проверку подлинности IIS. Ниже первых 2 пар запроса - ответа:

Запрос 1:

GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788

Ответ 1: (создан методом CreateNotAuthorizedResponse)

HTTP/1.1 401 Please provide Authorization headers with your request.
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:14 GMT
Content-Length: 6607

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 7.5 Detailed Error - 401.1 - Please provide Authorization headers with your request.</title> 

Запрос 2 (при вводе теста: тест - dGVzdDp0ZXN0):

GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic dGVzdDp0ZXN0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788

Ответ 2 (BeginRequest вызван, но не AuthenticateRequest):

HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:17 GMT
Content-Length: 6531

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 7.5 Detailed Error - 401.1 - Unauthorized</title> 

1 ответ

Решение

Я думаю, что вы смешиваете встроенную базовую аутентификацию IIS с вашим собственным модулем аутентификации. Короткий ответ - отключить базовую аутентификацию в IIS и включить анонимный доступ. Это передаст всю работу по аутентификации на asp.net.

Если вы тестируете в VS, я предполагаю, что вы делаете это через браузер, который автоматически запускается, когда вы нажимаете F5.

При включенной базовой аутентификации IIS сначала отвечает 401, что заставляет браузер отображать форму входа.

Учетные данные, которые вы вводите, должны быть действительными учетными данными Windows, которые IIS проверяет по вашим учетным записям Windows. Как только IIS проверит эти учетные данные, он передаст запрос вашему коду.

Если вы введете действительные учетные данные Windows, событие возникает, но ваш код отклонит его, потому что учетные данные не тестируются / тестируются и возвращают 401.1.

Если вы введете test/test, IIS отклонит учетные данные и отправит обратно 401, чтобы ваше событие никогда не вызывалось.

Заключительное слово: вы должны тестировать свой веб-сервис с помощью http-клиента (например, модульное тестирование с System.Net.WebClient) или использовать плагин chrome (postman / devhttp) для тестирования на уровне http. Если вы уже делаете это, то прости мое предположение.

Другие вопросы по тегам