martedì 27 dicembre 2011

Autenticazione di Web Service tramite Soap Header

 

In questi giorni sto lavorando ad un progetto per la creazione di una serie di Web Services che saranno consumati in prima battuta da una applicazione Windows Form e da altre applicazioni nel futuro.

Vista i tempi stretti e la necessità di “compatibilità interna” al team di sviluppo è stato scelto di non usare WCF ma di restare con i cari vecchi web methods. Per quanto riguarda l’autenticazione è stato scelto di usare un token, che tradotto nel caso specifico si tratta di un byte array costante, da passare del client al server e da validare lato server.

Nel mio caso i web methods da implementare saranno molti e quindi sono subito nate due esigenze: non tempestare i web methods con ripetitivi parametri e non duplicare il codice di validazione.

La soluzione scelta per la prima esigenza è stata quella di sfruttare la classe SoapHeader. Il soap header è un elemento opzionale del protocollo SOAP e contiene normalmente informazioni riguardanti autenticazione, pagamenti, …. perfetto! Nel mio caso quindi il token di autenticazione (sostituibile con le classiche proprietà utente e password) viene veicolato nell’intestazione soap.

Ma come fare per non duplicare il codice che effettua l’autenticazione vera e propria? La soluzione adottata è stata quella di utilizzare le classi SoapExtension e SoapExtensionAttribute. Il risultato finale è che in questo modo è possibile decorare un web method con il proprio attributo custom ed centralizzare il codice per l’autenticazione.

Ma adesso un po’ di codice.

Innanzitutto la classe MyCustomSoapHeader per la gestione del soap header che eredita da SoapHeader e contiene la proprietà byte array che rappresenta il token:

public class MyCustomSoapHeader : SoapHeader
{
    public byte[] Token;
    public MyCustomSoapHeader()
    {
    }
}
 

Nel web service basterà esporre una proprietà pubblica di tipo MyCustomSoapHeader e decorare il web method con l’attributo SoapHeaderAttribute:



public MyCustomSoapHeader MySoapHeader;
 
[WebMethod]
[SoapHeader("MySoapHeader", Direction = SoapHeaderDirection.InOut)]
public bool MyWebMethod(string parm1, int parm2)
{
    ....
}

In questo modo si dice al web service di usare la proprietà MySoapHeader per parcheggiare il soap header. Nel mio caso è stato aggiunto Direction = SoapHeaderDirection.InOut perchè si è voluto veicolare nel soap header anche altre informazione da rimandare al client chiamante.


Per la parte di autenticazione è stata implementata la classe TokenValidatorSoapExtension facendola ereditare da SoapExtension:



public class TokenValidatorSoapExtension : SoapExtension
{
    public override void ProcessMessage(SoapMessage message)
    {
        if (message.Stage == SoapMessageStage.AfterDeserialize)
        {
            MyCustomSoapHeader header = message.Headers[0] as MyCustomSoapHeader;
            if (header != null)
            {
                if (header.Token == new byte[] { 0x53, 0x64, 0x50, 0x32, 0x26, 0x21 };)
                {
                    return;
                }
            }
            throw new SoapHeaderException("Richiesta non autorizzata", SoapException.ClientFaultCode);
        }
    }
 
    public override object GetInitializer(Type serviceType)
    {
        return null;
    }
 
    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }
 
    public override void Initialize(object initializer)
    {
        return;
    }
}

Da notare che la classe SoapExtension ha vari metodi da sovrascrivere, ma solo ProcessMessage è quello che interessa per lo scopo. In particolare ProcessMessage ha 4 specifiche fasi SoapMessageStage in cui processare un messaggio: BeforeSerialize, AfterSerialize, BeforeDeserialize e AfterDeserialize. Ovviamente è stato usato AfterSerialize in quanto è l’unico momento in cui il messaggio soap o stato deserializzato in oggetti .NET.


Per poter applicare la SoapExtension ad un web method rimane solo da implementare una classe che erediti da SoapExtensionAttribute:



[AttributeUsage(AttributeTargets.Method)]
public class TokenValidatorAttribute : SoapExtensionAttribute
{
    private int _priority;
 
    public override Type ExtensionType
    {
        get { return typeof(TokenValidatorSoapExtension); }
    }
 
    public override int Priority
    {
        get { return _priority; }
        set { _priority = value; }
    }
}

A questo punto è possibile decorare il web method con l’attributo appena implementato e il gioco è fatto:



public MyCustomSoapHeader MySoapHeader;
 
[WebMethod]
[SoapHeader("MySoapHeader", Direction = SoapHeaderDirection.InOut)]
[TokenValidator]
public bool MyWebMethod(string parm1, int parm2)
{
    ....
}

Da notare l’attributo TokenValidator, che dice al web method di usare l’estensione TokenValidatorSoapExtension. A questo punto ogni volta che il web method viene invocato, viene recuperato il token dalla soap header custom MyCustomSoapHeader e avviene la validazione del token nell’override del metodo ProcessMessage.


Infine al client basterà creare il soap header e impostare il token:



RemoteService.RemoteService service = new RemoteService.RemoteService();
RemoteService.MySoapHeader myHeader = new RemoteService.MySoapHeader();
myHeader.Token = new byte[] { 0x53, 0x64, 0x50, 0x32, 0x26, 0x21 };
service.MyHeaderValue = myHeader;

Il meccanismo messo a disposizione dalla SoapExtension ovviamente può essere utile anche in scenari diversi da quello esposto.

domenica 17 ottobre 2010

Loosely Coupled Complexity - Unleash the power of your Domain Model with Command Query Responsibility Segregation and Event Sourcing

Check out this SlideShare Presentation:

domenica 27 settembre 2009

Validazione di DateTimeOffset con Enterprise Library Validation Application Block

Il tipo di dato DateTimeOffset è stato introdotto con .NET Framework 3.5 ed è simile a DateTime ma ha il vantaggio di aggiungere le informazioni di offset dal GMT in modo da poter legare una data ad un certo fuso orario.

Ho iniziato quindi ad utilizzarlo al posto del classico DateTime per ogni data che avesse bisogno anche delle informazioni del fuso orario. Ovviamente ho continuato ad usare DateTime per tutte le date che hanno un valore slegato dal fuso orario, ad esempio l'orario di apertura di un negozio.
Per dettagli sull' utilizzo di DateTimeOffset rimando a questo post del team BCL.

Per quanto riguarda la validazione di DateTimeOffset ho avuto qualche problema.
Solitamente per la validazione utilizzo il comodissimo Validation Application Block della libreria Entrprise Library, purtroppo però non esiste il corrispondente di DateTimeRangeValidator per DateTimeOffset.

Ho provato ugualmente ad utilizzare DateTimeRangeValidator con il tipo DateTimeOffset ma ho ricevuto il seguente messaggio: "Value to validate is not of the expected type: expected System.DateTime but got System.DateTimeOffset instead".

Ho pensato allora di chiedere se esiste qualcosa a rigurardo sul forum di Entreprise Library ed ho avuto una veloce e risolutiva risposta che illustra come implementare un custom RangeValidator per il tipo DateTimeOffset. Funziona perfettamente. Peccato solamente non averci pensato da solo.

E' possibile recuperare il codice dalla discussione stessa.

giovedì 27 agosto 2009

Bloggers: Win a free place for Roy Osherove’s TDD Masterclass (worth £2395!)


Roy Osherove is giving an hands-on TDD Masterclass in the UK, September 21-25. Roy is author of "The Art of Unit Testing" (http://www.artofunittesting.com/), a leading tdd & unit testing book; he maintains a blog at http://iserializable.com (which amoung other things has critiqued tests written by Microsoft for asp.net MVC - check out the testreviews category) and has recently been on the Scott Hanselman podcast (http://bit.ly/psgYO) where he educated Scott on best practices in Unit Testing techniques. For a further insight into Roy's style, be sure to also check out Roy's talk at the recent Norwegian Developer's Conference (http://bit.ly/NuJVa).

Full Details here: http://bbits.co.uk/tddmasterclass.

bbits are holding a raffle for a free ticket for the event. To be eligible to win the ticket (worth £2395!) you MUST paste this text, including all links, into your blog and email Ian@bbits.co.ukwith the url to the blog entry. The draw will be made on September 1st and the winner informed by email and on bbits.co.uk/blog.

I wish to win!

mercoledì 15 luglio 2009

TryParse, parametro di output e valore di default


I parametri di output sono utilizzabili tramite la keyword out e permettono di passare i parametri per riferimento. Hanno la caratteristica di non dover essere inizializzati prima della chiamata e che verranno valorizzati forzatamente dal metodo che li accetta.

Vediamo un esempio di dichiarazione e utilizzo dei parametri di output:

   public void MioMetodo(out string parametro1)
{
parametro1 = "Almeno una assegnazione è obbligatoria prima di uscire dal metodo";
}

public void Main()
{
string stringaNonInizializzata;
MioMetodo(out stringaNonInizializzata);
// a questo punto il valore della stringa è inizializzato
}

Nulla vieta però di scrivere:

public void Main()
{
string stringaInizializzata = "Valore di default";
MioMetodo(out stringaNonInizializzata);
// a questo punto il valore di default è stato modificato
}

In questo caso però bisogna stare attenti al fatto che il valore di default verrà sicuramente perso durante l'esecuzione di del metodo MioMetodo.

Un esempio di utilizzo dei parametri di output da parte del framework .NET si trova nei vari metodi TryParse, uno dei quali è quello relativo agli interi int.TryParse.
I metodi di TryParse permettono di evitare i blocchi try-catch, necessari invece se si utilizza i più classici metodi Parse e sono quindi molto più comodi.

Vediamo quindi che invece di utilizzare int.Parse:

int numero;
        try
{
numero = int.Parse("1");
}
catch (FormatException)
{
numero = 0;
}
catch (OverflowException)
{
numero = 0;
}

è possibile utilizzare int.TryParse e scrivere qualcosa di molto più semplice:

         int numero;
int.TryParse("1", out numero);

in quanto TryParse non solleva alcuna eccezione.
Molto meglio, no? Abbiamo scritto molto meno codice e il tutto è molto più leggibile.
Ma se la stringa che viene passata non rappresenta un numero, come è possibile gestire un valore di default nei casi di errore di parsing?

Una prima sbadata e sbagliata soluzione è scrivere qualcosa del tipo:

          int numero = -2; //il mio valore di default
int.TryParse("1", out numero);

perchè come ho già mostrato precedentemente tale valore verrà in ogni caso modificato dal metodo TryParse. La cosa ottimale è testare il valore booleano di ritorno e di conseguenza impostare il nostro valore di default:

int numero;
if (int.TryParse("1", out numero) == false)
{
numero = -2; //il mio valore di default
}
Attenzione quindi a non compiere come me l'errore di inizializzare il parametro di tipo out con il valore di default prima della chiamata, ma ricordate di farlo dopo, andando a testare il valore di ritorno. E' una piccola distrazione che può causare bugs difficili poi da scovare se non a colpi di debug.