Showing posts with label Apache. Show all posts
Showing posts with label Apache. Show all posts

4/24/2015

Runtime Setting of SSL Params when invoking web services(apache cxf client)

By default, when we config the SSL Key for WS call, we use below system parameters:

System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", "keystore.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "1234");
System.setProperty("javax.net.ssl.trustStore", "cacerts");

But it is not flexible enough if the application has gateway structure and needs different SSL configuration for each different channels. So we need a workaround.

Below example is based on the client stub that generated with Apache CXF.(Recommanded)
Personally I prefer to use Apache CXF to generate client stub instead of Apache Axis. It is more flexible and more user friendly.

Maven Config to generate client stub:

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>3.0.4</version>
    <executions>
        <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
                <sourceRoot>${project.basedir}/src/main/java</sourceRoot>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>${project.basedir}/src/main/resources/schemas/sample-ws.wsdl</wsdl>
                        <bindingFiles>
                            <bindingFile>${project.basedir}/src/main/resources/schemas/bindings/binding.xjb</bindingFile>
                        </bindingFiles>
                        <extraargs>
                            <extraarg>-impl</extraarg>
                            <extraarg>-client</extraarg>
                            <extraarg>-verbose</extraarg>
                            <extraarg>-p</extraarg>
                            <extraarg>com.sample.ws</extraarg>
                            <extraarg>-xjc-Xvalue-constructor</extraarg>
                        </extraargs>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-value-constructor</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics</artifactId>
            <version>0.6.4</version>
        </dependency>
    </dependencies>
</plugin>

After the client stub is generated, if we want to make web service call, the client code is like below:
public SampleResponse authenticate(SampleRequest request) {
    
    SampleServiceInterface port = this.getPort();
    return port.authenticate(request);
}

public SampleServiceInterface getPort() {

    SampleServiceInterfaceService ss = new SampleServiceInterfaceService();
    SampleServiceInterface port = ss.getSampleServiceAPI();
    
    return port;
}

To customize the SSL configuration, we need to write the customized SSLSocketFactory. To implement this, we can create one SSLSocketFactoryGenerator, like below:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.lang.StringUtils;


public class SSLSocketFactoryGenerator {

    
    private byte[] keyStore = null;
    private byte[] trustStore = null;
    
    private String alias = null;
    private String keyStoreType = null;
    private String keyStorePassword = null;
    
    private String trustStoreType = null;
    private String trustStorePassword = null;

    public SSLSocketFactoryGenerator(ClientConfig config) {
        
        this.alias = config.getKeyAlias();
        this.keyStore = config.getKeyStoreBytes();
        this.trustStore = config.getTrustStoreBytes();
        this.keyStoreType = config.getKeyStoreType();
        this.keyStorePassword = config.getKeystorePassword();
        this.trustStoreType = config.getTrustStoreType();
        this.trustStorePassword = config.getTruststorePassword();
    }

    public SSLSocketFactory getSSLSocketFactory() throws IOException,
            GeneralSecurityException {

        KeyManager[] keyManagers = getKeyManagers();
        TrustManager[] trustManagers = getTrustManagers();

        SSLContext context = SSLContext.getInstance("SSL");
        context.init(keyManagers, trustManagers, null);

        SSLSocketFactory ssf = context.getSocketFactory();
        return ssf;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public byte[] getKeyStore() {
        return keyStore;
    }

    public void setKeyStore(byte[] keyStore) {
        this.keyStore = keyStore;
    }

    public byte[] getTrustStore() {
        return trustStore;
    }

    public void setTrustStore(byte[] trustStore) {
        this.trustStore = trustStore;
    }

    public String getKeyStoreType() {
        return keyStoreType;
    }

    public void setKeyStoreType(String keyStoreType) {
        this.keyStoreType = keyStoreType;
    }

    public String getKeyStorePassword() {
        return keyStorePassword;
    }

    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    public String getTrustStoreType() {
        return trustStoreType;
    }

    public void setTrustStoreType(String trustStoreType) {
        this.trustStoreType = trustStoreType;
    }

    public String getTrustStorePassword() {
        return trustStorePassword;
    }

    public void setTrustStorePassword(String trustStorePassword) {
        this.trustStorePassword = trustStorePassword;
    }

    private KeyManager[] getKeyManagers() throws IOException,
            GeneralSecurityException {

        String alg = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);

        InputStream fis = new ByteArrayInputStream(getKeyStore());
        
        KeyStore ks = KeyStore.getInstance(getKeyStoreType());
        
        ks.load(fis, getKeyStorePassword().toCharArray());
        fis.close();

        kmFact.init(ks, StringUtils.isEmpty(getKeyStorePassword())?null:getKeyStorePassword().toCharArray());

        KeyManager[] kms = kmFact.getKeyManagers();
        return kms;
    }

    protected TrustManager[] getTrustManagers() throws IOException,
            GeneralSecurityException {

        String alg = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmFact = TrustManagerFactory.getInstance(alg);

        InputStream fis = new ByteArrayInputStream(getTrustStore());
        KeyStore ks = KeyStore.getInstance(getTrustStoreType());
        ks.load(fis, StringUtils.isEmpty(getTrustStorePassword())?null:getTrustStorePassword().toCharArray());
        fis.close();

        tmFact.init(ks);

        TrustManager[] tms = tmFact.getTrustManagers();
        return tms;
    }
}

This is a quite basic sample, for different scenarios, it should be quite easy to make complementary or revision. In above sample, we can create customized SSLSocketFactory by passing in different configuration data(ClientConfig.java).

In this case, we also  need to revise the previous getPort function to use this customized SSLSocketFactory.
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.naming.ConfigurationException;
import javax.xml.ws.BindingProvider;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

public class SampleServiceImpl implements SampleService {

    private static final Logger logger = Logger.getLogger(SampleServiceImpl.class);

    private static final String URI = "/auth";

    protected ClientConfig clientConfig;

    public SampleServiceImpl() throws ConfigurationException, IOException {
        super();
    }

    void loadDefaultIfEmptyClientConfig() throws IOException, ConfigurationException {
        if (this.getClientConfig() != null)
            return;

        Properties properties = new Properties();
        properties.load(this.getClass().getClassLoader().getResourceAsStream("config/sample.properties"));
        setClientConfig(new ClientConfig(properties));
    }

    public SampleServiceImpl(Properties properties) throws ConfigurationException, IOException {
        super();
        this.setClientConfig(new ClientConfig(properties));
    }
    
    public SampleServiceImpl(ClientConfig clientConfig) throws ConfigurationException {
        super();
        this.setClientConfig(clientConfig);
    }

    public SampleServiceInterface getPort(String relUrl) {

        SampleServiceInterfaceService ss = new SampleServiceInterfaceService();
        SampleServiceInterface port = ss.getSampleServiceAPI();
        
        BindingProvider bindingProvider = (BindingProvider) port; 
        try {
            bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", 
                    new SSLSocketFactoryGenerator(clientConfig).getSSLSocketFactory());
            
            if(StringUtils.isNotEmpty(relUrl)){
                bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, String.format("%s%s", this.clientConfig.getServiceUrl(), relUrl));
            }
        } catch (IOException e) {
            logger.error("Error happened when initializing SSLFactory.", e);
            throw new IllegalArgumentException(e);
        } catch (GeneralSecurityException e) {
            logger.error("Error happened when initializing SSLFactory.", e);
            throw new IllegalArgumentException(e);
        } 
        return port;
    }
    
    public ClientConfig getClientConfig() {
        return clientConfig;
    }

    public void setClientConfig(ClientConfig clientConfig) throws ConfigurationException {

        this.clientConfig = clientConfig;
    }
    
    @Override
    public SampleResponse authenticate(SampleRequest request) {
    
        SampleServiceInterface port = this.getPort();
        return port.authenticate(request);
    }

}
In this case, for each different channels that need different SSL configuration. We can create different service.




7/30/2013

WSDL Client Stub Generation (JAVA, Maven, Apache CXF)

To consume WSDL-based web service. The easiest way to do is to generate client stub to make the service call.

Apache CXF is one service framework for web services. Details could be found in official web-site:
http://cxf.apache.org/docs/overview.html

We use maven to manage client stub code, to integrate Apache CXF, we can add dependency:
<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <cxf.version>2.7.5</cxf.version>
</properties>

<dependencies>
 <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
 </dependency>
 <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>apache-cxf</artifactId>
   <version>${cxf.version}</version>
   <type>pom</type>
 </dependency>
</dependencies>
To generate client stub, we can use the 'cxf-codegen-plugin':
<plugin>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-codegen-plugin</artifactId>
 <version>${cxf.version}</version>
 <executions>
   <execution>
     <id>generate-sources</id>
     <phase>generate-sources</phase>
     <configuration>
       <sourceRoot>${basedir}/src/main/code</sourceRoot>
       <wsdlOptions>
         <wsdlOption>
           <wsdl>WSDL_URL/RELATIVE_PATH(e.g src/main/resources/wsdl/XXX.wsdl)</wsdl>
           <extraargs>
             <extraarg>-impl</extraarg>
             <extraarg>-verbose</extraarg>
             <extraarg>-client</extraarg>
             <extraarg>-p</extraarg>
             <extraarg>com.test.service.message</extraarg>
             <!-- <extraarg>-p</extraarg> <extraarg>SOME_NAMING_SPACE1=com.test.service.message.space1</extraarg>
          <extraarg>-p</extraarg> <extraarg>SOME_NAMING_SPACE2=com.test.service.message.space2</extraarg> 
          -->
           </extraargs>
         </wsdlOption>
         <wsdlOption>
           <wsdl>WSDL_URL/RELATIVE_PATH(e.g src/main/resources/wsdl/XXX2.wsdl)</wsdl>
           <extraargs>
             <extraarg>-impl</extraarg>
             <extraarg>-verbose</extraarg>
             <extraarg>-client</extraarg>
             <extraarg>-p</extraarg>
             <extraarg>com.test.service.directory</extraarg>
           </extraargs>
         </wsdlOption>
       </wsdlOptions>
     </configuration>
     <goals>
       <goal>wsdl2java</goal>
     </goals>
   </execution>
 </executions>
</plugin>

6/18/2013

Apache HTTP Server Setup as Load Balancer

Environment:

Win 7 64 bits, Apache Http Server (httpd) 2.4.3, Apache Tomcat 7.0.29.

1.       Download Apache Http Server:
In this test case, I use the XAMPP(1.8.1) with Apache Server version 2.4.3.
2.       Setup the XAMPP:
Run %XAMPP_HOME%/setup_xampp.bat
3.       Configure the Apache Server:
=====“httpd.conf”=====
#Add below modules under the module list:
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so

#Uncomment below modules:

#Update the listen port:
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 0.0.0.0:80
#Listen [::]:80
Listen 127.0.0.1:90

NOTE: Multiple ‘ip:port’ can be listed down below, but do not put port only. Also, do not put ‘0.0.0.0:port’. Otherwise, exception will be thrown:

“C:\xampp-win32-1.8.1-VC9\xampp>apache_start.bat
Diese Eingabeforderung nicht waehrend des Running beenden
Bitte erst bei einem gewollten Shutdown schliessen
Please close this command only for Shutdown
Apache 2 is starting ...
(OS 10048)Only one usage of each socket address (protocol/network address/port) is normally permitted.  : AH00072: make_sock: could not bind to address 0.0.0.0:80
AH00451: no listening sockets available, shutting down
AH00015: Unable to open logs”

If want to check which port is in use by other process, type in cmd:
netstat -a –n

#Update the Server Name:
#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
ServerName localhost:90

=====“httpd-ssl.conf”=====
#
# When we also provide SSL we have to listen to the
# standard HTTP port (see above) and to the HTTPS port
#
# Note: Configurations that use IPv6 but not IPv4-mapped addresses need two
#       Listen directives: "Listen [::]:443" and "Listen 0.0.0.0:443"
#
#Listen 0.0.0.0:443
#Listen [::]:443
Listen 127.0.0.1:443

=====“httpd-proxy.conf”=====
#
# Implements a proxy/gateway for Apache.
# # Required modules: mod_proxy, mod_proxy_http
#

<IfModule proxy_module>
<IfModule proxy_http_module>

#
# Reverse Proxy
#
ProxyRequests Off

<Proxy balancer://mycluster>
                  BalancerMember http://127.0.0.1:8080
                  BalancerMember http://127.0.0.1:9080
                  ProxySet lbmethod=byrequests
</Proxy>

#Optional  start
<Location /balancer-manager>
   SetHandler balancer-manager
</Location>
ProxyPass /balancer-manager !   #Not proxy balancer-manager
#Optional  end

ProxyPass / balancer://mycluster/
</IfModule>
</IfModule>

NOTEI: The ‘mycluster’ is the identifier of the load balancer, can pick any value as long as starts with ‘balancer://’

NOTEII: The ‘balancer-manager is one optional configuration, this is one web UI of the load balancer manager. You can choose to use it, or not. URL is http://localhost:90/balancer-manager

NOTEIII: IMPORTANT! ProxyPass /balancer-manager !   , this is to not proxy balancer-manager to tomcat.

NOTEIV: IMPORTANT! ProxyPass / balancer://mycluster/, this is to proxy url from base ‘/’, and the trailing slash is very important. If not have, you’ll have 500 http error code. If you check the error log, below warning will be shown:

“[Tue Jun 18 11:32:39.296798 2013] [proxy:warn] [pid 36488:tid 1732] [client 127.0.0.1:59079] AH01144: No protocol handler was valid for the URL /error/HTTP_BAD_GATEWAY.html.var. If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule.”

Configuration Summary
You can also group things together by using ‘VirtualHost’.

=====”httpd-vhosts.conf”=====

<VirtualHost *:90>
        ProxyRequests off
        ServerName localhost:90

        <Proxy balancer://mycluster>
                BalancerMember http://127.0.0.1:8080
                BalancerMember http://127.0.0.1:9080

                # Security "technically we aren't blocking
                # anyone but this the place to make those
                # chages
                Order Deny,Allow
                Deny from none
                Allow from all

                # Load Balancer Settings
                # We will be configuring a simple Round
                # Robin style load balancer.  This means
                # that all webheads take an equal share of
                # of the load.
                ProxySet lbmethod=byrequests
        </Proxy>

        # balancer-manager
        # This tool is built into the mod_proxy_balancer
        # module and will allow you to do some simple
        # modifications to the balanced group via a gui
        # web interface.
        <Location /balancer-manager>
                SetHandler balancer-manager

                # I recommend locking this one down to your
                # your office
                Order deny,allow
                Allow from all
        </Location>

        # Point of Balance
        # This setting will allow to explicitly name the
        # the location in the site that we want to be
        # balanced, in this example we will balance "/"
        # or everything in the site.
        ProxyPass /balancer-manager !
        ProxyPass / balancer://mycluster/
</VirtualHost>

4.       Start two Tomcat instances
5.       Start Apache Http server (MUST run as administrator)
Either run the bat file %XAMPP_HOME%/apache_start.bat
Or start XAMPP’s control panel and start the server.
If any error happens, the error log is a good place that could help debug.

6.       Test load balance
For example, I run the web app that test the central session.

Bingo. Happy ending.
This is a very very basic configuration of apache http server as load balancer. Main goal is to make it work. For further usage, please refer to the official web site.