Accueil > Java EE, Web Services - SOA > Ecrire un client WebService avec CXF (2/3) – comment sécuriser l’accès avec WS-Security

Ecrire un client WebService avec CXF (2/3) – comment sécuriser l’accès avec WS-Security

Cet article, proposé par Henri Darmet, fait suite à celui publié la semaine dernière et qui expliquait comment écrire la partie cliente d’un web service de manière simple avec CXF.

Seconde étape : sécuriser l’accès avec WS-Security

Je passe sur l’absolue nécessité de sécuriser l’accès à ce WebService afin que n’importe qui ne fasse pas n’importe quoi. D’ailleurs, en dehors des salles de TP en école d’ingénieur ou en université, tous les WebServices devraient être sécurisés. La bonne nouvelle, c’est que c’est plutôt simple à réaliser. La mauvaise nouvelle est qu’il n’est pas toujours facile de trouver un tutoriel complet sur le sujet. La nouvelle bonne nouvelle, c’est qu’existe enfin, cet article pour l’expliquer.

La méthode employée, pour faire simple est la plus commune : transmettre un login et un mot de passe. C’est lorsqu’on demande un accès au service que ces informations doivent être fournies. Inutile donc de modifier l’interface de notre service. Conséquence : les classes Mail et Mailer ne changent pas. Tout est contenu dans la méthode getClient().

La nouvelle mouture de cette méthode est la suivante :

package com.objetdirect.mailer;

 

import java.io.IOException;

import java.util.HashMap;

import java.util.Map;

 

import javax.security.auth.callback.Callback;

import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.callback.UnsupportedCallbackException;

 

import org.apache.cxf.interceptor.LoggingInInterceptor;

import org.apache.cxf.interceptor.LoggingOutInterceptor;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;

import org.apache.ws.security.WSConstants;

import org.apache.ws.security.WSPasswordCallback;

import org.apache.ws.security.handler.WSHandlerConstants;

 

publicclass MailerClient {

 

publicstatic Mailer getClient(

String url,

String username,

final String password)

{

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

Map<String,Object> outProps = new HashMap<String,Object>();

outProps.put(WSHandlerConstants.ACTION,WSHandlerConstants.USERNAME_TOKEN);

outProps.put(WSHandlerConstants.USER,username);

outProps.put(WSHandlerConstants.PASSWORD_TYPE,WSConstants.PW_TEXT);

class ClientPasswordCallback implements CallbackHandler {


public ClientPasswordCallback() {


}


publicvoid handle(Callback[] callbacks) throws IOException,

UnsupportedCallbackException {

WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

pc.setPassword(password);

}

}

outProps.put(WSHandlerConstants.PW_CALLBACK_REF,

new ClientPasswordCallback());

WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);

factory.getInInterceptors().add(new LoggingInInterceptor());

factory.getOutInterceptors().add(new LoggingOutInterceptor());

factory.getOutInterceptors().add(wssOut);

factory.setServiceClass(Mailer.class);

factory.setAddress(url);

Mailer client = (Mailer) factory.create();

return client;

}

}

C’est notoirement plus compliqué, évidemment. A y regarder de près, on s’aperçoit néanmoins que le code reste lisible et surtout, surtout, générique. En gros, que fait-on ?On définit un nouvel intercepteur (un intercepteur de sécurité cette fois) que l’on va paramétrer à l’aide d’informations stockées dans une Map. Ces informations sont :

–         le type d’identification/authentification souhaité (ici login/mot de passe)
–         le login
–         le mot de passe

Jusque-là rien de complexe. Mais voilà, pour une raison certainement très valable, les auteurs de CXF ont défini une bien étrange manière de passer le mot de passe : on fournit un objet au sein de la Map et cet objet implémente une interface particulière (CallbackHandler) contenant une méthode « handle » recevant des réflexes (callback) donc le premier (et le seul ?) est de type WSPasswordCallback. C’est à cet objet réflexe-là qu’il faut transmettre, au sein de la méthode « handle », donc, le mot de passe adéquat. Ça ne se devine pas, n’est-ce pas ? Je suis bien d’accord. On imagine que derrière, le mot de passe est passé de manière asynchrone. Pourquoi ? Mystère.

Petite remarque pour les petits malins qui essaieraient d’externaliser ClientPasswordCallback : c’est un « inner class » (même si elle n’est pas « anonymous »). Elle fait référence au nouveau paramètre « password » de la méthode getClient (qui prend aussi un autre nouveau paramètre : le login). Prévoir, donc, de toucher à deux ou trois choses.

Pour le repos des neurones du lecteur, voici donc l’appel du Web Service légèrement amendé :

publicstaticvoid main(String[] argv) {

Mailer mailer = MailerClient.getClient(

« http://www.acme.com/ws/mailer »,

« myid »,

« mypassword »);

mailer.send(new Mail(

« noanswer@acme.com »,

« bigboss@goyoo.com »,

« New Webservice »,

« The new Web Service is ready ! »));

}

Seule différence : il faut fournir à getClient, un login et un mot de passe.

Pour que cela fonctionne, il faut, évidemment que la partie serveur accepte ces informations d’identification/authentification et puisse les valider (en se basant sur un annulaire LDAP par exemple). Notez qu’il s’agit d’une sécurité au niveau du Web Service et non au niveau http. Ceci ce code (en java) dans le WebService lui-même. Alimenter le « realm » de Tomcat ne servira donc à rien en l’état. Je dis ça, car j’ai vu certains faire la confusion.

Dans le prochain article, nous verrons comment ajouter une pièce jointe.

Categories: Java EE, Web Services - SOA Tags:
  1. Pas encore de commentaire
  1. Pas encore de trackbacks


4 × = vingt