Actualité Tutoriels Liens contact dotnet-tech.com - Home - Tutoriels techniques avec code source Dotnet-tech.com : Les site sur les technologies .Net

Connexion distante en http ou https « server side » avec authentification (Framework .net 2.0 Béta 2)

(Avec code source à télécharger)

Mis à jour le 20/06/2005
Laurent Geffroy



Version PDF à télécharger
Code Source

Droit de diffusion:
L'ensemble ou partie de ce document ainsi que le code mis à disposition, ne peut être diffusé sur d'autres sites Web sans l'autorisation au préalable de son créateur.

Avant Propos :

Ce sujet m’a pris pas mal de temps à résoudre, jusqu’au moment où j’ai découvert que le problème venait du serveur distant !

Néanmoins, je pense qu’il est intéressant de résumer la manière de générer une requête « post » coté serveur. Nous étudierons donc :

  • L’utilisation de WebRequest et du WebResponse
  • Le contexte de connexion avec le ServicePointManager et le NetWorkCredential
L’objectif est d’envoyer des données XML à un serveur Web distant en https, le tout coté serveur en méthode POST. Le serveur distant me renverra un flux XML afin de valider ou non ma transaction.

J’ai donc tout d’abord créé une application Windows pour tester ma transmission.

Ce tutoriel peut vous servir à monter des relations B2B, mais également pour la mise en place de solutions de paiement électronique sans que votre utilisateur n’ait à changer de site internet.

La démo est réalisée sur la version Beta 2 du FrameWork .net 2.0.

Sommaire:

1. La méthode de connexion
2. L’appel de la méthode
3. Pour en savoir plus

1. La méthode de connexion



Ma classe HttpPost permet de gérer le transfert et l’ensemble de son contexte. L’assembly System.Security.Cryptography s’est étoffée avec le framework 2.0. N’oubliez pas d’ajouter l’assembly Security dans les références du projet. Par défaut, il prendra l’assembly 1.1 installée sur votre machine et vous ne comprendrez pas pourquoi X509Chain n’est pas reconnu !! J’autorise ici l’acceptation de tous les certificats. Cette partie de code est nécessaire dans le cadre d’une connexion https à un serveur distant. On peut également déterminer le nombre de connections à maintenir. Le MaxServicePointIdleTime permet de déterminer le temps durant lequel la connexion est maintenue. Au-delà, elle est supprimée par le garbage collector.

   1:          public PostHttp()
   2:          {
   3:              // Détermine le nombre de connexion concurrentes
   4:              ServicePointManager.DefaultConnectionLimit = 20;
   5:   
   6:              // En millisecondes, durée d’utilisation de l’instance
   7:              ServicePointManager.MaxServicePointIdleTime = 100000;
   8:   
   9:              ServicePointManager.ServerCertificateValidationCallback = 
  10:  delegate(Object obj, X509Certificate certificate, X509Chain chain,
  11:   SslPolicyErrors errors) { return true; };
  12:          }
J’ai donc créé une méthode LanceRequete qui reçoit 7 arguments nécessaires à ma connexion distante. Elle me retourne une string que je peux ensuite étudier. Le mieux serait bien entendu de retourner un Stream afin de faciliter le parsing XML. J’initialise ma connexion. Si un certificat X509 est requis pour l’authentification, on le transmets dans le WebRequest. p_strCer est le chemin du fichier .cer.

   1:          public string LanceRequete(string p_strCer,
   2:                                      string p_strLogin, 
   3:                                      string p_strPwd, 
   4:                                      string p_strData, 
   5:                                      string p_strURI,
   6:                                      string p_strMethod,
   7:                                      string p_host)
   8:          {
   9:              // Valeur de retour
  10:              string v_result = "ERROR:";
  11:   
  12:              // Récupère la connexion au serveur distant
  13:              ServicePoint sp = ServicePointManager.FindServicePoint(new Uri(p_strURI));
  14:           
  15:              // Credential dans le cadre d'une connexion authentifiée
  16:              NetworkCredential credentials;
  17:   
  18:              try
  19:              {
  20:                  // Création de la requete
  21:      HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(p_strURI));
  22:   
  23:                  // Gère le certificat X509 nécessaire à l'authentification de l'éméteur
  24:                  if (p_strCer != null)
  25:                  {
  26:  // Ajout du certificat à la Requete HTTP(s) en donnant le chemin du fichier .cer
  27:                      req.ClientCertificates.Add(X509Certificate.CreateFromCertFile(p_strCer));
  28:                  }
  29:   
  30:   


Afin de s’authentifier sur le site http(s), on envoie dans le contexte de connexion les informations. Je l’ajoute également dans le Header de la requête. deux précautions valent mieux qu’une !

   1:   
   2:                  // Dans le cadre d'une authentification
   3:                  if ((p_strLogin != null) && (p_strPwd != null))
   4:                  {
   5:                      // Création du NetWorkCredential
   6:                      credentials = new NetworkCredential(p_strLogin, p_strPwd);
   7:   
   8:                      // Nécessite une pré authentification
   9:                      req.PreAuthenticate = true;
  10:   
  11:                      // Met en cache le contexte d'authentification
  12:                      CredentialCache cache = new CredentialCache();
  13:                      cache.Add(new Uri(p_strURI), "Basic", credentials);
  14:                      req.Credentials = cache;
  15:   
  16:  // En fonction des cas, il vaut mieux également envoyer les données d'authenfication
  17:                      // Dans le Header de la requete HTTP(s)
  18:                      Encoding asciiEncoding = Encoding.ASCII;
  19:    byte[] v_ba = new   byte[asciiEncoding.GetByteCount(p_strLogin +":" +     p_strPwd)];
  20:                      v_ba = asciiEncoding.GetBytes(p_strLogin + ":" + p_strPwd);
  21:   
  22:                      // Ajoute au Header
  23:    req.Headers.Add(HttpRequestHeader.Authorization, "Basic " +      Convert.ToBase64String(v_ba));
  24:                  }


On finit de paramétrer la Requeste http(s) en indiquant la méthode, le referer, le timeout et le type de contenu.

   1:                  // Si le serveur de destination redirige vers une autre adresse...
   2:                  req.AllowAutoRedirect = true;
   3:   
   4:                  // Le host
   5:                  req.Referer = p_host;
   6:                  // La methode
   7:                  req.Method = p_strMethod;
   8:                  // Gèere le type de contenu envoyé
   9:                  req.ContentType = "text/xml";
  10:                  // Gère le timeout de la requete
  11:                  req.Timeout = 100000;
  12:                  req.KeepAlive = false;


Je mets maintenant la chaine de caractères dans la requête. Il s’agit de mon contenu XML.

   1:                  // Dépose les données dans la requete HTTP(s)
   2:                  if (p_strData != null)
   3:                  {
   4:                      byte[] v_Bytes = null;
   5:                      v_Bytes = Encoding.UTF8.GetBytes(p_strData);
   6:                      // Spécifie la taille de la requete HTTP(s)
   7:                      req.ContentLength = v_Bytes.Length;
   8:   
   9:                      // Dépose les données dans la requete
  10:                      Stream v_st = req.GetRequestStream();
  11:                      v_st.Write(v_Bytes, 0, v_Bytes.Length);
  12:                      v_st.Close();
  13:                  }
  14:                  else
  15:                  {
  16:                      req.ContentLength = 0;
  17:                  }


Je récupère la réponse et je le mets dans le v_result.

   1:                  // Gestion de la réponse
   2:                  try
   3:                  {
   4:                      WebResponse v_wr = req.GetResponse();
   5:                      Stream v_s = v_wr.GetResponseStream();
   6:   
   7:                      // Gestion de l'encodage de la réponse
   8:                      Encoding v_encode = System.Text.Encoding.GetEncoding("utf-8");
   9:                      StreamReader v_sr = new StreamReader(v_s, v_encode);
  10:   
  11:                      // Dépose les données dans la valeur de retour
  12:                      v_result = v_sr.ReadToEnd();
  13:   
  14:   
  15:                      v_sr.Close();
  16:                      v_wr.Close();
  17:                      v_s.Close();
  18:                  }


Je gère les erreurs, récupère le message, l’URL de réponse et les Headers reçus.

   1:   
   2:                  catch (WebException wex)
   3:                  {
   4:                      // Gestion des erreurs et envoi dans la valeur de retour
   5:                      v_result += "\r\nMessage : " + wex.Message + "\r\n";
   6:                      v_result += "ResponseURI : " + wex.Response.ResponseUri + "\r\n";
   7:                      if (wex.Response.Headers.Count > 0)
   8:                      {
   9:                          for (int i = 0; i < wex.Response.Headers.Count; i++)
  10:                          {
  11:  v_result += "Header " + (i + 1) + " : " + wex.Response.Headers[i].ToString() + "\r\n";
  12:                          }
  13:                      }
  14:                  }


On gère ici les erreurs du WebRequest.

   1:              }
   2:              catch (Exception v_ex)
   3:              {
   4:                  v_result += v_ex.Message;
   5:              }
   6:   
   7:              return v_result;
   8:          }
   9:   


2. L’appel de la méthode



Je récupère le fichier, je le lis et le dépose dans la variable v_strx. Je finis par lancer ma méthode et miracle, mon serveur me retourne sa réponse que je peux exploiter.

   1:              if (File.Exists(txt_file.Text))
   2:              {
   3:                  // Lit le fichier
   4:                  StreamReader v_srx = new StreamReader(txt_file.Text);
   5:                  string v_strx = v_srx.ReadToEnd();
   6:   
   7:                  
   8:                  PostHttp v_post = new PostHttp();
   9:   
  10:                  // Envoi de la demande
  11:                  txt_response.Text = v_post.LanceRequete(null,
  12:                                                          "login",
  13:                                                          "password",
  14:                                                          v_strx,
  15:                                                          "https://monserveurdistant/mapage",
  16:                                                          "POST",
  17:                                                          "monserveurlocal");
  18:              }
  19:              else
  20:              {
  21:                  txt_response.Text = "Le fichier n'existe pas";
  22:              }


3. Pour en savoir plus



A approfondir, l’utilisation du certificat X509.

La méthode ServicePointManager.CertificatePolicy est maintenant obsolète sur le FrameWork 2.0. Il faut maintenant utiliser ServicePointManager.ServerCertificateValidationCallback.

http://blogs.msdn.com/adarshk/archive/category/7225.aspx

Utilisation du NetWorkCredential

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetnetworkcredentialclasstopic.asp

Gestion des headers

http://forum.hardware.fr/hardwarefr/Programmation/Header-http-sujet-67998-1.htm

l’auteur : http://www.laurentgeffroy.com

PS : Merci Fred et Elise pour le coup de pied au c..



Les produits mentionnés ne sont pas encore commercialisés. Ils sont en phase de test. Si vous souhaitez obtenir Visual Studio 2005 en version beta ou en version finale dès sa disponibilité, vous pouvez souscrire un abonnement MSDN http://www.microsoft.com/france/msdn/abonnements/presentation.asp




Accueil - Tutoriels & Articles - Liens - A Propos de l'auteur dotnet-tech.com : le site des technologies .net

www.dotnet-tech.com - 2003-2007