SoapHeader : s'authentifier proprement a un WebService SOAP
Les WebServices utilisent le protocole SOAP, ce protocole permet d'échanger des données facilement. Une enveloppe SOAP est composée de plusieurs parties : le Header et le Body. La plupart du temps nous utilisons seulement la partie body.
Voici un exemple de requête SOAP
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetProduct xmlns="http://tempuri.org/">
<productID>int</productID>
</GetProduct>
</soap:Body>
</soap:Envelope>
et voici une réponse possible :
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetProductResponse xmlns="http://tempuri.org/">
<GetProductResult>
<ProductID>int</ProductID>
<ProductName>string</ProductName>
<Description>string</Description>
<Price>int</Price>
</GetProductResult>
</GetProductResponse>
</soap:Body>
</soap:Envelope>
ASP.net permet de gérer très facilement les WebServices, le code .net correspondant est :
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
[WebMethod]
public Product GetProduct(int productID)
{
return new Product(productID, "title", "description", 100);
}
}
Mais imaginons maintenant que je veuille m'authentifier auprès de ce WebService ? Une des solutions consiste à passer les login/pass en paramètre de la méthode GetProduct ce qui n'est pas très propre.
L'autre solution est d'utiliser l'en-tete d'une requête SOAP. Pour cela on va tout d'abord créer un nouveau type qui hérite de SoapHeader, ce type contiendra les informations de connexion :
public class CredentialHeader : SoapHeader
{
public CredentialHeader()
: base()
{ }
public CredentialHeader(String userName, String password)
: base()
{
this._userName = userName;
this._password = password;
}
private String _userName;
private String _password;
public String UserName
{
get { return _userName; }
set { _userName = value; }
}
public String Password
{
get { return _password; }
set { _password = value; }
}
}
Ensuite nous rajoutons l'attribute SoapHeaderAttribute sur la WebMethod qui prend en argument le nom d'une variable public du type que l'on vient de créer.
Ainsi lorsqu'un client enverra une requête SOAP contenant les informations de connexion dans l'en-tête, notre variable Authentication sera automatiquement renseigné, nous pouvons alors tester si le login/pass est correcte dans notre WebMethod.
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
public CredentialHeader Authentication;
[WebMethod]
[SoapHeader("Authentication")]
public Product GetProduct(int productID)
{
if (Authentication.UserName == "Cyril" && Authentication.Password == "porcinet")
return new Product(productID, "title", "description", 100);
else
throw new System.Security.Authentication.InvalidCredentialException();
}
}
Notre requête SOAP devra donc contenir les informations de connexions dans le header. Cela se traduit par une requête de ce type :
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<CredentialHeader xmlns="http://tempuri.org/">
<UserName>string</UserName>
<Password>string</Password>
</CredentialHeader>
</soap:Header>
<soap:Body>
<GetProduct xmlns="http://tempuri.org/">
<productID>int</productID>
</GetProduct>
</soap:Body>
</soap:Envelope>
Maintenant que nous savons comment demander des paramètres dans le header d'une requête SOAP, voyons comment les envoyer. Pour cela nous référençons classiquement notre i en faisant un "Add Web Reference" dans Visual Studio. Puis lorsque nous instancions notre classe proxy, nous avons la possibilité de renseigner une propriété CredentialHeaderValue de type CredentialHeader :
WS.WebService ws = new WS.WebService();
ws.CredentialHeaderValue = new WS.CredentialHeader();
ws.CredentialHeaderValue.UserName = "Cyril";
ws.CredentialHeaderValue.Password = "porcinet";
Console.WriteLine(ws.GetProduct(10).Price);
L'attribut SoapHeaderAttribute permet également de spécifier la direction de l'en-tête, ce qui permet d'avoir plusieurs variables de retour lors de l'appel à un WebService.
Merci à
chimerique qui m'a fait découvrir les header SOAP grâce à sa question sur le forum
ASP de
CodeS-SourceS :
Web Service + Sécurité