Condividi tramite


Sviluppo di un modulo con .NET

di Mike Volodarsky

Introduzione

IIS 7.0 e versioni successive consente di estendere il server in base ai moduli sviluppati in due modi:

  • Uso del codice gestito e delle API di estendibilità del server ASP.NET
  • Uso del codice nativo e delle API di estendibilità del server nativo IIS

In passato, ASP.NET moduli erano limitati in funzionalità, poiché la pipeline di elaborazione delle richieste ASP.NET era separata dalla pipeline di richiesta del server principale.

In IIS i moduli gestiti diventano praticamente potenti come moduli nativi con l'architettura della pipeline integrata. Più importante, i servizi forniti dai moduli gestiti possono ora essere applicati a tutte le richieste al server, non solo alle richieste di ASP.NET contenuto come pagine ASPX. I moduli gestiti vengono configurati e gestiti in modo coerente con i moduli nativi e possono essere eseguiti nelle stesse fasi di elaborazione e ordinamento dei moduli nativi. Infine, i moduli gestiti possono eseguire un set più ampio di operazioni per modificare l'elaborazione delle richieste tramite diverse API di ASP.NET aggiunte e migliorate.

Questo articolo illustra l'estensione del server con un modulo gestito per aggiungere la possibilità di eseguire l'autenticazione di base in un archivio di credenziali arbitrario, ad esempio l'infrastruttura delle credenziali basata sul provider nel sistema di appartenenza ASP.NET 2.0.

Ciò consente di sostituire il supporto di autenticazione di base predefinito in IIS, associato all'archivio credenziali di Windows, con uno che supporta archivi di credenziali arbitrari o uno dei provider di appartenenze esistenti forniti con ASP.NET 2.0 come SQL Server, SQL Express o Active Directory.

Questo articolo esamina le attività seguenti:

  • Sviluppo di un modulo gestito con API ASP.NET
  • Distribuzione di un modulo gestito nel server

Per altre informazioni sulle nozioni di base sullo sviluppo di moduli e gestori IIS, vedere Sviluppo di moduli e gestori IIS7 con .NET Framework.

È anche possibile trovare numerose risorse e suggerimenti sulla scrittura di moduli IIS nel blog, http://www.mvolo.com/, nonché scaricare i moduli IIS esistenti per le applicazioni. Per alcuni esempi, vedere Reindirizzamento delle richieste all'applicazione con il modulo HttpRedirection, elenchi di directory interessanti per il sito Web IIS con DirectoryListingModule e Visualizzazione di icone di file piuttosto belle nelle applicazioni ASP.NET con IconHandler.

Nota

Il codice fornito in questo articolo è scritto in C#.

Prerequisiti

Per seguire la procedura descritta in questo documento, è necessario che siano installate le funzionalità IIS seguenti:

ASP.NET

Installare ASP.NET tramite windows Vista Pannello di controllo. Selezionare "Programmi" - "Attiva o disattiva funzionalità di Windows". Aprire quindi "Internet Information Services" - "World Wide Web Services" - "Funzionalità di sviluppo di applicazioni" e selezionare "ASP.NET".

Se si dispone di una build di Windows Server® 2008, aprire "Server Manager" - "Ruoli" e selezionare "Server Web (IIS)". Fare clic su "Aggiungi servizi ruolo". In "Sviluppo di applicazioni" selezionare "ASP.NET".

Informazioni generali sull'autenticazione di base

L'autenticazione di base è uno schema di autenticazione definito nel protocollo HTTP.1 (RFC 2617). Usa un meccanismo standard basato su sfida che funziona come segue a livello generale:

  • Il browser effettua una richiesta a un URL senza credenziali
  • Se il server richiede l'autenticazione per tale URL, risponde con un messaggio 401 Accesso negato e include un'intestazione che indica che lo schema di autenticazione di base è supportato
  • Browser riceve la risposta e, se configurata, richiederà all'utente un nome utente o una password che includerà in testo normale all'interno di un'intestazione della richiesta per la richiesta successiva all'URL
  • Il server riceve il nome utente/password all'interno di un'intestazione e li usa per l'autenticazione

Nota

Anche se una discussione dettagliata di questo protocollo di autenticazione non rientra nell'ambito di questo articolo, vale la pena menzionare che lo schema di autenticazione di base richiede che SSL sia sicuro, in quanto invia il nome utente/password in testo normale.

IIS include il supporto per l'autenticazione di base per gli account di Windows archiviati nell'archivio account locale o Active Directory per gli account di dominio. Si vuole consentire all'utente di eseguire l'autenticazione usando l'autenticazione di base, ma per convalidare le credenziali usando ASP.NET servizio di appartenenza 2.0. In questo modo è possibile archiviare le informazioni utente in un'ampia gamma di provider di appartenenze esistenti, ad esempio SQL Server, senza essere associati agli account di Windows.

Attività 1: Sviluppo di un modulo con .NET

In questa attività viene esaminato lo sviluppo di un modulo di autenticazione che supporta lo schema di autenticazione di base HTTP.1. Questo modulo è stato sviluppato usando il modello di modulo ASP.NET standard disponibile a partire da ASP.NET v1.0. Questo stesso modello viene usato per compilare ASP.NET moduli che estendono il server IIS. In realtà, i moduli ASP.NET esistenti scritti per le versioni precedenti di IIS possono essere usati in IIS e sfruttare meglio l'integrazione ASP.NET per offrire maggiore potenza alle applicazioni Web che le usano.

Nota

Il codice completo per il modulo è disponibile nell'Appendice A.

Un modulo gestito è una classe .NET che implementa l'interfaccia System.Web.IHttpModule . La funzione primaria di questa classe consiste nel registrare uno o più eventi che si verificano all'interno della pipeline di elaborazione delle richieste IIS e quindi eseguire alcune operazioni utili quando IIS richiama i gestori eventi del modulo per tali eventi.

Consente di creare un nuovo file di origine denominato "BasicAuthenticationModule.cs" e creare la classe del modulo (il codice sorgente completo è disponibile nell'Appendice A):

public class BasicAuthenticationModule : System.Web.IHttpModule
{
    void Init(HttpApplication context)
    {
    }
    void Dispose()
    {
    }
}

La funzione primaria del metodo Init consiste nel collegare i metodi del gestore eventi del modulo agli eventi della pipeline di richiesta appropriati. La classe del modulo fornisce i metodi di gestione degli eventi e implementa le funzionalità desiderate fornite dal modulo. Questo argomento viene illustrato più dettagliatamente.

Il metodo Dispose viene usato per pulire qualsiasi stato del modulo quando l'istanza del modulo viene eliminata. In genere non viene implementato a meno che il modulo non usi risorse specifiche che richiedono il rilascio.

Init()

Dopo aver creato la classe, il passaggio successivo consiste nell'implementare il metodo Init . L'unico requisito consiste nel registrare il modulo per uno o più eventi della pipeline di richieste. Collegare i metodi del modulo, che seguono la firma del delegato System.EventHandler, agli eventi della pipeline desiderati esposti nell'istanza di System.Web.HttpApplication specificata:

public void Init(HttpApplication context)            
{
   //          
   // Subscribe to the authenticate event to perform the 
   // authentication. 
   // 
   context.AuthenticateRequest += new        
              EventHandler(this.AuthenticateUser);

   // 
   // Subscribe to the EndRequest event to issue the 
   // challenge if necessary. 
   // 
   context.EndRequest += new 
              EventHandler(this.IssueAuthenticationChallenge);
}

Il metodo AuthenticateUser viene richiamato in ogni richiesta durante l'evento AuthenticateRequest . Viene utilizzato per autenticare l'utente in base alle informazioni sulle credenziali presenti nella richiesta.

Il metodo IssueAuthenticationChallenge viene richiamato su ogni richiesta durante l'evento EndRequest . È responsabile del rilascio di una richiesta di autenticazione di base al client ogni volta che il modulo di autorizzazione rifiuta una richiesta ed è necessaria l'autenticazione.

AuthenticateUser()

Implementare il metodo AuthenticateUser . Questo metodo esegue le operazioni seguenti:

  • Estrarre le credenziali di base, se presenti dalle intestazioni della richiesta in ingresso. Per visualizzare l'implementazione di questo passaggio, vedere il metodo di utilità ExtractBasicAuthenticationCredentials .
  • Tenta di convalidare le credenziali fornite tramite l'appartenenza (usando il provider di appartenenze predefinito configurato). Per visualizzare l'implementazione di questo passaggio, vedere il metodo dell'utilità ValidateCredentials .
  • Crea un'entità utente che identifica l'utente se l'autenticazione ha esito positivo e la associa alla richiesta.

Al termine dell'elaborazione, se il modulo è riuscito a ottenere e convalidare le credenziali utente, genererà un'entità utente autenticata che altri moduli e codice dell'applicazione usano in seguito nelle decisioni relative al controllo di accesso. Ad esempio, il modulo di autorizzazione URL esamina l'utente nell'evento della pipeline successiva per applicare le regole di autorizzazione configurate dall'applicazione.

IssueAuthenticationChallenge()

Implementare il metodo IssueAuthenticationChallenge . Questo metodo esegue le operazioni seguenti:

  • Controllare il codice di stato della risposta per determinare se la richiesta è stata rifiutata.
  • In tal caso, rilasciare un'intestazione di verifica di autenticazione di base per la risposta per attivare l'autenticazione del client.

Metodi di utilità

Implementare i metodi di utilità usati dal modulo, tra cui:

  • ExtractBasicAuthenticationCredentials. Questo metodo estrae le credenziali di autenticazione di base dall'intestazione della richiesta Authorize, come specificato nello schema di autenticazione di base.
  • ValidateCredentials. Questo metodo tenta di convalidare le credenziali utente usando l'appartenenza. L'API di appartenenza astrae l'archivio credenziali sottostante e consente di configurare le implementazioni dell'archivio credenziali aggiungendo/rimuovendo provider di appartenenze tramite la configurazione.

Nota

In questo esempio, la convalida dell'appartenenza viene impostata come commento e il modulo controlla semplicemente se il nome utente e la password sono entrambi uguali alla stringa "test". Questa operazione viene eseguita per maggiore chiarezza e non è destinata alle distribuzioni di produzione. Si è invitati a abilitare la convalida delle credenziali basate sull'appartenenza semplicemente annullando il commento del codice di appartenenza all'interno di ValidateCredentials e configurando un provider di appartenenze per l'applicazione. Per altre informazioni, vedere Appendice C.

Attività 2: Distribuire il modulo nell'applicazione

Dopo aver creato il modulo nella prima attività, è necessario aggiungerlo all'applicazione.

Distribuire nell'applicazione

Distribuire prima di tutto il modulo nell'applicazione. Di seguito sono disponibili diverse opzioni:

  • Copiare il file di origine contenente il modulo nella directory /App_Code dell'applicazione. Ciò non richiede la compilazione del modulo: ASP.NET compila automaticamente e carica il tipo di modulo all'avvio dell'applicazione. Salvare semplicemente questo codice sorgente come BasicAuthenticationModule.cs all'interno della directory /App_Code dell'applicazione. Fare questo se non si sente comodo con gli altri passaggi.

  • Compilare il modulo in un assembly e rilasciare questo assembly nella directory /BIN dell'applicazione. Questa è l'opzione più tipica se si vuole che questo modulo sia disponibile solo per questa applicazione e non si vuole inviare l'origine del modulo con l'applicazione. Compilare il file di origine del modulo eseguendo quanto segue da un prompt della riga di comando:

    <PATH_TO_FX_SDK>csc.exe /out:BasicAuthenticationModule.dll /target:library BasicAuthenticationModule.cs

    Dove <PATH_TO_FX_SDK> è il percorso di .NET Framework SDK che contiene il compilatore CSC.EXE.

  • Compilare il modulo in un assembly fortemente denominato e registrare questo assembly nella gaC. Questa è una buona opzione se si desidera che più applicazioni nel computer usino questo modulo. Per altre informazioni sulla creazione di assembly con nome sicuro, vedere Creare e usare assembly con nome sicuro.

Prima di apportare modifiche alla configurazione nel file di web.config dell'applicazione, è necessario sbloccare alcune delle sezioni di configurazione bloccate a livello di server per impostazione predefinita. Eseguire il comando seguente da un prompt dei comandi con privilegi elevati (fare > clic con il pulsante destro del mouse su Cmd.exe e scegliere "Esegui come amministratore"):

%windir%\system32\inetsrv\APPCMD.EXE unlock config /section:windowsAuthentication
%windir%\system32\inetsrv\APPCMD.EXE unlock config /section:anonymousAuthentication

Dopo aver eseguito questi comandi, sarà possibile definire queste sezioni di configurazione nel file di web.config dell'applicazione.

Configurare il modulo da eseguire nell'applicazione. Iniziare creando un nuovo file di web.config, che conterrà la configurazione necessaria per abilitare e usare il nuovo modulo. Iniziare aggiungendo il testo seguente e salvandolo nella radice dell'applicazione (%systemdrive%\inetpub\wwwroot\web.config se si usa l'applicazione radice nel sito Web predefinito).

<configuration> 
    <system.webServer> 
        <modules> 
        </modules> 
        <security> 
            <authentication> 
                <windowsAuthentication enabled="false"/> 
                <anonymousAuthentication enabled="false"/> 
            </authentication> 
        </security> 
    </system.webServer> 
</configuration>

Prima di abilitare il nuovo modulo di autenticazione di base, disabilitare tutti gli altri moduli di autenticazione IIS. Per impostazione predefinita, vengono abilitate solo autenticazione di Windows e l'autenticazione anonima. Poiché non si vuole che il browser tenti di eseguire l'autenticazione con le credenziali di Windows o consentire utenti anonimi, si disabilita sia il modulo di autenticazione di Windows che il modulo di autenticazione anonima.

Abilitare ora il modulo aggiungendolo all'elenco di moduli caricati dall'applicazione. Aprire di nuovo web.config e aggiungere la voce all'interno del <modules> tag

<add name="MyBasicAuthenticationModule" type="IIS7Demos.BasicAuthenticationModule" />

È anche possibile distribuire il modulo usando lo strumento di amministrazione IIS o lo strumento della riga di comando APPCMD.EXE.

Il contenuto finale del file di web.config dell'applicazione dopo che queste modifiche vengono fornite in Appendice B.

Congratulazioni, è stata completata la configurazione del modulo di autenticazione di base personalizzato.

come illustrato di seguito. Aprire Internet Explorer e effettuare una richiesta all'applicazione nell'URL seguente:

http://localhost/

Verrà visualizzata la finestra di dialogo di accesso di autenticazione di base. Immettere "test" nel campo "Nome utente:" e "test" nel campo "Password:" per ottenere l'accesso. Si noti che se si copia HTML, JPG o qualsiasi altro contenuto nell'applicazione, anche questi saranno protetti dal nuovo BasicAuthenticationModule.

Riepilogo

In questo articolo si è appreso come sviluppare e distribuire un modulo gestito personalizzato per un'applicazione e abilitare tale modulo per fornire servizi per tutte le richieste all'applicazione.

Si è inoltre assistito alla potenza di sviluppo di componenti del server nel codice gestito. Ciò ha consentito lo sviluppo di un servizio di autenticazione di base che viene saccoppiato dall'archiviazione delle credenziali di Windows.

Se si è avventurosi, configurare questo modulo per sfruttare la potenza dei servizi dell'applicazione di appartenenza ASP.NET 2.0 per supportare gli archivi credenziali pluggable. Per altre informazioni, vedere Appendice C.

Trovare molte risorse e suggerimenti per la scrittura di moduli IIS nel blog, http://www.mvolo.com/, nonché scaricare i moduli IIS esistenti per le applicazioni. Per alcuni esempi, vedere Reindirizzamento delle richieste all'applicazione con il modulo HttpRedirection, elenchi di directory alla ricerca di Nizza per il sito Web IIS con DirectoryListingModule e Visualizzazione di icone di file abbastanza nelle applicazioni ASP.NET con IconHandler.

Appendice A: Codice sorgente del modulo di autenticazione di base

Salvare questo codice sorgente come BasicAuthenticationModule.cs all'interno della directory /App_Code per distribuirla rapidamente nell'applicazione.

Nota

Se si usa Il blocco note, assicurarsi di impostare Salva con nome: Tutti i file per evitare di salvare il file come BasicAuthenticationModule.cs.txt.

#region Using directives
using System;
using System.Collections;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Security.Principal;
using System.IO;
#endregion
 
namespace IIS7Demos
{
    /// 
    /// This module performs basic authentication. 
    /// For details on basic authentication see RFC 2617. 
    /// 
    /// The basic operational flow is: 
    /// 
    ///     On AuthenticateRequest: 
    ///         extract the basic authentication credentials 
    ///         verify the credentials 
    ///         if succesfull, create the user principal with these credentials 
    /// 
    ///     On SendResponseHeaders: 
    ///         if the request is being rejected with an unauthorized status code (401), 
    ///         add the basic authentication challenge to trigger basic authentication. 
    ///       
    /// 

    public class BasicAuthenticationModule : IHttpModule
    {
        #region member declarations
        public const String     HttpAuthorizationHeader = "Authorization";  // HTTP1.1 Authorization header 
        public const String     HttpBasicSchemeName = "Basic"; // HTTP1.1 Basic Challenge Scheme Name 
        public const Char       HttpCredentialSeparator = ':'; // HTTP1.1 Credential username and password separator 
        public const int        HttpNotAuthorizedStatusCode = 401; // HTTP1.1 Not authorized response status code 
        public const String     HttpWWWAuthenticateHeader = "WWW-Authenticate"; // HTTP1.1 Basic Challenge Scheme Name 
        public const String     Realm = "demo"; // HTTP.1.1 Basic Challenge Realm 
        #endregion

        #region Main Event Processing Callbacks
        public void AuthenticateUser(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            String userName = null;
            String password = null;
            String realm = null;
            String authorizationHeader = context.Request.Headers[HttpAuthorizationHeader];

            // 
            //  Extract the basic authentication credentials from the request 
            // 
            if (!ExtractBasicCredentials(authorizationHeader, ref userName, ref password))
                return;
            // 
            // Validate the user credentials 
            // 
            if (!ValidateCredentials(userName, password, realm))
               return;

            // 
            // Create the user principal and associate it with the request 
            // 
            context.User = new GenericPrincipal(new GenericIdentity(userName), null);
        }

        public void IssueAuthenticationChallenge(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            // 
            // Issue a basic challenge if necessary 
            // 

            if (context.Response.StatusCode == HttpNotAuthorizedStatusCode)
            {
                context.Response.AddHeader(HttpWWWAuthenticateHeader, "Basic realm =\"" + Realm + "\"");
            }
        }
        #endregion

        #region Utility Methods
        protected virtual bool ValidateCredentials(String userName, String password, String realm)
        {
            // 
            //  Validate the credentials using Membership (refault provider) 
            // 
            // NOTE: Membership is commented out for clarity reasons.   
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // WARNING: DO NOT USE THE CODE BELOW IN PRODUCTION 
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // return Membership.ValidateUser(userName, password); 
            if (userName.Equals("test") && password.Equals("test"))
            {
                return true;
            }
            else 
            {
                return false;
            }    
        }
      
        protected virtual bool ExtractBasicCredentials(String authorizationHeader, ref String username, ref String password)
        {
            if ((authorizationHeader == null) || (authorizationHeader.Equals(String.Empty)))
               return false;
            String verifiedAuthorizationHeader = authorizationHeader.Trim();
            if (verifiedAuthorizationHeader.IndexOf(HttpBasicSchemeName) != 0)     
                return false;

            // get the credential payload 
            verifiedAuthorizationHeader = verifiedAuthorizationHeader.Substring(HttpBasicSchemeName.Length, verifiedAuthorizationHeader.Length - HttpBasicSchemeName.Length).Trim();
           // decode the base 64 encoded credential payload 
            byte[] credentialBase64DecodedArray = Convert.FromBase64String(verifiedAuthorizationHeader);
            UTF8Encoding encoding = new UTF8Encoding();
            String decodedAuthorizationHeader = encoding.GetString(credentialBase64DecodedArray, 0, credentialBase64DecodedArray.Length);

            // get the username, password, and realm 
            int separatorPosition = decodedAuthorizationHeader.IndexOf(HttpCredentialSeparator);

           if (separatorPosition <= 0)
              return false;
            username = decodedAuthorizationHeader.Substring(0, separatorPosition).Trim();
           password = decodedAuthorizationHeader.Substring(separatorPosition + 1, (decodedAuthorizationHeader.Length - separatorPosition - 1)).Trim();

            if (username.Equals(String.Empty) || password.Equals(String.Empty))
               return false;

           return true;
        }
        #endregion

        #region IHttpModule Members
        public void Init(HttpApplication context)
        {
            // 
            // Subscribe to the authenticate event to perform the 
            // authentication. 
            // 
            context.AuthenticateRequest += new 
                               EventHandler(this.AuthenticateUser);
            // 
            // Subscribe to the EndRequest event to issue the 
            // challenge if necessary. 
            // 
            context.EndRequest += new 
                               EventHandler(this.IssueAuthenticationChallenge);
        }
        public void Dispose()
        {
            // 
            // Do nothing here 
            // 
        }
        #endregion

    }
}

Appendice B: Web.config per il modulo di autenticazione di base

Salvare questa configurazione come file web.config nella radice dell'applicazione:

<configuration> 
    <system.webServer> 
      <modules> 
           <add name="MyBasicAuthenticationModule" type="IIS7Demos.BasicAuthenticationModule" /> 
      </modules> 
      <security> 
         <authentication> 
          <windowsAuthentication enabled="false"/> 
             <anonymousAuthentication enabled="false"/> 
         </authentication> 
      </security> 
    </system.webServer> 
</configuration>

Appendice C: Configurazione dell'appartenenza

Il servizio di appartenenza ASP.NET 2.0 consente alle applicazioni di implementare rapidamente la convalida delle credenziali e la gestione degli utenti richieste dalla maggior parte degli schemi di autenticazione e controllo di accesso. L'appartenenza isola il codice dell'applicazione dall'implementazione effettiva dell'archivio credenziali e offre numerose opzioni per l'integrazione con gli archivi credenziali esistenti.

Per sfruttare l'appartenenza a questo esempio di modulo, annullare ilcommentare una chiamata a Membership.ValidateUser all'interno del metodo ValidateCredentials e configurare un provider di appartenenza per l'applicazione. Per altre informazioni sulla configurazione dell'appartenenza, vedere Configurazione di un'applicazione ASP.NET per l'uso dell'appartenenza.