2012-09-26 25 views
8

Sto tentando di implementare l'autenticazione per un servizio REST implementato in WCF e ospitato su Azure. Sto usando HttpModule per gestire gli eventi AuthenticationRequest, PostAuthenticationRequest e EndRequest. Se manca l'intestazione di autorizzazione o se il token in esso contenuto non è valido, durante EndRequest sto impostando lo StatusCode sulla risposta su 401. Tuttavia, ho determinato che EndRequest viene chiamato due volte e nella seconda chiamata la risposta ha già intestazioni impostato, causando il codice che imposta lo StatusCode per generare un'eccezione.HttpModule EndRequest gestore chiamato due volte

Ho aggiunto i blocchi a Init() per garantire che il gestore non venisse registrato due volte; ancora funzionato due volte. Init() ha anche eseguito due volte, a indicare che due istanze di HttpModule erano state create. Tuttavia, l'utilizzo di Set Object ID nel debugger VS sembra indicare che le richieste sono in realtà richieste diverse. Ho verificato in Fiddler che è stata rilasciata una sola richiesta al mio servizio dal browser.

Se si passa all'utilizzo del routing global.asax anziché a seconda della configurazione dell'host del servizio WCF, il gestore viene chiamato una sola volta e tutto funziona correttamente.

Se aggiungo la configurazione alla sezione di configurazione system.web e alla sezione di configurazione system.webServer in Web.config, il gestore viene chiamato una sola volta e tutto funziona correttamente.

Quindi ho delle attenuazioni, ma non mi piace il comportamento che non capisco. Perché il gestore viene chiamato due volte?

Ecco un repro minima del problema:

Web.config:

<system.web> 
    <compilation debug="true" targetFramework="4.0" /> 
    <!--<httpModules> 
     <add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/> 
    </httpModules>--> 
    </system.web> 
    <system.serviceModel> 
    <behaviors> 
     <endpointBehaviors> 
     <behavior name="WebBehavior"> 
      <webHttp/> 
     </behavior> 
     </endpointBehaviors> 
     <serviceBehaviors> 
     <behavior> 
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> 
      <serviceMetadata httpGetEnabled="true" /> 
      <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> 
      <serviceDebug includeExceptionDetailInFaults="true"/> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" /> 
    <services> 
     <service name="TestWCFRole.Service1"> 
     <endpoint binding="webHttpBinding" name="RestEndpoint" contract="TestWCFRole.IService1" bindingConfiguration="HttpSecurityBinding" behaviorConfiguration="WebBehavior"/> 
     <host> 
      <baseAddresses> 
      <add baseAddress="http://localhost/" /> 
      </baseAddresses> 
     </host> 
     </service> 
    </services> 
    <standardEndpoints> 
     <webHttpEndpoint> 
     <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/> 
     </webHttpEndpoint> 
    </standardEndpoints> 
    <bindings> 
     <webHttpBinding> 
     <binding name="HttpSecurityBinding" > 
      <security mode="None" /> 
     </binding> 
     </webHttpBinding> 
    </bindings> 
    </system.serviceModel> 
    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
     <add name="AuthModule" type="TestWCFRole.AuthModule, TestWCFRole"/> 
    </modules> 
    <directoryBrowse enabled="true"/> 
    </system.webServer> 

modulo HTTP:

using System; 
using System.Web; 

namespace TestWCFRole 
{ 
    public class AuthModule : IHttpModule 
    { 
     /// <summary> 
     /// You will need to configure this module in the web.config file of your 
     /// web and register it with IIS before being able to use it. For more information 
     /// see the following link: http://go.microsoft.com/?linkid=8101007 
     /// </summary> 
     #region IHttpModule Members 

     public void Dispose() 
     { 
      //clean-up code here. 
     } 

     public void Init(HttpApplication context) 
     { 
      // Below is an example of how you can handle LogRequest event and provide 
      // custom logging implementation for it 
      context.EndRequest += new EventHandler(OnEndRequest); 
     } 

     #endregion 

     public void OnEndRequest(Object source, EventArgs e) 
     { 
      HttpContext.Current.Response.StatusCode = 401; 
     } 
    } 
} 
+0

Usi URLRewrite nella vostra applicazione? Sembra che causi l'arresto di EndRequest due volte. –

+0

Può essere abilitato in background? Non ho il UrlRewriteModule nel mio web.config. –

+0

Io non la penso così. Questo è davvero strano, perché non posso dire che tutto il mio problema causato da UrlRewriteModule. Ma alcuni di loro lo sono. –

risposta

2

Non ho idea del motivo per cui potrebbe essere chiamato due volte, tuttavia EndRequest può essere chiamato per più motivi. richiesta terminata, la richiesta è stata interrotta, si è verificato un errore. Quindi non mi fido di pensare che se ci arrivi, hai un 401, potrebbe essere per altri motivi.

avevo appena mantenere il mio logica in cantiere AuthenticateRequest:

public class AuthenticationModule : IHttpModule 
    { 
     public void Dispose() { } 

     public void Init(HttpApplication context) 
     { 
      context.AuthenticateRequest += Authenticate; 
     } 

     public static void Authenticate(object sender, EventArgs e) 
     { 
      // authentication logic here    
      //............. 

      if (authenticated) { 
       HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(myUser, myRoles); 
      } 

      // failure logic here   
      //.............   
     } 
    } 
+0

Sì, non riesco a capire perché non mi ero reso conto di come avrei dovuto affrontarlo prima (suppongo di aver pensato che il campione che stavo usando lo stesse facendo "bene"), ma alla fine ho spostato la logica in AuthenticateRequest , Sezioni PostAuthenticateRequest e AuthorizeRequest. Sfortunatamente, questo non risponde alla domanda sul perché il gestore si comporta in questo modo. –

7

Quando un'applicazione ASP.net viene avviato, per ottimizzare le prestazioni il processo ASP.NET Worker creerà istantaneamente il numero di oggetti HttpApplication necessario. Ogni oggetto HttpApplication, inoltre, crea un'istanza di una copia di ogni IHttpModule registrato e chiama il metodo Init! Questo è in realtà un progetto interno del processo ASP.NET che gira sotto IIS (o cassini che è VS costruito nel webserver). Potrebbe essere perché la pagina ASPX ha collegamenti ad altre risorse che il tuo browser tenterà di scaricare, una risorsa esterna e iframe, un file css o forse un comportamento del processo di lavoro ASP.NET.

Per fortuna non è il caso per Global.asax:

Here's from MSDN:

I metodi Application_Start e Application_End sono metodi speciali che non rappresentano eventi HttpApplication. ASP.NET li chiama una volta per la durata del dominio dell'applicazione, non per ciascuna istanza HttpApplication .

Tuttavia HTTPModule's init metodo viene chiamato una volta per ogni istanza della classe HttpApplication dopo tutti i moduli sono stati creati

La prima volta che una pagina ASP.NET o un processo è richiesto in un'applicazione , una nuova l'istanza di HttpApplication è stata creata.Tuttavia, per ottimizzare le prestazioni di , le istanze di HttpApplication potrebbero essere riutilizzate per più richieste .

e illustrato dal seguente schema: enter image description here

Se si vuole il codice che è garantito per eseguire una sola volta, è possibile utilizzare Application_Start del Global.asax o impostare una bandiera e bloccarlo nel modulo sottostante, che è che non penso sia una buona pratica per amore dell'autenticazione!

+0

Mi piacerebbe solo che l'impostazione 'runAllManagedModulesForAllRequests =" true "' causi l'esecuzione del modulo personalizzato per ogni singola richiesta, inclusi i file statici (JS/CSS/Immagini). Quindi solo due volte per un caricamento di una singola pagina mi sembra anche troppo poco per me con quella configurazione. – astaykov

+0

Non ho bisogno del codice per eseguire una singola volta; il problema è che vedo due EndRequests per una singola richiesta al servizio. Non dovrebbe essere JS/CSS/Immagini, perché questo è un servizio, non una pagina web. Riesco a vedere in Fiddler che viene fatta una sola richiesta. La domanda è perché vedo una singola richiesta tramite Fiddler ma due richieste nel modulo e perché questo problema scompare se duplo la configurazione nel web.config. –

+0

Normalmente non è necessario duplicare la configurazione, se si utilizza IIS7 + la configurazione deve essere impostata nella sezione 'system.webServer'! Il metodo 'Init' del modulo viene chiamato una volta mentre' EndRequest' viene chiamato due volte per una singola richiesta? perché 'EndRequest' non può essere chiamato due volte per richiesta! Tuttavia noto che stai abilitando 'aspNetCompatibilityEnabled' nella configurazione dell'host WCF, con quale server web stai testando? IIS? IIS Express? VS Web Server? Come si ospita il WCF in IIS? A 'WebServiceHost'? File '.svc'? Puoi pubblicare 'HttpContext.Current.Request.Path' in EndRequest? –

Problemi correlati