Java

Apache CXF(JAX-WS) で WS-Security

Apache CXF(JAX-WS) で WS-Security

JAX-WS Client で HTTPS の焼き直しです。
WCFでやった時と同じくらいシンプルな構成に出来たと思います。


まずはサービス側の実装から。

扱うコンテナはCaucho Resin J2EE Server
Apache CXF は 2.2.8 を使用しました。
当然 DIコンテナであるSpringFrameworkの力も借ります。

JDKのkeytool.exeにて証明書を作成しておきます。

[ keys.bat ]
----------------
echo on

set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_20
set PATH=%PATH%;%JAVA_HOME%\bin

keytool.exe -genkey -keyalg RSA -keystore server.keystore

[ EOF ]
----------------

/usr/local/resin/keys/ へ server.keystoreをコピーして下さい。

[ /usr/local/resin/conf/resin.conf (抜粋) ]
----------
    <server-default>
      <!-- The http port -->
      <http address="*" port="8081"/>

      <!--
         - SSL port configuration:
         -
         - <http address="*" port="8443">
         -   <openssl>
         -     <certificate-file>keys/gryffindor.crt</certificate-file>
         -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
         -     <password>test123</password>
         -   </openssl>
         - </http>
        -->
      <http address="*" port="8443">
         <jsse-ssl>
             <key-store-type>jks</key-store-type>
             <key-store-file>keys/server.keystore</key-store-file>
             <password>password</password>
         </jsse-ssl>
      </http>
[ 省略 ]
----------



サービス側の実装コード。プロジェクト名は ws-sample-service です。

[ IService.java ]
----------
package com.chocbanana.ws;


import javax.jws.WebMethod;
import javax.jws.WebService;
import com.chocbanana.bean.Person;

@WebService
public interface IService {

    @WebMethod
    public String hello();
   
    @WebMethod
    public String sayHello(String message);
   
    @WebMethod
    public Person getPerson();

    @WebMethod
    public void setPerson(Person person);
}
[ EOF ]
----------

[ Service.java ]
----------
package com.chocbanana.ws;


import javax.jws.WebService;
import javax.jws.WebMethod;
import com.chocbanana.bean.Person;

@WebService
public class Service implements IService{

    private Person person;
   
    public Service(){}
   
    @WebMethod
    public String hello(){
        return "Hello";
    }
   
    @WebMethod
    public String sayHello(String message){
        message = "Hello " + message;
        return  message;
    }
   
    @WebMethod
    public Person getPerson(){
        return new Person();
    }
   
    @WebMethod
    public void setPerson(Person person){
        this.person = person;
        System.out.println("age: " + this.person.getAge());
        System.out.println("name: " + this.person.getName());
    }
}
[ EOF ]
----------


[ cxf.xml ]
----------
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.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="service" implementor="com.chocbanana.ws.Service" address="/Service" />
</beans>
[ EOF ]
----------

[ Person.java ]
----------
package com.chocbanana.bean;

public class Person {
    private String name;
    private int age;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
[ EOF ]
----------

[ web.xml ]
----------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>


    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>
            org.springframework.web.context.ContextLoaderServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
[ EOF ]
----------

ブ ラウザで http://127.0.0.1:8081/ws-sample/services/Service?wsdl もしくは https://127.0.0.1:8443/ws-sample/services/Service?wsdl にてWSDLが表示されることを確認します。



ここからクライアント側の実装になります。
別のプロジェクトとして実装します。プロジェクト名は ws-sample-client です。

クライアント側の実装の前に、CXFに付属している wsdl2javaを使用してWSDLからサービス側のスタブを生成します。
そのスタブからサービス側のInterfaceを参照します。WCFで使用した SvcUtil.exe/WSDL.exeと同じ扱いですね。

[ my_wsdl2java.bat ]
----------
echo on

set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_20
set CXF_HOME=D:\share\Spring\apache-cxf-2.2.8
set PATH=%PATH%;%JAVA_HOME%\bin;%CXF_HOME%\bin

wsdl2java -p com.chocbanana.ws http://127.0.0.1:8081/ws-sample/services/Service?wsdl

[ EOF ]
----------

これでスタブが生成されるので、クライアントの実装コードからサービス側のInterfaceやJavaBeanを参照します。
注意するのは、ConfigureSSLOnTheClientクラスにてHTTPSの認証をパスさせている点です。

[ com.chocbanana.ws.client.Client.java ]
----------
package com.chocbanana.ws.client;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.chocbanana.ws.Person;

public class Client {

    public static void main(String args[]) {
        try {
            //サービス側のリモートインターフェースから、サーバー側のメソッドを呼び出します。
            com.chocbanana.ws.IService servicePort =
                (com.chocbanana.ws.IService)new ClassPathXmlApplicationContext("/com/chocbanana/ws/client/bean.xml").getBean("service");
   
            //HTTPS認証
            ConfigureSSLOnTheClient.configureSSLOnTheClient(servicePort);

            System.out.println("please press Enter key...");
            System.in.read();
            String resp = servicePort.sayHello("hoge");
            System.out.println("Server responded with: " + resp);
           
            //wsdl2javaで作成したサービス側のインスタンス
            Person person = servicePort.getPerson();
            person.setAge(10);
            person.setName("hoehoe");
           
            servicePort.setPerson(person);
           
        } catch (Exception e) {
            System.out.println("Message :" + e.getMessage());
            e.printStackTrace();
        }
    }
}

[ EOF ]
----------

[ com.chocbanana.ws.client.ConfigureSSLOnTheClient.java ]
----------
package com.chocbanana.ws.client;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

public class ConfigureSSLOnTheClient {

    public static void configureSSLOnTheClient(Object o) {
        org.apache.cxf.endpoint.Client clientProxy = ClientProxy.getClient(o);

        HTTPConduit conduit = (HTTPConduit) clientProxy.getConduit();
        HTTPClientPolicy httpClientPolicy = conduit.getClient();
        httpClientPolicy.setAllowChunking(false);

        String targetAddr = conduit.getTarget().getAddress().getValue();
        if (targetAddr.toLowerCase().startsWith("https:")) {
            TrustManager[] simpleTrustManager = new TrustManager[] { new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }

                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }
            } };
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setTrustManagers(simpleTrustManager);
            tlsParams.setDisableCNCheck(true);
            conduit.setTlsClientParameters(tlsParams);
        }
    }
}
[ EOF ]
----------

以下がクライアント側の bean定義ファイル。

[ bean.xml ]
----------
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:sec="http://cxf.apache.org/configuration/security"
    xmlns:http="http://cxf.apache.org/transports/http/configuration"
    xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
    xsi:schemaLocation="
             http://cxf.apache.org/configuration/security
                http://cxf.apache.org/schemas/configuration/security.xsd
           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">

    <bean id="proxyFactory"
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass"
            value="com.chocbanana.ws.IService" />
        <property name="address"
            value="https://127.0.0.1:8443/ws-sample/services/Service" />
    </bean>

    <bean id="service" class="com.chocbanana.ws.IService"
        factory-bean="proxyFactory" factory-method="create" />

</beans>
[ EOF ]
----------
このページの先頭へ