Saturday, 27 September 2014

WebService security and policy implementation using CXF 2.5.2 and Jboss 5.0.1

A web service is a method of communication between multiple devices over the World Wide Web. Using Web Services we can publish application's functions to everyone.In my previous blog I have explained with example how to create simple jaxws webservice application using apache cxf Jaxws Web Services example using CXF .

This Blog instruction will help us to create a sample secure jaxws webservice using apahe cxf and webservice policies
(http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd).


Please note I choose wsdl and spring xml configuration to secure jaxws webservice application so it will be easy for user to switch on/off and configure security features without modifying service interface and implementation classes.

  • Pre-requsite
  • Instructions assume that jdk,jboss,cxf is installed without any error.

  • Implemenation instructions
    1. please open existing project that successfully created by using previous blog instructions previous blog instructions . Now we will modify existing project and secure the web application.
    2. Open Sample.wsdl under WEB-INF\wsdl\Sample.wsdl and add following 3 options is wsdl.
      • Username token with plain text
      This is first option . In this case user need to provide username and password.
       
      <!--  Option 1 -->
          <!-- UsernameTokenWithPlainTextPassword ## Simple username token policy plain text -->
           <!--  -->
              <wsp:Policy wsu:Id="UsernameTokenWithPlainTextPassword"
                 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                          xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:ExactlyOne>
                   <wsp:All>
                          <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                           <wsp:Policy>
                                  <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient" />
                              </wsp:Policy>
                          </sp:SupportingTokens>
                      </wsp:All>
                  </wsp:ExactlyOne>
               </wsp:Policy>
      
      • Username token with password hash
      This is second option . In this case user need to provide username and password.
       
      <!--  Option 2 -->
          <!-- UsernameTokenWithTimestampNoncePasswordHash ## UsernameToken With Hash -->
        <wsp:Policy wsu:Id="UsernameTokenWithTimestampNoncePasswordHash"
                      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                      xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
         <wsp:ExactlyOne>
          <wsp:All>
                        <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                               <wsp:Policy>
                                       <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                      <sp:HashPassword/>
                               </wsp:Policy>
                               </sp:UsernameToken>
                              </wsp:Policy>
                        </sp:SupportingTokens>
          </wsp:All>
         </wsp:ExactlyOne>
        </wsp:Policy>
      
      • SignEncryption
      This is third option . In this case user need to provide public key information. Policy for first signing and then encrypting all messages, with the certificate included in the message from client to server but only a thumb print on messages from the server to the client. we can say it is ws-trust feature.
      <!--  Option 3 -->
           <!-- SignEncryption ## Policy for first signing and then encrypting all messages, with the certificate
               included in the message from client to server but only a thumb print on messages from
               the server to the client. -->
           
             <wsp:Policy wsu:Id="SignEncryption"
                         xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                         xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
              <wsp:ExactlyOne>
                  <wsp:All>
                      <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <wsp:Policy>
                              <sp:InitiatorToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:InitiatorToken>
                              <sp:RecipientToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:RecipientToken>
                              <sp:AlgorithmSuite>
                                  <wsp:Policy>
                                      <sp:TripleDesRsa15/>
                                  </wsp:Policy>
                              </sp:AlgorithmSuite>
                              <sp:Layout>
                                  <wsp:Policy>
                                      <sp:Strict/>
                                  </wsp:Policy>
                              </sp:Layout>
                              <sp:IncludeTimestamp/>
                              <sp:OnlySignEntireHeadersAndBody/>
                          </wsp:Policy>
                      </sp:AsymmetricBinding>
                      <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:SignedParts>
                      <sp:EncryptedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:EncryptedParts>
                  </wsp:All>
              </wsp:ExactlyOne>
          </wsp:Policy>
      
      After adding above three security policy in wsdl we will use them one by one by referring the policy in wsdl:binding section. eg .
        
      <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithPlainTextPassword" />
      
      After adding three security policy wsdl will look like following.
      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <wsdl:definitions name="BSGLifecycleServices"
      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
      xmlns:tns="http://poc.kaustuv.com/ws/service/greetings"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://poc.kaustuv.com/ws/service/greetings">
       <!--  Option 1 -->
          <!-- UsernameTokenWithPlainTextPassword ## Simple username token policy plain text -->
           <!--  -->
              <wsp:Policy wsu:Id="UsernameTokenWithPlainTextPassword"
                 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                          xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:ExactlyOne>
                   <wsp:All>
                          <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                           <wsp:Policy>
                                  <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient" />
                              </wsp:Policy>
                          </sp:SupportingTokens>
                      </wsp:All>
                  </wsp:ExactlyOne>
               </wsp:Policy>
      
       <!--  Option 2 -->
          <!-- UsernameTokenWithTimestampNoncePasswordHash ## UsernameToken With Hash -->
          <!--
        <wsp:Policy wsu:Id="UsernameTokenWithTimestampNoncePasswordHash"
                      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
                      xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
         <wsp:ExactlyOne>
          <wsp:All>
                        <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                               <wsp:Policy>
                                       <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                      <sp:HashPassword/>
                               </wsp:Policy>
                               </sp:UsernameToken>
                              </wsp:Policy>
                        </sp:SupportingTokens>
          </wsp:All>
         </wsp:ExactlyOne>
        </wsp:Policy>
          -->
      
       <!--  Option 3 -->
           <!-- SignEncryption ## Policy for first signing and then encrypting all messages, with the certificate
               included in the message from client to server but only a thumb print on messages from
               the server to the client. -->
           <!--
             <wsp:Policy wsu:Id="SignEncryption"
                         xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                         xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
              <wsp:ExactlyOne>
                  <wsp:All>
                      <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <wsp:Policy>
                              <sp:InitiatorToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:InitiatorToken>
                              <sp:RecipientToken>
                                  <wsp:Policy>
                                      <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                                          <wsp:Policy>
                                              <sp:RequireThumbprintReference/>
                                          </wsp:Policy>
                                      </sp:X509Token>
                                  </wsp:Policy>
                              </sp:RecipientToken>
                              <sp:AlgorithmSuite>
                                  <wsp:Policy>
                                      <sp:TripleDesRsa15/>
                                  </wsp:Policy>
                              </sp:AlgorithmSuite>
                              <sp:Layout>
                                  <wsp:Policy>
                                      <sp:Strict/>
                                  </wsp:Policy>
                              </sp:Layout>
                              <sp:IncludeTimestamp/>
                              <sp:OnlySignEntireHeadersAndBody/>
                          </wsp:Policy>
                      </sp:AsymmetricBinding>
                      <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:SignedParts>
                      <sp:EncryptedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                          <sp:Body/>
                      </sp:EncryptedParts>
                  </wsp:All>
              </wsp:ExactlyOne>
          </wsp:Policy>
         -->
          <wsdl:types>
              <xsd:schema targetNamespace="http://poc.kaustuv.com/ws/service/greetings">
                  <xsd:element name="GreetingsRequest">
                      <xsd:complexType>
                          <xsd:sequence>
                              <xsd:element name="name" type="xsd:string" />
                          </xsd:sequence>
                      </xsd:complexType>
                  </xsd:element>
                  <xsd:element name="GreetingsResponse">
                      <xsd:complexType>
                          <xsd:sequence>
                              <xsd:element name="message" type="xsd:string" />
                          </xsd:sequence>
                      </xsd:complexType>
                  </xsd:element>
              </xsd:schema>
          </wsdl:types>
          <wsdl:message name="GreetingsReq">
              <wsdl:part element="tns:GreetingsRequest" name="parameterIn" />
          </wsdl:message>
          <wsdl:message name="GreetingsRes">
              <wsdl:part element="tns:GreetingsResponse" name="parameterOut" />
          </wsdl:message>
          <wsdl:portType name="Greetings">
              <wsdl:operation name="Greetings">
                  <wsdl:input message="tns:GreetingsReq" />
                  <wsdl:output message="tns:GreetingsRes" />
              </wsdl:operation>
          </wsdl:portType>
          <wsdl:binding name="GreetingsSOAPBinding" type="tns:Greetings">
              <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithPlainTextPassword" />
        <!-- wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#UsernameTokenWithTimestampNoncePasswordHash" /-->
        <!-- wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#SignEncryption" /-->
              <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
              <wsdl:operation name="Greetings">
                  <soap:operation soapAction="Greetings" />
                  <wsdl:input>
                      <soap:body use="literal" />
                  </wsdl:input>
                  <wsdl:output>
                      <soap:body use="literal" />
                  </wsdl:output>
              </wsdl:operation>
          </wsdl:binding>
          <wsdl:service name="GreetingsService">
              <wsdl:port name="GreetingsPort" binding="tns:GreetingsSOAPBinding">
                  <soap:address location="http://www.kaustuv.com/ws/service/" />
              </wsdl:port>
          </wsdl:service>
      </wsdl:definitions>
      
      It is strongly recommended to use above three options individually. Eg. suppose we are planning to use security option 1 then comment out other two security options and refer option 1 in wsd:binding section.
    3. Create a new package "com.kaustuv.poc.ws.security"
      • create a new CallbackHandler class ServerPasswordCallback under package "com.kaustuv.poc.ws.security". Add following code snippet.

      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package com.kaustuv.poc.ws.security;
      
      import java.io.IOException;
      
      import javax.security.auth.callback.Callback;
      import javax.security.auth.callback.CallbackHandler;
      import javax.security.auth.callback.UnsupportedCallbackException;
      
      import org.apache.ws.security.WSPasswordCallback;
      
      /**
       * This class is responsible to implement web service security features.
       *
       * @author KMaji
       *
       */
      public class ServerPasswordCallback implements CallbackHandler {
      
       private SignatureInfoBean signatureInfo;
      
       /**
        * Getter for the property signatureInfo.
        *
        * @return Returns the value of signatureInfo.
        */
       public SignatureInfoBean getSignatureInfo() {
        return signatureInfo;
       }
      
       /**
        * Setter for the property signatureInfo.
        *
        * @param signatureInfo
        *            The new value of signatureInfo.
        */
       public void setSignatureInfo(SignatureInfoBean signatureInfo) {
        this.signatureInfo = signatureInfo;
       }
      
       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback wsPasswordCallback = null;
        String password = null;
        for (int count = 0; count < callbacks.length; count++) {
         wsPasswordCallback = (WSPasswordCallback) callbacks[count];
         // String identifier = wsPasswordCallback.getIdentifier();
         switch (wsPasswordCallback.getUsage()) {
          case WSPasswordCallback.USERNAME_TOKEN :
           try {
            password = "password";
           } catch (Exception exp) {
            exp.printStackTrace();
           }
           wsPasswordCallback.setPassword(password);
           break;
          case WSPasswordCallback.DECRYPT :
           wsPasswordCallback.setPassword(signatureInfo.getSignatureServiceKeyPassword());
           break;
          case WSPasswordCallback.SIGNATURE :
           wsPasswordCallback.setPassword(signatureInfo.getSignatureServiceKeyPassword());
           break;
         }
        }
       }
      }
      

      • Create SignatureInfoBean class under package "com.kaustuv.poc.ws.security". Add following code snippet

      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package com.kaustuv.poc.ws.security;
      
      import java.io.Serializable;
      
      public class SignatureInfoBean implements Serializable {
      
       private static final long serialVersionUID = -5749465533676763004L;
      
       public SignatureInfoBean() {
       }
      
       private String signatureServiceKey = null;
       private String signatureServiceKeyPassword = null;
      
       /**
        * Getter for the property signatureServiceKey.
        *
        * @return Returns the value of signatureServiceKey.
        */
       public String getSignatureServiceKey() {
        return signatureServiceKey;
       }
      
       /**
        * Setter for the property signatureServiceKey.
        *
        * @param signatureServiceKey
        *            The new value of signatureServiceKey.
        */
       public void setSignatureServiceKey(String signatureServiceKey) {
        this.signatureServiceKey = signatureServiceKey;
       }
      
       /**
        * Getter for the property signatureServiceKeyPassword.
        *
        * @return Returns the value of signatureServiceKeyPassword.
        */
       public String getSignatureServiceKeyPassword() {
        return signatureServiceKeyPassword;
       }
      
       /**
        * Setter for the property signatureServiceKeyPassword.
        *
        * @param signatureServiceKeyPassword
        *            The new value of signatureServiceKeyPassword.
        */
       public void setSignatureServiceKeyPassword(String signatureServiceKeyPassword) {
        this.signatureServiceKeyPassword = signatureServiceKeyPassword;
       }
      }
      

    4. Open cxf-beans.xml under WEB-INF/conf and modify as following.

    5. <?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-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" />
          <bean id="greeting" class="com.kaustuv.poc.ws.service.greetings.GreetingsImpl" />
          <jaxws:endpoint xmlns:tns="http://poc.kaustuv.com/ws/service/greetings" id="greetings"
              implementor="#greeting" wsdlLocation="WEB-INF/wsdl/Sample.wsdl" endpointName="tns:GreetingsPort"
              serviceName="tns:GreetingsService" address="/Greetings">
              <jaxws:features>
                  <bean class="org.apache.cxf.feature.LoggingFeature" />
              </jaxws:features>
              <!-- usernameToken option 1 and 2 -->
              <!--  -->
              <jaxws:properties>
                  <entry key="schema-validation-enabled" value="true" />
                  <entry key="ws-security.callback-handler">
                      <ref bean="serverPasswordCallback" />
                  </entry>
              </jaxws:properties>
             
               <!-- Sign Encryption option 3 only-->
               <!-- 
              <jaxws:properties>
                  <entry key="ws-security.signature.properties" value="file:D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.properties"/>
                  <entry key="ws-security.signature.username" value="myservicekey"/>
                  <entry key="ws-security.encryption.username" value="useReqSigCert"/>
                  <entry key="ws-security.callback-handler">
                      <ref bean="serverPasswordCallback" />
                  </entry>
              </jaxws:properties>
               -->
          </jaxws:endpoint>
      </beans>
      
      In above xml there are two jaxws:properties configuration added for three security option that we already mentioned security options in wsdl. For option 1 and 2 written in wsdl we must enable following by following snippet in cxf-beans.xml.
           
      <jaxws:properties>
       <entry key="schema-validation-enabled" value="true" />
       <entry key="ws-security.callback-handler">
        <ref bean="serverPasswordCallback" />
       </entry>
      </jaxws:properties>
      
      For option 3 written in wsdl we must enable corresponding jaxws:properties by following snippet in cxf-beans.xml .
            
      <jaxws:properties>
       <entry key="ws-security.signature.properties" value="file:D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.properties"/>
       <entry key="ws-security.signature.username" value="myservicekey"/>
       <entry key="ws-security.encryption.username" value="useReqSigCert"/>
       <entry key="ws-security.callback-handler">
        <ref bean="serverPasswordCallback" />
       </entry>
      </jaxws:properties>
      
      Please notice we can not keep both jaxws:properties in cxf-beans.xml .
      • Create security-beans.xml under WEB-INF/conf and add following 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"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
          <bean id="serverPasswordCallback" class="com.kaustuv.poc.ws.security.ServerPasswordCallback">
              <property name="signatureInfo" ref="signatureInfo" />
          </bean>
          <bean id="signatureInfo" class="com.kaustuv.poc.ws.security.SignatureInfoBean">
              <property name="signatureServiceKey" value="myservicekey" />
              <property name="signatureServiceKeyPassword" value="skpass" />
          </bean>
      </beans>
      
      In abve xml we created two bean one is serverPasswordCallback and another one is signatureInfo both are used for security implementation.
    6. create folder name keys under WEB-INF/
      • create following private and public key and certificates under WEB-INF/keys
      key maker steps
      keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 730 -alias myservicekey -keypass skpass -storepass sspass -keystore serviceKeystore.jks -dname "cn=localhost"
      keytool -genkey -keyalg RSA -sigalg SHA1withRSA -validity 730 -alias myclientkey  -keypass ckpass -storepass cspass -keystore clientKeystore.jks -dname "cn=clientuser"
      keytool -export -rfc -keystore clientKeystore.jks -storepass cspass -alias myclientkey -file MyClient.cer
      keytool -import -trustcacerts -keystore serviceKeystore.jks -storepass sspass -alias myclientkey -file MyClient.cer -noprompt
      keytool -export -rfc -keystore serviceKeystore.jks -storepass sspass -alias myservicekey -file MyService.cer
      keytool -import -trustcacerts -keystore clientKeystore.jks -storepass cspass -alias myservicekey -file MyService.cer -noprompt
      

      • create serviceKeystore.properties under WEB-INF/keys and add following snippet

      org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
      org.apache.ws.security.crypto.merlin.keystore.file=D:/workspace/jaxws-security-example/WebContent/WEB-INF/keys/serviceKeystore.jks
      org.apache.ws.security.crypto.merlin.keystore.password=sspass
      org.apache.ws.security.crypto.merlin.keystore.type=jks
      org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
      
      Now for security option 3 server side is secured with private key and to access secured webservice then user need to provide public key. Once user is authorized by public key then user will be able to access webservice.
    7. open web.xml and modify as following
    8. This step is optional. We are doing it for just to be in safely sync with the examples.
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
        <display-name>jaxws-security-example</display-name>
         <servlet>
              <description>Apache CXF Endpoint</description>
              <display-name>cxf</display-name>
              <servlet-name>CXFServlet</servlet-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>/*</url-pattern>
          </servlet-mapping> 
          <listener>
           <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
         </listener>
         <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>WEB-INF/conf/*beans.xml</param-value>
         </context-param>
      </web-app>
      

    9. Export and package this as war(jaws-security-example.war).Destination path %JBOSS_HOME%\server\default\deploy
    10. Note:If we do not modify the web.xml then we do not need to change the war name.

    11. Open command prompt and go to directory %JBOSS_HOME\bin .
    12. Execute run.bat -c default -b 0.0.0.0
    13. click following link for our secure jaxws webservice project.
    14. http://localhost:8080/jaxws-security-example/Greetings?wsdl
      Using webservice policy is very transparent for webservice client to understand what kind of security is associated to service by readin published webservice wsdl link. Check highlighted part in following image.

        Post Development testing


    1. Option 1 security test.
    2. Following class will be used to test the username token policy.
      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package client;
      
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.Map;
      
      import javax.xml.namespace.QName;
      import javax.xml.ws.BindingProvider;
      import javax.xml.ws.Service;
      
      import org.apache.cxf.endpoint.Client;
      import org.apache.cxf.endpoint.Endpoint;
      import org.apache.cxf.frontend.ClientProxy;
      import org.apache.cxf.interceptor.LoggingInInterceptor;
      import org.apache.cxf.interceptor.LoggingOutInterceptor;
      import org.apache.log4j.BasicConfigurator;
      import org.apache.log4j.Level;
      import org.apache.log4j.Logger;
      
      import com.kaustuv.poc.ws.service.greetings.Greetings;
      import com.kaustuv.poc.ws.service.greetings.GreetingsRequest;
      import com.kaustuv.poc.ws.service.greetings.GreetingsResponse;
      
      /**
       * @author KMaji
       *
       */
      public class TestUserNameToken {
      
       private static final Logger LOG = Logger.getLogger(TestUserNameToken.class.getName());
      
       /**
        * @param args
        * @throws MalformedURLException
        */
       public static void main(String[] args) throws MalformedURLException {
        Service service = Service.create(new URL("http://localhost:8080/jaxws-security-example/Greetings?wsdl"),
          new QName("http://poc.kaustuv.com/ws/service/greetings",
          "GreetingsService"));
          
        Greetings greetings = service.getPort(Greetings.class);
        Client client = ClientProxy.getClient(greetings);
        Endpoint endPoint = client.getEndpoint();
        endPoint.getOutInterceptors().add(new LoggingOutInterceptor());
        endPoint.getInInterceptors().add(new LoggingInInterceptor());
      
        Map<String, Object> ctx = ((BindingProvider) greetings).getRequestContext();
      
        ctx.put("ws-security.username", "demo");
        ctx.put("ws-security.password", "password");
      
        GreetingsRequest gReq = new GreetingsRequest();
        gReq.setName("kaustuv");
      
        GreetingsResponse gRes = null;
        try {
         gRes = greetings.greetings(gReq);
        } catch (Throwable e) {
         LOG.error(e.getMessage(), e.getCause());
        }
        LOG.info("Response # " + gRes.getMessage());
       }
      }
      
      ID: 2
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[745], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], user-agent=[Apache CXF 2.5.2]}
      Payload: demopasswordkaustuv
      --------------------------------------
      18:58:33,784 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@74d345b8
      INFO: Outbound Message
      ---------------------------
      ID: 2
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: kaustuv
      --------------------------------------
      
      In above console log we saw the username and password (plain text) is wrapped in soap:header section.

    3. Option 2 security test.
    4. For testing option 2 we will use the same class which we used to test username token plain text.
      ID: 3
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length=[977], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], user-agent=[Apache CXF 2.5.2]}
      Payload: demoZQl7x4is11VIDiZXxZex7jAPe4g=/okFkV98Off1m8VYQs0BXg==2014-09-27T13:36:29.140Zkaustuv
      --------------------------------------
      19:06:29,471 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@46d4db6
      INFO: Outbound Message
      ---------------------------
      ID: 3
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: kaustuv
      --------------------------------------
      
      In above console log we saw the username and password (encrypted) is wrapped in soap:header section.

    5. Option 3 security test.
    6. Create clientcallback class for client application for encrypting message.
      /**
       *
       * Copyright (c) Kaustuv Maji , 2014
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       *
       */
      package client;
      
      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.ws.security.WSPasswordCallback;
      
      /**
       * @author KMaji
       *
       */
      public class SignEncrClientCallback implements CallbackHandler {
      
       private Map<String, String> passwords = new HashMap<String, String>();
      
       public SignEncrClientCallback() {
        passwords.put("myclientkey", "ckpass");
       }
      
       @Override
       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
         WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
         String pass = passwords.get(pc.getIdentifier());
         if (pass != null) {
          pc.setPassword(pass);
          return;
         }
        }
       }
      }
      
      Following class is main test class for security option 3.
      /**
      *
      * Copyright (c) Kaustuv Maji , 2014
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      *
      */
      package client;
      
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.Map;
      
      import javax.xml.namespace.QName;
      import javax.xml.ws.BindingProvider;
      import javax.xml.ws.Service;
      
      import org.apache.cxf.endpoint.Client;
      import org.apache.cxf.endpoint.Endpoint;
      import org.apache.cxf.frontend.ClientProxy;
      import org.apache.cxf.interceptor.LoggingInInterceptor;
      import org.apache.cxf.interceptor.LoggingOutInterceptor;
      import org.apache.log4j.BasicConfigurator;
      import org.apache.log4j.Level;
      import org.apache.log4j.Logger;
      
      import com.kaustuv.poc.ws.service.greetings.Greetings;
      import com.kaustuv.poc.ws.service.greetings.GreetingsRequest;
      import com.kaustuv.poc.ws.service.greetings.GreetingsResponse;
      
      /**
       * @author KMaji
       *
       */
      public class SignEncryptionClient {
      
       /**
        * @param args
        * @throws MalformedURLException
        */
       public static void main(String[] args) throws MalformedURLException {
        Service service = Service.create(new URL("http://localhost:8080/jaxws-security-example/Greetings?wsdl"),
          new QName("http://poc.kaustuv.com/ws/service/greetings",
          "GreetingsService"));
        Logger LOG = Logger.getLogger(SignEncryptionClient.class.getName());
        Greetings greetings = service.getPort(Greetings.class);
        Client client = ClientProxy.getClient(greetings);
        Endpoint endPoint = client.getEndpoint();
        endPoint.getOutInterceptors().add(new LoggingOutInterceptor());
        endPoint.getInInterceptors().add(new LoggingInInterceptor());
      
        Map<String, Object> ctx = ((BindingProvider) greetings).getRequestContext();
      
        ctx.put("ws-security.signature.properties", "clientKeystore.properties");
        ctx.put("ws-security.signature.username", "myclientkey");
        ctx.put("ws-security.encryption.properties", "clientKeystore.properties");
        ctx.put("ws-security.encryption.username", "myservicekey");
        ctx.put("ws-security.callback-handler", "client.SignEncrClientCallback");
      
        GreetingsRequest gReq = new GreetingsRequest();
        gReq.setName("kaustuv");
      
        GreetingsResponse gRes = null;
        try {
         gRes = greetings.greetings(gReq);
         LOG.info("Response #" + gRes.getMessage());
        } catch (Exception e) {
         e.printStackTrace();
        }
       }
      }
      
      ID: 4
      Address: http://localhost:8080/jaxws-security-example/Greetings
      Encoding: UTF-8
      Http-Method: POST
      Content-Type: text/xml; charset=UTF-8
      Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["Greetings"], transfer-encoding=[chunked], user-agent=[Apache CXF 2.5.2]}
      Payload: MIIBoTCCAQqgAwIBAgIEUVm7NzANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpjbGllbnR1c2VyMB4XDTEzMDQwMTE2NTIwN1oXDTE1MDQwMTE2NTIwN1owFTETMBEGA1UEAxMKY2xpZW50dXNlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmvRe7Fw4zS4z+whmwFfzuma9R2GbG1kkZTzUkogDdR2zes6qnk84LxIwbyoLDL4HOBrm4ENdGMknYletrLppejRqXdyu4mkbJ1FH0zLAuGJzxN5vZluu0uQJvc1YmKVAbsAwDsKnkvURLn471YtBAtCJsEdUyYOESTOc2lQrrSMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBbJfRlYAyhGMkEUqKkb1epaxa6TfR5aAE4UPV5fbw5H7EYPJkC7i5Rg1uUqSC7ZI4+fNnMGU2Pbg9at4FLCyB9++KhQu0hWsE/AfwIIff+57aXBU3n3oJK+5RUaYaTmQWQnTc3nwui2eRgIG9yGiR1wkNntvYNVXmKsXP+h1e9Kg==2014-09-27T13:56:49.689Z2014-09-27T14:01:49.689Zo4dTpJf/6Meble547wkYIbB6M60=Oi7ejMXN+dPXHSmYUdAH+kxmB3XTscAbqp3TfMgSxegIBMdgb/Ce/HukLqZqgpClDZFX+iMKHSR8hCNdOM7X3v/gdL+zJbvSAgrRDt7+aPv+wdtSSjSzdhEVlWM5zIEsQ0QIAoz8P55hZjbRl4/4kJEf5Fu/ez909jslcApgp6M=WBhxAWuZAfryjQe7v04w87EMtvI=yvK+/PtNMYLa+S2qnMe3ZMkm0zE=aXQTtQLlupIbjWV3lhhrxcABAheoCT1EWDQPx6qIb1xwV6PgFedQIuX5+3a/I9cyrPEBcgTTooBu
      +Aot8R8nYdcOhwv8jgfuhmlsAZPSQFd0KE7S2u/SmAB2tC5RUSNAFKb2/bf2VXEzTQyk6b/Muom+
      z509bUL8/jLA1lUJpu8=cJ4gcuZwuuqskkjU9Hi9dUe2jjtVT+xKMypmnQf4CxkI/VXchAo7xgNoSrJLIPUlbdfBJ/TZ4c2dGkyV5L6DSBGSQz7B1BcvWulWQfrlvrLy1r0YtvGUtzmeON4TPLV0LfTeS+vz6p1bd3EP0mg5dJaVrnhedSpp/x3FWvTDbS/5OWfnJjR9plwW0ggbdYs+gfVh2NhBHcv7cXty+ABA2d2Cz7WChsYXgDKHm4S9PXwFiayH1uebAxEwSfUz0U8eC+eMCyZEFDorG9ZJgRhbtIBvvPxzyvOVg9BJISXNgNFFq25J0n79I0nhEOKht1wePJXAux+HR7PPeTZEygrqcGzbJO36vBBGhrDSIK/DBg8YIzGHmq8RGQ==
      --------------------------------------
      19:26:52,164 INFO  [STDOUT] com.kaustuv.poc.ws.service.greetings.GreetingsRequest@3a4c421b
      INFO: Outbound Message
      ---------------------------
      ID: 4
      Encoding: UTF-8
      Content-Type: text/xml
      Headers: {}
      Payload: MIIBoTCCAQqgAwIBAgIEUVm7NzANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpjbGllbnR1c2VyMB4XDTEzMDQwMTE2NTIwN1oXDTE1MDQwMTE2NTIwN1owFTETMBEGA1UEAxMKY2xpZW50dXNlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmvRe7Fw4zS4z+whmwFfzuma9R2GbG1kkZTzUkogDdR2zes6qnk84LxIwbyoLDL4HOBrm4ENdGMknYletrLppejRqXdyu4mkbJ1FH0zLAuGJzxN5vZluu0uQJvc1YmKVAbsAwDsKnkvURLn471YtBAtCJsEdUyYOESTOc2lQrrSMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBbJfRlYAyhGMkEUqKkb1epaxa6TfR5aAE4UPV5fbw5H7EYPJkC7i5Rg1uUqSC7ZI4+fNnMGU2Pbg9at4FLCyB9++KhQu0hWsE/AfwIIff+57aXBU3n3oJK+5RUaYaTmQWQnTc3nwui2eRgIG9yGiR1wkNntvYNVXmKsXP+h1e9Kg==2014-09-27T13:56:52.253Z2014-09-27T14:01:52.253ZdXw6imyjRGr3un72RDm6T4MuYiaq2jHT3rtAWgQFWoeeCQXl05WMRi3sWVHyZFCmCZIqyjEh4yVbkNuS06KGRupQpP3PyC6f2YVFqxa5m2u0z+CZZRbeIxWuihwbL3mXDcJylOdgpfM8C1nJSXv6mbSKMk49NrSnL546FbUBRSc=SrBUKiCsZWWkUJ+g88BRlFbbv1Q=dI2z5gM35CEySeXSGZqTzb637dA=js1iy79LBQLszVLDR6q3keKlnXxTAb9DAe9OtXYC3Crm9tjd+jdox2F+JMacRa/frvgk+45ZmI2b1WTpn70ZdngTUlSSH393bmSFZKZnGiTDrzyPHNRYl/NPIESUsyW0kjQS6hK5RYt25SRHOGcBLrMz93VH6+DDSoqWGODKIO0=o4dTpJf/6Meble547wkYIbB6M60=HaP1FpvQM09q+e72nhytV2n/j/Nlz5wVk/wZdWWc234HPibx08f2/DGrvCuP9FdheC41XqgKHVa6/zLWw5I2jXoUIpxnVkyRyCxUHi3x7Cu7uQtRqvdGdhho0hrNI99d2fZ1QLDyXy3hRkiL3BD7d3Gmh1RTqLghDJOUkY6VqCvWT1xpVBV50Uu0fycub94br8fmKDZTOh8av78pF7DCfj9D+y3r4bvZwJXvVWbDbUVsKWFYcXQJJXwj1RahgxegvMnUfd5QRIPT0wympH41RnjccBMojBZ/NYLAwqMV0O32/IZZ2I7slkjYyoeQOp9OxgafQJnK3mM45ASaN+2VnSeY5W4d1AHgdi/PCfsTlJ6W7ry/gEXY2DXYh3ajyx9R
      --------------------------------------
      
      In above example console log we can see that entire message is encrypted now.
    Click here to download source code of above example

    Source Code


    • References:
    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.

    Sunday, 21 September 2014

    Restful Webservices security example using Apache cxf , Spring web security and Jboss 5.0.1

    REST (Representational State Transfer) is an architecture style that describes how to use HTTP to access web services.
    In my previous blog I have explained with example how to create simple RESTful webservice application.
    Restful Web Services example using CXF , JAXRS.
    This Blog instruction will help us to create a sample secure RESTful webservice using cxf , spring security api and jaxrs api.
    NB. I choose spring xml security configuration to secure RESTful webservice application so it will be easy for user to switch on/off and configure security features without modifying service interface and implementation classes.

  • Required Software
  • Pre-requsite
  • Instructions assume that jdk,jboss,cxf is installed without any error.

  • Instructions
    1. please open existing project that successfully created by using previous blog instructions previous blog instructions . Now we will modify existing project and secure the web application.

    2. Open web.xml under WEB-INF\web.xml and add following xml snippet.


    3. <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
          <display-name>jaxrs-security-example</display-name>
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>WEB-INF/conf/*beans.xml</param-value>
          </context-param>
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          <filter>
              <filter-name>springSecurityFilterChain</filter-name>
              <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>springSecurityFilterChain</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
          <servlet>
              <description>Apache CXF Endpoint</description>
              <display-name>cxf</display-name>
              <servlet-name>CXFServlet</servlet-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>/*</url-pattern>
          </servlet-mapping>
      </web-app>
      


    4. Create a new package "com.kaustuv.jaxrs.example.security"

    5. create a new customize exception class NotAuthorizedException under package "com.kaustuv.jaxrs.example.security". Add following code snippet.

    6. /**
      |
      | Copyright (c) Kaustuv Maji , 2013
      | 
      | Please do not use source code in production.
      | Repos -  https://github.com/kaustuvmaji
      | Blog  -  http://kaustuvmaji.blogspot.in 
      */
      package com.kaustuv.jaxrs.example.security;
      
      import javax.ws.rs.WebApplicationException;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.core.Response;
      import javax.xml.ws.WebFault;
      
      /**
       * @author KMaji
       *
       */
      @WebFault
      public class NotAuthorizedException extends WebApplicationException{
      
        private static final long serialVersionUID = -1203116970226591712L;
      
      
        public NotAuthorizedException(String faultString) {
          super(Response.status(Response.Status.UNAUTHORIZED)
                .entity(faultString).type(MediaType.TEXT_PLAIN).build());
          System.out.println("################NotAuthorizedException(String faultString)######################");
        }
      
      }
      

    7. Create SecurityExceptionMapper under package "com.kaustuv.jaxrs.example.security". Add following code snippet

    8. /**
      |
      | Copyright (c) Kaustuv Maji , 2014
      |
      | Please do not use source code in production.
      | Repos -  https://github.com/kaustuvmaji
      | Blog  -  http://kaustuvmaji.blogspot.in
      */
      package com.kaustuv.jaxrs.example.security;
      
      import javax.ws.rs.core.Response;
      import javax.ws.rs.ext.ExceptionMapper;
      import javax.ws.rs.ext.Provider;
      
      import com.kaustuv.jaxrs.example.security.NotAuthorizedException;
      /**
       *
       * @author kmaji
       */
      @Provider
      public class SecurityExceptionMapper implements ExceptionMapper {
        public Response toResponse(NotAuthorizedException exception) {
          Response.Status status;
          // This means that the client could not be authenticated. In this case the client may want to
          // send (new) credentials and we should return 401.
          status = Response.Status.UNAUTHORIZED;
          return Response.status(status).entity(exception.getLocalizedMessage()).build();
        }
      
      }
      

    9. Open global-beans.xml under WEB-INF/conf and add bean following line to create SecurityExceptionMapper bean.
    10. <bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
      
      We will use this exceptionProvider bean in our jaxrs server (global) configuration so all services will be able to use this bean.
      <?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:jaxrs="http://cxf.apache.org/jaxrs" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:security="http://www.springframework.org/schema/security"
      
       xsi:schemaLocation=" http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
       <import resource="classpath:META-INF/cxf/cxf*.xml" />
       <!-- Cxf Jason provider -->
          <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider">
              <property name="dropRootElement" value="true" />
              <property name="supportUnwrapped" value="true" />
          </bean>
          <!-- Cxf Jaxb Provider -->
          <bean id="jaxbXmlProvider" class="org.apache.cxf.jaxrs.proviacder.JAXBElementProvider" scope="prototype" />
          <!-- cxf exception generator -->
          <bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
          <!-- Cxf Cors filter (cross domain purpose)-->
          <bean id="cors-filter" class="org.apache.cxf.jaxrs.cors.CrossOriginResourceSharingFilter"/>
      </beans>
      

    11. Open jaxrs-beans.xml under WEB-INF/conf and refer exceptionProvider bean information in the jaxrs:providers section along with existing jsonProvider,jaxbXmlProvider,cors-filter

    12. <?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:jaxrs="http://cxf.apache.org/jaxrs"
          xmlns:cxf="http://cxf.apache.org/core"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xmlns:security="http://www.springframework.org/schema/security"
          xsi:schemaLocation=" http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
          http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
          <import resource="classpath:META-INF/cxf/cxf*.xml"/>
      
          <!-- Service -->
          <bean id="customerService" class="com.kaustuv.jaxrs.example.service.DemoCustomerServiceImpl"
          init-method="init"
          destroy-method="destroy" />
      
          <!-- Jaxrs Server -->
          <jaxrs:server id="restserver" address="/customerService">
              <!-- adding service bean -->
              <jaxrs:serviceBeans>
                  <ref bean="customerService" />
              </jaxrs:serviceBeans>
              <!-- adding media type provider -->
              <jaxrs:providers>
                  <ref bean="jsonProvider" />
                  <ref bean="jaxbXmlProvider" />
                  <ref bean="cors-filter" />
                  <ref bean="exceptionProvider"/>
              </jaxrs:providers>
               <jaxrs:features>
                  <cxf:logging/>
              </jaxrs:features>
              <!-- Keeping extention type -->
              <jaxrs:extensionMappings>
                  <entry key="json" value="application/json" />
                  <entry key="xml" value="application/xml" />
                  <entry key="html" value="text/html"/>
                  <entry key="feed" value="application/atom+xml"/>
              </jaxrs:extensionMappings>
          </jaxrs:server>
      </beans>
      

    13. Create a xml name security-beans.xml under WEB-INF/conf
    14. <?xml version="1.0" encoding="UTF-8"?>
      <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
       <context:component-scan base-package="com.kaustuv.jaxrs.example" />
       <!-- cxf exception generator -->
       <beans:bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
       <!-- Authentication manager configuration -->
       <authentication-manager alias="security">
        <authentication-provider>
         <user-service>
          <user name="kaustuv" password="kaustuv" authorities="ROLE_ADMIN" />
          <user name="get" password="get" authorities="ROLE_GET" />
          <user name="post" password="post" authorities="ROLE_POST" />
          <user name="put" password="put" authorities="ROLE_PUT" />
          <user name="delete" password="delete" authorities="ROLE_DELETE" />
         </user-service>
        </authentication-provider>
       </authentication-manager>
       <!-- HTTP basic authentication in Spring Security [option 1] -->
       <!--
         <http>
         <intercept-url pattern="/customerService/*" access="ROLE_ADMIN" />
         <http-basic />
         </http>
        -->
       <!-- HTTP basic authentication in Spring Security with http intercept url pattern [Option 2] -->
       <http create-session="stateless" use-expressions="true">
        <intercept-url pattern="/customerService/getCustomerById/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/customerService/addCustomer/**" access="hasRole('ROLE_POST')" />
        <intercept-url pattern="/customerService/updateCustomer/**" access="hasRole('ROLE_PUT')" />
        <!-- for multiple authorities use hasAnyRole -->
        <intercept-url pattern="/customerService/deleteCustomer/**" access="hasAnyRole('ROLE_ADMIN','ROLE_DELETE')" />
        <http-basic />
       </http>
      </beans:beans>
      
      Now in above xml we are protecting each resource path with the access role. Suppose we want to add customer then we have to use username=post and password=post to add customer otherwise we will not be able to access the resource path. Now carefully look in the authentication manager configuration section, there we configured authorities based on the username and password and in option 2 section we are saying that this path will be access by authenticated users. We can allow anonymous users to access any path by just providing permitAll.
       <intercept-url pattern="/" access="permitAll"/>
      
      In the above xml there is another section that is option 1. Please note that it is not allowed to keep both option open. Now if we use option 1 then we have to remove or commented out option 2 in the xml then we will uncomment the option 1 section . So by using option 1 each and every methods will be accessible by just providing username=kaustuv and password=kaustuv. In the post developement testing chapter we will the example snippets.
    15. Export and package this as war. Destination path %JBOSS_HOME%\server\default


    16. Open command prompt and go to directory %JBOSS_HOME\bin .
    17. Execute run.bat -c default -b 0.0.0.0
    18. click on our wadl url for our restful webservice project.
    19. http://localhost:8080/jaxrs-security-example/customerService?_wadl

        Post Development testing

    1. open soap ui and create a new project using our wadl url.
    2. http://localhost:8080/jaxrs-security-example/customerService?_wadl

      Test HTTP POST Method
    3. Explore soap ui project name RestCustomerService. Select Request of service addCustoer
    4. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be post/post.

    5. enter customer xml use following snippet and click on submit .
    6. <customer>
      <customerId>3</customerId>
      <customerName>testPost</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       



      Test HTTP PUT Method
    7. Explore soap ui project name RestCustomerService. Select Request of service updateCustomer
    8. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be put/put.
    9. enter customer xml use following snippet and click on submit .

    10. <customer>
      <customerId>3</customerId>
      <customerName>testUpdate</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       


      Test HTTP GET Method
    11. Explore soap ui project name RestCustomerService. Select Request of service getCustomerById
    12. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be kaustuv/kaustuv.
    13. enter value of parameter custId in following example picture value is 2 . click on submit

    14. Test HTTP DELETE Method
    15. Explore soap ui project name RestCustomerService. Select Request of service deleteCustomer
    16. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be delete/delete.
    17. enter value of parameter custId in following example picture value is 3 . click on submit .



    Click here to download source code of above example

    Source Code


    • References:
    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.