Web services securisés avec CXF et WSS4J (partie 1)
Par guillaume le lundi 13 septembre 2010, 09:37 - java / j2ee - Lien permanent
La sécurisation des webservices devient de plus en plus nécessaire dans le monde de l'entreprise. Il y a une époque pas si lointaine, les entreprises ne se souciaient pas trop de la sécurisation de leurs données à l'intérieur de l'entreprise. Or il s'avère que les risques sécuritaires viennent majoritairement de l'intérieur!
Voici donc un tutoriel qui explique comment mettre en place toute l'infrastructure webservice : serveur et client. Nous utiliserons CXF qui nous parait être une des solutions les plus efficaces et maniables. CXF allie performances et simplicité d'intégration avec la plupart des frameworks (Spring et WSS4J).
Etape 1 : création d'un webservice SOAP / HTTP
a) création du projet
Le serveur de webservices (WS) sera en fait une webapp qui exposera un fichier WSDL et qui répondra aux requêtes exprimées en SOAP. Pour un début nous allons faire dans l'originalité et faire un WS HelloWorld. Dans un deuxième temps, nous en ferons un plus complexe en terme de structure données.
On commence par créer un nouveau projet sous eclipse. On utilise maven pour construire et packager l'application. J'utilise la structure classique des répertoires de maven avec src/main/java et src/main/resources pour les sources et target/ pour les builds.

Dans le fichier pom.xml on y met les dépendances CXF et SL4J :
- cxf-rt-frontend-jaxws
- cxf-rt-transports-http
- slf4j-api
- slf4j-log4j12
<properties>
<spring.version>2.5.6</spring.version>
<cxf.version>2.2.9</cxf.version>
<sl4j.version>1.6.1</sl4j.version>
</properties>La version de spring est donnée juste à titre indicatif car c'est celle utilisée par CXF (les librairies spring-core, spring-context, spring-beans, spring-web sont utilisées).
b) définition et implémentation du service
La définition et création du service est très rapide avec les annotations JAX-WS. L'interface est la suivante :package fr.openfarm.helloworld.wss;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.WebParam;
@WebService
public interface HelloWorldWss {
@WebMethod
public String sayHi(@WebParam(name="text")String
text);
}L'annotation @WebService est l'annotation minimale. Cependant, pour éviter de rendre les paramètres anonymes dans la génération à la volée du WSDL, il est conseillé d'ajouter @WebMethod et @WebParam afin de forcer le nommage.
L'implémentation est tout aussi rapide :
package fr.openfarm.helloworld.wss;
import javax.jws.WebService;
import org.apache.log4j.Logger;
@WebService(endpointInterface =
"fr.openfarm.helloworld.wss.HelloWorldWss")
public class HelloWorldWssImpl implements HelloWorldWss {
static final Logger logger =
Logger.getLogger(HelloWorldWssImpl.class);
public String sayHi(String text) {
StringBuilder sb = new
StringBuilder("Hello");
sb.append(text);
logger.debug(sb.toString());
return sb.toString();
}
}c) configuration Spring et CXF
Une fois cette (superbe) implémentation réalisée, il faut configurer CXF , le contexte Spring et le contexte web.Voici le contenu du fichier src/main/resources/applicationContext.xml minimal (dans le cas d'un client comme on le verra plus loin) auquel on aura ajouté un élément jaxws:endpoint qui définira notre web service.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/transports/http/configuration
http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml"
/>
<import
resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"
/>
<jaxws:endpoint id="helloWorld"
implementor="fr.openfarm.helloworld.wss.HelloWorldWssImpl"
address="/HelloWorld"
xmlns:e="http://service.jaxws.cxf.apache.org/endpoint"
xmlns:s="http://service.jaxws.cxf.apache.org/service"/>
</beans>Afin de pouvoir utiliser cette notation, il est important de bien ajouter les namespaces xmlns:jaxws et xmlns:http-conf dans l'entête. D'autre part, il ne faut pas oublier de mettre les XSD de spring 2.5 dans schemaLocation, sinon vous risquez d'obtenir cette erreur :
The prefix "jaxws" for element "jaxws:endpoint" is not bound. Elle peut vite arriver si vous vous fiez à la documentation CXF qui n'est pas toujours à jour.
Les
xmlns:eet
xmlns:ssont ajoutés pour mémoire au cas où l'on souhaite personnaliser le nommage du endpoint et du service par l'intermédiare des attributs
endpointet
servicedans la balise <jaxws:endpoint>.
Les trois directives
importchargent les fichiers de configuration CXF supplémentaires. Pour le moment ces fichiers n'existent pas.
d) configuration webapp et logs
Pour log4j (via API SL4L) il est nécessaire d'ajouter un fichier de configuration minimum pour les logs dans src/main/resourceslog4j.rootCategory=WARN, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%d{ABSOLUTE} %-5p %c{1}]:
%m%n
log4j.logger.fr.openfarm=DEBUG
Ensuiite le fichier web.xml devra être comme suit :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Hello World
WSS</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF
Servlet</display-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>La première partie permet au contexte Spring d'être chargée grâce au ContextLoaderListener auquel on indique de charger le fichier applicationContext.xml.
On déclare ensuite la servlet CXF qui va permettre d'exposer nos services derrière le contexte helloworld-wss/services/.
e) compilation et packaging de l'application avec maven
Dans le pom.xml on ajoute le plugin pour compiler le tout en Java 6 :<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
ensuite dans le répertoire du projet il suffit pour créer le WAR de lancer
$ mvn clean package(ne pas oublier de mettre
<packaging>war</packaging>
en début du pom.xml)f) déploiement
On peut utiliser le plugin maven pour tomcat, ou sinon voir cet articleg) utilisation du web service
Une fois la webapp déployée sous Tomcat, on peut aller à l'URL suivante http://localhost:8081/helloworld-wss/services/ pour voir ceci :
En cliquant sur le lien, on peut alors accéder au fichier WSDL qui aura l'allure suivante :
<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions
name="HelloWorldWssImplService"
targetNamespace="http://wss.helloworld.openfarm.fr/"
xmlns:ns1="http://cxf.apache.org/bindings/xformat"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://wss.helloworld.openfarm.fr/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified"
targetNamespace="http://wss.helloworld.openfarm.fr/" version="1.0"
xmlns:tns="http://wss.helloworld.openfarm.fr/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="sayHi" type="tns:sayHi" />
<xs:element name="sayHiResponse" type="tns:sayHiResponse" />
<xs:complexType name="sayHi">
<xs:sequence>
<xs:element minOccurs="0" name="text"
type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHiResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return"
type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="sayHiResponse">
<wsdl:part element="tns:sayHiResponse"
name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="sayHi">
<wsdl:part element="tns:sayHi"
name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorldWss">
<wsdl:operation name="sayHi">
<wsdl:input message="tns:sayHi"
name="sayHi">
</wsdl:input>
<wsdl:output message="tns:sayHiResponse"
name="sayHiResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloWorldWssImplServiceSoapBinding"
type="tns:HelloWorldWss">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="sayHi">
<soap:operation soapAction=""
style="document" />
<wsdl:input name="sayHi">
<soap:body use="literal"
/>
</wsdl:input>
<wsdl:output name="sayHiResponse">
<soap:body use="literal"
/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldWssImplService">
<wsdl:port
binding="tns:HelloWorldWssImplServiceSoapBinding"
name="HelloWorldWssImplPort">
<soap:address
location="http://localhost:8081/helloworld-wss/services/HelloWorld" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>La sécurisation fera l'objet d'un deuxième billet.
@Bientot