EDIT: Free ayant modifié le fonctionnement de la procédure de connexion, les informations ci dessous illustrant l’utilisation de la classe HttpsURLConnection ne sont plus d’actualité.
Vous pourrez trouver le code de connexion mis à jour ici.

Les connexions réseau sont très faciles à manipuler en Java, mais lorsqu’on souhaite communiquer des informations à un serveur HTML (pour s’identifier par exemple) les choses se corsent: il n’y a rien de plus barbare que d’écrire des entêtes HTTP à la main.

HttpURLConnection et HttpsURLConnection.
Heureusement il existe une classe, HttpURLConnection, qui simplifie ce travail en manipulant pour nous sockets et écriture d’entêtes. Encore mieux: il existe également une classe HttpsURLConnection qui se charge des connexions sécurisées (pour info cette classe hérite de la première qui elle même hérite de URLConnection, leur utilisation est donc similaire).

Voici comment la classe HttpURLConnection est présentée dans l’API:

Each HttpURLConnection instance is used to make a single request but the underlying network connection to the HTTP server may be transparently shared by other instances. Calling the close() methods on the InputStream or OutputStream of an HttpURLConnection after a request may free network resources associated with this instance but has no effect on any shared persistent connection. Calling the disconnect() method may close the underlying socket if a persistent connection is otherwise idle at that time.

Cela signifie qu’il faut recréer un objet HttpURLConnection pour chaque nouvelle requête, mais que les connexions sous-jacentes sont gérées en arrière plan de manière à ce qu’elles puissent être partagées entre plusieurs requêtes. On apprend également que la fermeture des objets de type InputStream / Outputstream liés à ces connexions libère les ressources mais n’a aucun effet sur la persistance.

Connexion sécurisée et certificat.
Avant de poursuivre, une petite précision: normalement les serveurs proposant un service sécurisé possèdent un certificat émis par une autorité reconnue. Ce certificat est transmis au client lors de la connexion. Lorsque le certificat n’a pas été validé pour l’une de ces autorités, votre navigateur vous demande en général de confirmer que vous souhaitez effectivement vous connecter à ce serveur. Or nombreux sont les sites ne possédant pas de certificat « valide », et une telle situation avec Java générera l’exception suivante:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: CA key usage check failed:
keyCertSign bit is not set

Pour résoudre ce problème il va falloir indiquer que la connexion à ce serveur est sûre. par chance quelqu’un s’est déjà occupé de la question, et vous trouverez comment faire ici. Pour ceux qui ne parlent pas anglais, il suffit donc d’exécuter ce programme avec en argument l’adresse du site qui pose problème. Un fichier appelé jssecacerts sera alors généré dans le répertoire courant, et il vous faudra le copier dans $JAVA_HOME/jre/lib/security.

Utilisation de HttpsURLConnexion.
À l’aide d’un exemple concret, comment se connecter au réseau Free-Wifi, je vais rapidement vous présenter les deux ou trois choses essentielles à savoir sur l’utilisation de HttpsURLConnection. La connexion s’effectue sur https://wifi.free.fr/, et le certificat n’a pas été validé par un organisme reconnu. Vous devrez donc effectuer l’opération indiquée plus haut pour pouvoir poursuivre.

Le code pour vous connecter:

// L'url du site auquel on s'apprête à se connecter
URL url = new URL("https://wifi.free.fr");
 
// Création de l'objet httpsURLConnection.
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
 
// Quelque headers de base
String navigateur = "Mozilla/4.0";
https.setRequestProperty("User-Agent", navigateur);
 
// On lance la connexion
https.connect();
 
// On extrait le flux d'entrée pour lire la réponse du serveur
BufferedReader in = new BufferedReader(new InputStreamReader(https.getInputStream()));
while (in.ready())
	System.out.println(in.readLine());

Surtout n’oubliez pas le « s » de https en écrivant l’URL, autrement vous vous retrouverez avec une erreur à l’exécution peu explicite et vous perdrez deux heures de votre temps pour en trouver la cause… Et comme je n’avais rien trouvé sur google pour me mettre sur la piste, j’espère que ceci permettra à d’autres étourdis de retrouver leur chemin:

Exception in thread "main" java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection cannot be cast to javax.net.ssl.HttpsURLConnection

Au moment où vous vous êtes connecté le serveur vous a attribué un identifiant. Vous le retrouverez dans le code HTML au niveau du formulaire. L’attribut s’appelle priv et il contient une suite de 128 caractères hexadécimaux. Une fois que vous l’avez extrait de la réponse alors vous pouvez vous logger facilement:

// La réponse à envoyer
String formulaire = "login=monLogin&password=monMotDePasse&priv=" + priv + "&submit=Valider";
 
// La réponse codée en UTF-8 et sous forme de tableau d'octets
byte[] post = formulaire.getBytes("UTF-8")
 
// On répète l'opération de tout à l'heure
URL url = new URL("https://wifi.free.fr");
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
String navigateur = "Mozilla/4.0";
https.setRequestProperty("User-Agent", navigateur);
 
// On autorise l'envoi de données
https.setDoOutput(true);
 
// Méthode utilisée: POST
https.setRequestMethod("POST");
 
// Type de données envoyées: réponse à un formulaire
https.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
 
// Taille des données envoyées
https.setRequestProperty("Content-Length", String.valueOf(post.length));
 
// On extrait le flux en sortie et on envoit:	
DataOutputStream out = new DataOutputStream(https.getOutputStream());
out.write(post);

N’oubliez pas que les données doivent être encodées en UTF-8.