Sunday 17 May 2015

HTTPS security for Webservices with cxf using embedded Jetty

HTTPS is a communications protocol for secure all communication over a computer network, with especially wide deployment on the Internet. The main purpose of HTTPS is to provide authentication of web application. It uses following two secure protocol layer

    - SSL (Secure Sockets Layer)
    - TLS (Transport Layer Security)

Both of these use asymmetric public key Infrastructure (PKI) system.

An asymmetric system uses two 'keys' to encrypt communications, a 'public' key and a 'private' key. Anything encrypted with the public key can only be decrypted by the private key and vice-versa. more details can be found at HTTPS .

In this blog instruction we will discuss how to enable transport layer security for web application using cxf embedded jetty.
We already implemented sample web application using cxf embedded jetty Webservice with cxf using embedded Jetty . This time we will just secure this application by changing configuration.

  • Keystore, Truststore, Certificates.
  • We will use keytool command to prepare keystore , truststore and importing exporting certificates.
    For example purpose we used following command to prepare required Keystore , Truststore and Certificates. It is not required to use example files user always have option to use their own keystore , truststore and certificates.
     
     keytool -v -genkey -alias KServer -keyalg RSA -keystore kblogger.jks -dname "cn=localhost, ou=KBLOG, o=blogger, c=IN" -storepass blogger -keypass blogger -validity 360
     keytool -list -v -keystore kblogger.jks  -storepass blogger -keypass blogger
     keytool -export -v -alias KServer -file client.cer -keystore kblogger.jks -storepass blogger -keypass blogger -validity 360
     keytool -import -v -trustcacerts -alias KServer -file client.cer -keystore truststore.jks -storepass blogger -keypass blogger -validity 360
     keytool -import -v -alias kclient -file client.cer -keystore kClient.jks -storepass blogger -keypass blogger -validity 360
     
  • CXF embedded Jetty configuration
  • Entire cxf embabded jetty configuration devided in following two part.

    • Transport layer secuirty configuration(httpj:identifiedTLSServerParameters)
    • Jetty engine configuration (httpj:engine)

         1. Transport layer secuirty (httpj:identifiedTLSServerParameters)


    Specifies a reusable set of properties for securing an HTTP server. It has a single attribute, id, that specifies a unique identifier by which the property set can be referred.
    We will use this element to configure HTTP transport layer security.
    httpj:tlsServerParameters Specifies a set of properties for configuring the security used for the specific Jetty instance.
    See the TLS Configuration page for more information.

    Edit a xml file name cxf-jetty-beans.xml under WEB-INF/conf and add following configuration snippets

     
      <httpj:tlsServerParameters> 
        <sec:keyManagers keyPassword="blogger"> 
          <sec:keyStore type="JKS" password="blogger" file="D:\kblogger.jks" /> 
        </sec:keyManagers> 
        <sec:trustManagers> 
          <sec:keyStore type="JKS" password="blogger" file="D:\truststore.jks" /> 
        </sec:trustManagers> 
        <sec:cipherSuitesFilter> 
          <!-- these filters ensure that a ciphersuite with export-suitable or null encryption is used, 
             but exclude anonymous Diffie-Hellman 
             key change as this is vulnerable to man-in-the-middle attacks --> 
          <sec:include>.*_EXPORT_.*</sec:include> 
          <sec:include>.*_EXPORT1024_.*</sec:include> 
          <sec:include>.*_WITH_DES_.*</sec:include> 
          <sec:include>.*_WITH_AES_.*</sec:include> 
          <sec:include>.*_WITH_NULL_.*</sec:include> 
          <sec:exclude>.*_DH_anon_.*</sec:exclude> 
        </sec:cipherSuitesFilter> 
        <sec:clientAuthentication want="true" required="true" /> 
      </httpj:tlsServerParameters> 
     
    Following table explain the elements and attributes of complexType TLSServerParametersIdentifiedType that we used in above xml snippet for configuring transport layer security.

    ComplexType :- TLSServerParametersIdentifiedType
    name space :- http://cxf.apache.org/configuration/security
    xsd path :- http://cxf.apache.org/schemas/configuration/security.xsd
    Element/(a)Attribute Type Description
    (a)id String Unique idetifier of TLS server parameters
    tlsServerParameters TLSServerParametersType Specifies an instance of the security parameters for the Jetty instance.
    keyManagers KeyManagersType This element contains the KeyManagers specification.
    (a)keyPassword String This attribute specified the password that unlock keys within the keystore.
    keyStore KeyStoreType A KeyStoreType represents the information needed to load a collection of key and certificate material from a desired location. The "url", "file", and "resource" attributes are intended to be mutually exclusive, though this assumption is not encoded in schema. The precedence order observed by the runtime is 1) "file", 2) "resource", and 3) "url".
    (a)type String This attribute specifies the type of the keystore. It is highly correlated to the provider. Most common examples are "jks" "pkcs12".
    (a)password String This attribute specifes the integrity password for the keystore. This is not the password that unlock keys within the keystore.
    (a)file String This attribute specifies the File location of the keystore. This element should be a properly accessible file from the working directory. Only one attribute of "url", "file", or "resource" is allowed.
    trustManagers TrustManagersType This structure contains the specification of JSSE TrustManagers for a single Keystore used for trusted certificates.
    keyStore KeyStoreType
    cipherSuitesFilter FiltersType This element contains the filters of the supported CipherSuites that will be supported and used if available.
    include String Include all the possible specified ciphersuite.
    exclude String Exclude all the possible specified ciphersuite.
    clientAuthentication ClientAuthentication This element contains Client Authentication specification.
    (a)want String This Boolean attribute specifies if client authentication should be requested. Example value true or false.
    (a)required String This Boolean attribute specifies if client authentication should be required. Example value true or false.

         2. Jetty engine configuration (httpj:engine)


    Referring tlsServerParameters in jetty engine configuration
     
     <httpj:engine port="666"> 
       <!-- Use "secureJetty" in jetty engine to activate https security --> 
       <httpj:tlsServerParametersRef id="secureJetty" /> 
       <!-- jetty thread pooling --> 
       <httpj:threadingParameters minThreads="5" maxThreads="15" /> 
       <httpj:handlers> 
         <ref bean="defaultHandler" /> 
       </httpj:handlers> 
       <httpj:sessionSupport>true</httpj:sessionSupport> 
     </httpj:engine> 
     
    Finally cxf-jetty-beans.xml will looks like following
     
     <?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:cxf="http://cxf.apache.org/core"
      xmlns:sec="http://cxf.apache.org/configuration/security"
      xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xmlns:http="http://cxf.apache.org/transports/http/configuration"
      xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
      xmlns:wsa="http://cxf.apache.org/ws/addressing"
      xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
      xmlns:wsrm-mgr="http://cxf.apache.org/ws/rm/manager"
      xsi:schemaLocation="
           http://cxf.apache.org/core
           http://cxf.apache.org/schemas/core.xsd
           http://cxf.apache.org/configuration/security
           http://cxf.apache.org/schemas/configuration/security.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://cxf.apache.org/transports/http/configuration
           http://cxf.apache.org/schemas/configuration/http-conf.xsd
           http://cxf.apache.org/transports/http-jetty/configuration
           http://cxf.apache.org/schemas/configuration/http-jetty.xsd
           http://schemas.xmlsoap.org/ws/2005/02/rm/policy
           http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd
           http://cxf.apache.org/ws/rm/manager
           http://cxf.apache.org/schemas/configuration/wsrm-manager.xsd
           http://cxf.apache.org/transports/http/configuration
           http://cxf.apache.org/schemas/configuration/http-conf.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd">
      <!-- import resource -->
      <import resource="classpath:META-INF/cxf/cxf*.xml" />
      <bean id="biosocket" class="org.eclipse.jetty.server.bio.SocketConnector">
       <property name="host" value="localhost" />
       <property name="port" value="9003" />
      </bean>
      <bean id="defaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler" />
      <!-- Cxf Bus implementation -->
      <cxf:bus>
       <!-- Cxf features implementation global -->
       <cxf:features>
        <!-- cxf logging implementation -->
        <cxf:logging />
       </cxf:features>
      </cxf:bus>
      <!-- Jetty server configuration and implementation -->
      <httpj:engine-factory bus="cxf">
       <!-- Transport layer secuirty -->
       <httpj:identifiedTLSServerParameters id="secureJetty">
        <httpj:tlsServerParameters>
         <sec:keyManagers keyPassword="blogger">
          <sec:keyStore type="JKS" password="blogger" file="D:/kblogger.jks" />
         </sec:keyManagers>
         <sec:trustManagers>
          <sec:keyStore type="JKS" password="blogger" file="D:/truststore.jks" />
         </sec:trustManagers>
          <sec:cipherSuitesFilter>
         <!-- these filters ensure that a ciphersuite with export-suitable or null encryption is used, but exclude anonymous Diffie-Hellman
          key change as this is vulnerable to man-in-the-middle attacks -->
          <sec:include>.*_EXPORT_.*</sec:include>
          <sec:include>.*_EXPORT1024_.*</sec:include>
          <sec:include>.*_WITH_DES_.*</sec:include>
          <sec:include>.*_WITH_AES_.*</sec:include>
          <sec:include>.*_WITH_NULL_.*</sec:include>
          <sec:exclude>.*_DH_anon_.*</sec:exclude>
          </sec:cipherSuitesFilter>
          <!-- want="true"  -->
         <sec:clientAuthentication want="true" required="true" />
        </httpj:tlsServerParameters>
       </httpj:identifiedTLSServerParameters>
       <!-- jetty engine configuration-->
       <httpj:engine port="666">
        <!-- Use "secureJetty" in jetty engine to activate https security -->
        <httpj:tlsServerParametersRef id="secureJetty" />
        <!-- jetty thread pooling -->
        <httpj:threadingParameters minThreads="5" maxThreads="15" />
        <httpj:handlers>
         <ref bean="defaultHandler" />
         <httpj:sessionSupport>true</httpj:sessionSupport> 
        </httpj:handlers>
       </httpj:engine>
      </httpj:engine-factory>
     </beans>
     

    Modify exsiting cxf-beans.xml WEB-INF/conf/cxf-beans.xml add following xml snippet.

     
      <?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: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">
       <jaxws:endpoint xmlns:tns="http://poc.kaustuv.com/ws/service/greetings"
        id="greetings"
        implementor="com.kaustuv.poc.ws.service.greetings.GreetingsImpl"
        wsdlLocation="WEB-INF/wsdl/Sample.wsdl"
        endpointName="tns:GreetingsPort"
        serviceName="tns:GreetingsService"
        address="https://localhost:666/Greetings">
        <!-- to use the catalina default 8443 port user address="/Greetings" -->
       </jaxws:endpoint>
      </beans>
     
  • Export and package this as war.Destination path %JBOSS_HOME%\server\default
  • Open command prompt and go to directory %JBOSS_HOME\bin
  • Execute run.bat -c default -b 0.0.0.0
  • Deployement will be sucessfull according to above instructions. following log message will apear in jboss console log.
     
     main:21:34:55,310:INFO:[STDERR]:May 14, 2015 9:34:55 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
     INFO: Creating Service {http://poc.kaustuv.com/ws/service/greetings}GreetingsService from WSDL: WEB-INF/wsdl/Sample.wsdl
     main:21:34:56,283:INFO:[STDERR]:May 14, 2015 9:34:56 PM org.apache.cxf.endpoint.ServerImpl initDestination
     INFO: Setting the server's publish address to be https://localhost:666/Greetings
     main:21:34:56,456:INFO:[STDERR]:May 14, 2015 9:34:56 PM org.eclipse.jetty.server.Server doStart
     INFO: jetty-7.5.4.v20111024
     main:21:34:56,963:INFO:[STDERR]:May 14, 2015 9:34:56 PM org.eclipse.jetty.server.AbstractConnector doStart
     INFO: Started CXFJettySslSocketConnector@0.0.0.0:666 STARTING
     main:21:34:57,179:INFO:[STDERR]:May 14, 2015 9:34:57 PM org.eclipse.jetty.server.handler.ContextHandler startContext
     INFO: started o.e.j.s.h.ContextHandler{,null}
     main:21:34:57,189:INFO :[ContextLoader]:Root WebApplicationContext: initialization completed in 6443 ms
     
    WSDL url : https://localhost:666/Greetings?wsdl Before accessing wsdl url user must install certificate that server side will provide. Then after we will be able to see wsdl otherwise we will get following connection close error in browser and in server console we will get following error.
     
     WARNING: 0:0:0:0:0:0:0:1:57089 
     javax.net.ssl.SSLHandshakeException: null cert chain
      at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1421)
      at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
      at sun.security.ssl.SSLEngineImpl.writeAppRecord(SSLEngineImpl.java:1214)
      at sun.security.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:1186)
      at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:469)
      at org.eclipse.jetty.io.nio.SslSelectChannelEndPoint.wrap(SslSelectChannelEndPoint.java:642)
      at org.eclipse.jetty.io.nio.SslSelectChannelEndPoint.process(SslSelectChannelEndPoint.java:309)
      at org.eclipse.jetty.io.nio.SslSelectChannelEndPoint.fill(SslSelectChannelEndPoint.java:398)
      at org.eclipse.jetty.http.HttpParser.fill(HttpParser.java:949)
      at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:274)
      at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
      at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
      at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
      at java.lang.Thread.run(Thread.java:745)
     Caused by: javax.net.ssl.SSLHandshakeException: null cert chain
      at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
      at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666)
      at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:304)
      at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:292)
      at sun.security.ssl.ServerHandshaker.clientCertificate(ServerHandshaker.java:1804)
      at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:222)
      at sun.security.ssl.Handshaker.processLoop(Handshaker.java:969)
      at sun.security.ssl.Handshaker$1.run(Handshaker.java:909)
      at sun.security.ssl.Handshaker$1.run(Handshaker.java:906)
      at java.security.AccessController.doPrivileged(Native Method)
      at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1359)
      at org.eclipse.jetty.io.nio.SslSelectChannelEndPoint.process(SslSelectChannelEndPoint.java:283)
     
      Post Development testing
    Following image will lead us to import client keystore in soapui, Which will be mentioned by client before calling client web service. Step 1. Loading client keystore which contain certificate provided by server. Step 2. Using keystore in webservice request.
    • References:
    Jetty wiki
    Embedded Jetty
    CXF documents

    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.

    Friday 8 May 2015

    Simple example of Spring LDAP framework using Object-Directory Mapping

    This Blog instruction will help us to do LDAP programming using Spring framework LDAP api and Object-Directory Mapping (Spring-ODM). We will focus on creating simple example of search, create, read, update and delete (SCRUD).
    In previous blog instruction it was explained how to use sping ldap template. Simple example of Spring LdapTemplate.

    Object-relational mapping frameworks like Hibernate and JPA offers developers the ability to use annotations to map relational database tables to Java objects. Spring LDAP project offers a similar ability with respect to LDAP directories through the use of the org.springframework.ldap.odm. This blog instruction we will discuss about OdmManager. The OdmManager orchestrates the process of reading objects from the directory and mapping the data to annotated Java object classes. This interface provides following methods:

      <T> T findByDn(Name dn, Class<T> clazz)
    
      <T> T findOne(LdapQuery query, Class<T> clazz)
    
      <T> List<T> find(LdapQuery query, Class<T> clazz)
    
      <T> List<T> findAll(Class<T> clazz)
    
      <T> List<T> findAll(Name base, SearchControls searchControls, Class<T> clazz)
    
      <T> List<T> findAll(Name base, Filter filter, SearchControls searchControls, Class<T> clazz)
    
      void create(Object entry)
    
      void update(Object entry)
    
      void delete(Object entry)
     

  • Pre-requsite
  • Instructions assume that jdk, Apache DS, Apache Directory Studio is installed without any error.
    We have to start LDAP service. After installation of Apache DS following image will guide us to start default LDAP service. We got check default Apache DS. After starting Apache DS following image will guide us to explore newly started LDAP services.

  • Implemenation instructions
    1. Create a Simple spring project name "spring-ldap-example". Following image will guide us. Add the required libraries in classpath.
    2. - spring-aop-4.1.3.RELEASE.jar
      - spring-beans-4.1.3.RELEASE.jar
      - spring-context-4.1.3.RELEASE.jar
      - spring-context-support-4.1.3.RELEASE.jar
      - spring-core-4.1.3.RELEASE.jar
      - spring-expression-4.1.3.RELEASE.jar
      - spring-ldap-core-2.0.2.RELEASE.jar
      - spring-ldap-core-tiger-2.0.2.RELEASE.jar
      - spring-ldap-ldif-batch-2.0.2.RELEASE.jar
      - spring-ldap-ldif-core-2.0.2.RELEASE.jar
      - spring-ldap-test-2.0.2.RELEASE.jar
      - log4j-1.2.14.jar
      - slf4j-jcl-1.7.5.jar

    3. Create folder name resource and add in classpath. Create test_data.ldif in resource folder and following snippet. This test_data.ldif is used to prepare test data.
    4.      version: 1
      
           dn: dc=example,dc=com
           objectclass: top
           objectclass: domain
           dc: example
      
           dn: ou=groups,dc=example,dc=com
           objectClass: top
           objectClass: organizationalUnit
           ou: groups
           description: this will contains all the groups
      
           dn: uid=kaustuv,ou=users,dc=example,dc=com
           objectClass: organizationalPerson
           objectClass: person
           objectClass: uidObject
           objectClass: top
           cn: kaustuv
           sn: maji
           uid: kaustuv
           postalAddress: GC 207, Sector III, SaltlakeCity, Kolkata 700106, WestBengal, India
           telephoneNumber: 9831198311
           userPassword:: e1NTSEF9OXg3VGxzamNrQkFWZmVRRllRYnBXS25IUFYvV0hpdmtiSFNNMXc9PQ==
      
           dn: cn=testGroup,ou=groups,dc=example,dc=com
           objectClass: top
           objectClass: groupOfUniqueNames
           cn: testGroup
           uniqueMember: uid=kaustuv,ou=users,dc=example,dc=com
           o: kaustuv's blog
      
           dn: ou=users,dc=example,dc=com
           objectClass: top
           objectClass: organizationalUnit
           ou: users
      
           dn: uid=guest,ou=users,dc=example,dc=com
           objectClass: top
           objectClass: uidObject
           objectClass: person
           objectClass: organizationalPerson
           cn: guest
           sn: guest
           uid: guest
           postalAddress: DreamLand
           telephoneNumber: 9830098300
           userPassword:: e1NTSEF9OXg3VGxzamNrQkFWZmVRRllRYnBXS25IUFYvV0hpdmtiSFNNMXc9PQ==
    5. Create package ldap.odm.example
    6. Create simple pojo class User and following snippet.
    7.       /**
            *
            * Copyright © Kaustuv Maji , 2014
            * Repos - https://github.com/kaustuvmaji
            * Blog -  http://kaustuvmaji.blogspot.in
            *
            */
            package ldap.odm.example;
      
        import java.io.Serializable;
      
        import javax.naming.Name;
      
        import org.springframework.ldap.odm.annotations.Attribute;
        import org.springframework.ldap.odm.annotations.DnAttribute;
        import org.springframework.ldap.odm.annotations.Entry;
        import org.springframework.ldap.odm.annotations.Id;
      
            /**
          * This class is mapped with ldap entry user
             * @author KMaji
             *
             */
         @Entry(objectClasses = { "top", "uidObject", "person", "organizationalPerson" } , base="ou=users")
            public class User implements Serializable {
      
             /**
              *
              */
             private static final long serialVersionUID = 9081527761576640803L;
      
        @Id
        private Name    distinguisedName;
        @Attribute(name = "uid")
        @DnAttribute(value="uid", index=1)
        private String    uid;
        @Attribute(name = "cn")
        private String    cn;
        @Attribute(name = "sn")
        private String    sn;
        @Attribute(name = "postalAddress")
        private String    postalAddress;
        @Attribute(name = "telephoneNumber")
        private String    telephoneNumber;
      
             /**
              * @return the uid
              */
             public synchronized final String getUid() {
              return uid;
             }
      
             /**
              * @param uid
              *            the uid to set
              */
             public synchronized final void setUid(String uid) {
              this.uid = uid;
             }
      
             /**
              * @return the cn
              */
             public synchronized final String getCn() {
              return cn;
             }
      
             /**
              * @param cn
              *            the cn to set
              */
             public synchronized final void setCn(String cn) {
              this.cn = cn;
             }
      
             /**
              * @return the sn
              */
             public synchronized final String getSn() {
              return sn;
             }
      
             /**
              * @param sn
              *            the sn to set
              */
             public synchronized final void setSn(String sn) {
              this.sn = sn;
             }
      
             /**
              * @return the userPassword
              */
             public synchronized final String getUserPassword() {
              return userPassword;
             }
      
             /**
              * @param userPassword
              *            the userPassword to set
              */
             public synchronized final void setUserPassword(String userPassword) {
              this.userPassword = userPassword;
             }
      
             /**
              * @return the postalAddress
              */
             public synchronized final String getPostalAddress() {
              return postalAddress;
             }
      
             /**
              * @param postalAddress
              *            the postalAddress to set
              */
             public synchronized final void setPostalAddress(String postalAddress) {
              this.postalAddress = postalAddress;
             }
      
             /**
              * @return the telephoneNumber
              */
             public synchronized final String getTelephoneNumber() {
              return telephoneNumber;
             }
      
             /**
              * @param telephoneNumber
              *            the telephoneNumber to set
              */
             public synchronized final void setTelephoneNumber(String telephoneNumber) {
              this.telephoneNumber = telephoneNumber;
             }
      
             /*
              * (non-Javadoc)
              *
              * @see java.lang.Object#toString()
              */
             @Override
             public String toString() {
              StringBuilder builder = new StringBuilder();
              builder.append("User [");
              if (uid != null) {
               builder.append("uid=");
               builder.append(uid);
               builder.append(", ");
              }
              if (cn != null) {
               builder.append("cn=");
               builder.append(cn);
               builder.append(", ");
              }
              if (sn != null) {
               builder.append("sn=");
               builder.append(sn);
               builder.append(", ");
              }
              if (userPassword != null) {
               builder.append("userPassword=");
               builder.append(userPassword);
               builder.append(", ");
              }
              if (postalAddress != null) {
               builder.append("postalAddress=");
               builder.append(postalAddress);
               builder.append(", ");
              }
              if (telephoneNumber != null) {
               builder.append("telephoneNumber=");
               builder.append(telephoneNumber);
              }
              builder.append("]");
              return builder.toString();
             }
            }
      following table explains possible annotation options provided by spring ldap odm framework.

      Annotation Type Description
      Entry This annotation marks a Java class to be persisted in an LDAP directory. It is recommended to use this annotation at class level. Example: @Entry(objectClasses = { "top", "uidObject", "person", "organizationalPerson" })
      Id This annotation marks a Java field as containing the Distinguished Name of an LDAP Entry. Example : @Id private Name distinguisedName;
      Attribute This annotation describes the mapping of a Java field to an LDAP attribute. Example: @Attribute(name = "uid") private String uid;
      DnAttribute Indicates that a field is to be automatically populated to/from the distinguished name of an entry. Example: @Attribute(name = "uid") @DnAttribute(value="uid", index=1) private String uid;
      Transient This annotation identifies a field in an Entry annotated class that should not be persisted to LDAP.

    8. Create interface UserRepositoryIntf and add following snippet.
    9. We will define all following methods to implements example of SCRUD functions.
          /**
       *
       * Copyright © Kaustuv Maji , 2014
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       *
       */
       package ldap.advance.example;
      
       import java.util.List;
      
       /**
        * <pre>
        * This interface is used for
        *   a) fetch all the user details as a list of String
        *   b) fetch all the user details as a list of User object
        *   c) fetch user details of particular user.
        * </pre>
        *
        * @author KMaji
        *
        */
       public interface UserRepositoryIntf <T>{
      
        /**
         * This method is responsible to fetch all the user details as a list of
         * String.
         *
         * @return list of String.
         */
        public List<String> getAllUserNames();
      
        /**
         * This method is responsible to fetch all the user details as a list of
         * User object
         *
         * @return list of {@link T}
         */
        public List<T> getAllUsers();
      
        /**
         * This method is responsible to fetch user details of particular user.
         *
         * @return user details {@link T}
         */
        public T getUserDetails(String userName);
      
        /**
         * This method is responsible to fetch user details of particular user as a string.
         *
         * @return user detail {@link T}
         */
        public Object getUserDetail(String userName);
      
        /**
         * This method is responsible to authenticate user.
         *
         * @return boolean true|false
         */
        public boolean authenticate(String base,String userName, String password);
      
        /**
         * This method is responsible to update telephone number of user.
         *
         * @return boolean true|false
         */
        public T updateTelePhone(String userName, String newNumber);
      
        /**
         * This method is responsible to create user.
         */
        public boolean createUser(T user);
      
        /**
         * This method is responsible to delete user.
         */
        public boolean remove(String uid);
       }
    10. Create class UserRepositoryImpl implements interface UserRepositoryIntf and add following snippet.
    11.   /**
         *
         * Copyright © Kaustuv Maji , 2014
         * Repos - https://github.com/kaustuvmaji
         * Blog -  http://kaustuvmaji.blogspot.in
         *
         */
        package ldap.odm.example;
      
        import java.util.List;
      
        import javax.naming.directory.SearchControls;
      
        import ldap.advance.example.UserRepositoryIntf;
      
        import org.apache.log4j.Logger;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.ldap.core.DistinguishedName;
        import org.springframework.ldap.core.LdapTemplate;
        import org.springframework.ldap.odm.core.OdmManager;
        import org.springframework.stereotype.Component;
      
        /**
         * This class implements the @see {@link UserRepositoryIntf}.
         *
         * @author KMaji
         *
         */
        @Component
        public class UserRepositoryImpl implements UserRepositoryIntf<User> {
      
         private static Logger  log    = Logger.getLogger(UserRepositoryImpl.class);
         private static final String basedn   = "ou=users";
         private static final String queryDelimeter = ",";
      
         @SuppressWarnings("deprecation")
         @Autowired
         private OdmManager   odmManager;
      
         @Autowired(required = true)
         @Qualifier(value = "ldapTemplate")
         private LdapTemplate  ldapTemplate;
      
         /**
          * (non-Javadoc)
          *
          * @see ldap.advance.example.UserRepositoryIntf#getAllUserNames()
          **/
         @Override
         public List<String> getAllUserNames() {
          return null;
         }
      
         /**
          * (non-Javadoc)
          *
          * @see ldap.advance.example.UserRepositoryIntf#getAllUsers()
          **/
         @SuppressWarnings("deprecation")
         @Override
         public List<User> getAllUsers() {
          SearchControls controls = new SearchControls();
          // controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
          return odmManager.search(User.class, new DistinguishedName(basedn), "(uid=*)", controls);
         }
      
         /**
          * (non-Javadoc)
          *
          * @see
          * ldap.advance.example.UserRepositoryIntf#getUserDetails(java.lang.String)
          **/
         @Override
         public User getUserDetails(String userName) {
          return null;
         }
      
         /**
          * (non-Javadoc)
          *
          * @see
          * ldap.advance.example.UserRepositoryIntf#getUserDetail(java.lang.String)
          **/
         @SuppressWarnings("deprecation")
         @Override
         public User getUserDetail(String userName) {
          log.info("executing {getUserDetail}");
          log.info(bindDN(userName));
          User rUser = odmManager.read(User.class, bindDN(userName));
          return rUser;
         }
      
         /**
          * (non-Javadoc)
          *
          * @see
          * ldap.advance.example.UserRepositoryIntf#authenticate(java.lang.String,
          * java.lang.String)
          **/
         @Override
         public boolean authenticate(String base, String userName, String password) {
          log.info("executing {authenticate}");
          return false;
         }
      
         /**
          * (non-Javadoc)
          *
          * @see
          * ldap.advance.example.UserRepositoryIntf#updateTelePhone(java.lang.String)
          **/
         @SuppressWarnings("deprecation")
         @Override
         public User updateTelePhone(String userName, String newNumber) {
          log.info("executing {updateTelePhone}");
          User mUser = odmManager.read(User.class, bindDN(userName));
          mUser.setTelephoneNumber(newNumber);
          odmManager.update(mUser);
          return getUserDetail(userName);
         }
      
         /**
          * (non-Javadoc)
          *
          * @see
          * ldap.advance.example.UserRepositoryIntf#createUser(ldap.advance.example
          * .User)
          **/
         @SuppressWarnings("deprecation")
         @Override
         public boolean createUser(User user) {
          log.info("executing {createUser}");
          user.setDistinguisedName(bindDN(user.getUid()));
          odmManager.create(user);
          return true;
         }
      
         /**
          * (non-Javadoc)
          *
          * @see ldap.advance.example.UserRepositoryIntf#remove(java.lang.String)
          **/
         @SuppressWarnings("deprecation")
         @Override
         public boolean remove(String uid) {
          // ldapTemplate.unbind(bindDN(uid));
          odmManager.delete(getUserDetail(uid));
          return true;
         }
      
         @SuppressWarnings("deprecation")
         public static javax.naming.Name bindDN(String userName) {
          javax.naming.Name name = new DistinguishedName("uid=".concat(userName).concat(queryDelimeter.concat(basedn)));
          return name;
         }
        }
      • Following table will explain the methods that used to SCRUD functionalities.

      Methods name Description
      createUser This method is responsible to create user.
      - Example of ldapTemplate.bind() and BasicAttribute.
      updateTelePhone This method is responsible to update telephone number of user.
      - Example of ldapTemplate.modifyAttributes and ModificationItem.
      remove This method is responsible to delete user.
      - Example of ldapTemplate.unbind().
      getAllUsers This method is responsible to fetch all the user details as a list of User object.
      - ldapTemplate.search() is used to search data.
      - Example of SearchControls.
      getUserDetail This method is responsible to fetch user details of particular user as a string.

        <?xml version="1.0" encoding="UTF-8"?>
        <!--
        #
        # Copyright © Kaustuv Maji , 2014
        # Repos - https://github.com/kaustuvmaji
        # Blog -  http://kaustuvmaji.blogspot.in
        #
        -->
        <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:context="http://www.springframework.org/schema/context"
         xmlns:ldap="http://www.springframework.org/schema/ldap"
         xmlns:p="http://www.springframework.org/schema/p"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap-2.0.xsd">
      
         <context:component-scan base-package="ldap.odm.example" />
      
         <bean id="simpleDirContextAuthenticationStrategy"
          class="org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy" />
      
         <bean id="userReposImpl" class="ldap.odm.example.UserRepositoryImpl" />
      
         <bean id="fromStringConverter"
          class="org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter" />
         
         <bean id="toStringConverter"
          class="org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter" />
          
         <!-- The OdmManager relies on the org.springframework.ldap.odm.typeconversion package to convert LDAP attributes 
         to Java fields. The main interface in this class is the org.springframework.ldap.odm.typeconversion.ConverterManager. --"
         <bean id="converterManager"
          class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean">
          <property name="converterConfig">
           <set>
            <bean
             class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
             <property name="fromClasses">
              <set>
               <value>java.lang.String</value>
              </set>
             </property>
             <property name="toClasses">
              <set>
               <value>java.lang.Byte</value>
               <value>java.lang.Short</value>
               <value>java.lang.Integer</value>
               <value>java.lang.Long</value>
               <value>java.lang.Float</value>
               <value>java.lang.Double</value>
               <value>java.lang.Boolean</value>
              </set>
             </property>
             <property name="converter" ref="fromStringConverter" />
            </bean>
            <bean
             class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
             <property name="fromClasses">
              <set>
               <value>java.lang.Byte</value>
               <value>java.lang.Short</value>
               <value>java.lang.Integer</value>
               <value>java.lang.Long</value>
               <value>java.lang.Float</value>
               <value>java.lang.Double</value>
               <value>java.lang.Boolean</value>
              </set>
             </property>
             <property name="toClasses">
              <set>
               <value>java.lang.String</value>
              </set>
             </property>
             <property name="converter" ref="toStringConverter" />
            </bean>
           </set>
          </property>
         </bean>
      
         <ldap:context-source url="ldap://localhost:10389"
          base="dc=example,dc=com" username="uid=admin,ou=system" password="secret"
          authentication-strategy-ref="simpleDirContextAuthenticationStrategy"
          native-pooling="true" />
      
      
         <bean id="odmManager"
          class="org.springframework.ldap.odm.core.impl.OdmManagerImplFactoryBean">
          <property name="converterManager" ref="converterManager" />
          <property name="contextSource" ref="contextSource" />
          <property name="managedClasses">
           <set>
            <value>ldap.odm.example.User</value>
           </set>
          </property>
         </bean>
      
      
         <ldap:ldap-template id="ldapTemplate" />
        </beans>

      The OdmManager depends on the typeconversion provided by spring ldap odm framework to convert LDAP attributes to Java fields. The main interface in this class is the ConverterManager. The default ConverterManager implementation uses the following algorithm when parsing objects to convert fields:

      1. Try to find and use a Converter registered for the fromClass, syntax and toClass and use it.
      2. If this fails, then if the toClass isAssignableFrom the fromClass then just assign it.
      3. If this fails try to find and use a Converter registered for the fromClass and the toClass ignoring the syntax.
      4. If this fails then throw a ConverterException.

      The factory bean requires converter configurations to be declared in the bean configuration. In our example we used default typeconversion configuration recommended by spring framework. we used following steps.

      • org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter used for String to Object conversion.
      • org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter used for Object to String conversion.
      • org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean used for providing converter configurations. We refer this bean while creating OdmManager bean.
      More details about type conversion are in Chapter 12.4 of Object-Directory Mapping (ODM).
    12. Create log4j.xml in resource folder and add following snippet.
    13.      <?xml version="1.0" encoding="UTF-8"?>
           <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
           <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
      
            <!-- Appenders -->
            <appender name="console" class="org.apache.log4j.ConsoleAppender">
             <param name="Target" value="System.out" />
             <layout class="org.apache.log4j.PatternLayout">
              <param name="ConversionPattern" value="%d{ISO8601} %-5p %-1X{TID} %t [%c] %m%n" />
             </layout>
            </appender>
      
             <!-- Appenders -->
            <appender name="file" class="org.apache.log4j.RollingFileAppender">
             <param name="Threshold" value="ALL" />
             <param name="File" value="diag.log" />
             <param name="Append" value="true" />
             <param name="MaxFileSize" value="500000KB" />
             <param name="MaxBackupIndex" value="10" />
             <layout class="org.apache.log4j.PatternLayout">
              <param name="ConversionPattern" value="%d{ISO8601} %-5p %-1X{TID} %t [%c] %m%n" />
             </layout>
            </appender>
      
            <!-- Root Logger -->
            <root>
             <priority value="INFO" />
             <appender-ref ref="console" />
             <appender-ref ref="file" />
            </root>
      
           </log4j:configuration>
       

        Post Development testing


      Following class will be used to test spring ldap template examples.
       /**
        *
        * Copyright © Kaustuv Maji , 2015
        * Repos - https://github.com/kaustuvmaji
        * Blog -  http://kaustuvmaji.blogspot.in
        *
        */
       package ldap.odm.example.test;
      
       import java.util.Date;
      
       import ldap.odm.example.User;
       import ldap.advance.example.UserRepositoryIntf;
      
       import org.apache.log4j.Logger;
       import org.springframework.context.support.AbstractApplicationContext;
       import org.springframework.context.support.ClassPathXmlApplicationContext;
      
       /**
        * @author KMaji
        *
        */
       public class LdapODMApp {
      
        private static Logger log = Logger.getLogger(LdapODMApp.class);
      
        public static void main(String[] args) {
         AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-ldap-odm.xml");
         log.info("Test started at " + new Date(context.getStartupDate()));
         @SuppressWarnings("unchecked")
         UserRepositoryIntf<User> ldapDao = (UserRepositoryIntf<User>) context.getBean("userReposImpl");
         final String success = "Success";
         final String failure = "Failure";
         final String username = "kaustuv";
         {
          // Create
          User user = new User();
          {
           user.setUid("spring_ldap_test");
           user.setCn("spring_ldap_test");
           user.setSn("spring_ldap_test");
           user.setPostalAddress("spring_ldap_test");
           user.setTelephoneNumber("9830098301");
          }
      
          log.info("\n Example of OdmManager create #=> " + (ldapDao.createUser(user) ? success : failure));
      
          // Read
          log.info("\n Example of OdmManager search all #=> " + ldapDao.getAllUsers());
      
          // find
          log.info("\n Example of OdmManager read #=> " + ldapDao.getUserDetail(username));
      
          // Update
          log.info("\n Example of OdmManager update #=> " + ldapDao.updateTelePhone("spring_ldap_test", "9831198311"));
      
          // Delete
          log.info("\n Example of OdmManager delete #=> " + (ldapDao.remove("spring_ldap_test") ? success : failure));
         }
         context.registerShutdownHook();
         context.close();
        }
       }
       
      Output
       2015-05-09 10:33:19,784 INFO   main [org.springframework.context.support.ClassPathXmlApplicationContext] Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c2e1f26: startup date [Sat May 09 10:33:19 IST 2015]; root of context hierarchy
       2015-05-09 10:33:19,949 INFO   main [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [spring-ldap-odm.xml]
       2015-05-09 10:33:20,925 INFO   main [ldap.odm.example.test.LdapODMApp] Test started at Sat May 09 10:33:19 IST 2015
       2015-05-09 10:33:20,926 INFO   main [ldap.odm.example.UserRepositoryImpl] executing {createUser}
       2015-05-09 10:33:22,051 INFO   main [ldap.odm.example.test.LdapODMApp] 
        Example of OdmManager create #=> Success
       2015-05-09 10:33:22,086 INFO   main [ldap.odm.example.test.LdapODMApp] 
        Example of OdmManager search all #=> [User [uid=spring_ldap_test, cn=spring_ldap_test, sn=spring_ldap_test, postalAddress=spring_ldap_test, telephoneNumber=9830098301], User [uid=kaustuv, cn=kaustuv, sn=maji, postalAddress=GC 207, Sector III, SaltlakeCity, Kolkata 700106, WestBengal, India, telephoneNumber=9831198311], User [uid=guest, cn=guest, sn=guest, postalAddress=DreamLand, telephoneNumber=9830098300]]
       2015-05-09 10:33:22,086 INFO   main [ldap.odm.example.UserRepositoryImpl] executing {getUserDetail}
       2015-05-09 10:33:22,087 INFO   main [ldap.odm.example.UserRepositoryImpl] uid=kaustuv,ou=users
       2015-05-09 10:33:22,097 INFO   main [ldap.odm.example.test.LdapODMApp] 
        Example of OdmManager read #=> User [uid=kaustuv, cn=kaustuv, sn=maji, postalAddress=GC 207, Sector III, SaltlakeCity, Kolkata 700106, WestBengal, India, telephoneNumber=9831198311]
       2015-05-09 10:33:22,102 INFO   main [ldap.odm.example.UserRepositoryImpl] executing {updateTelePhone}
       2015-05-09 10:33:22,941 INFO   main [ldap.odm.example.UserRepositoryImpl] executing {getUserDetail}
       2015-05-09 10:33:22,942 INFO   main [ldap.odm.example.UserRepositoryImpl] uid=spring_ldap_test,ou=users
       2015-05-09 10:33:22,948 INFO   main [ldap.odm.example.test.LdapODMApp] 
        Example of OdmManager update #=> User [uid=spring_ldap_test, cn=spring_ldap_test, sn=spring_ldap_test, postalAddress=spring_ldap_test, telephoneNumber=9831198311]
       2015-05-09 10:33:22,948 INFO   main [ldap.odm.example.UserRepositoryImpl] executing {getUserDetail}
       2015-05-09 10:33:22,948 INFO   main [ldap.odm.example.UserRepositoryImpl] uid=spring_ldap_test,ou=users
       2015-05-09 10:33:23,854 INFO   main [ldap.odm.example.test.LdapODMApp] 
        Example of OdmManager delete #=> Success
       
    Click here to download source code of above example

    Source Code


    • References:
    Spring LDAP ODM

    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.