Tuesday, February 4, 2014

Web Service Security with Axis1.4 and WSS4j1.5.12 using UserName Token

Applying  WSS4J based  Web Service  Security based on User Name Token to the existing Axis1.4 web service:

Assumption : Webservice Allready deployed on tomcat server with wsdl accessible and client is also generated using wsdl 
and there is an entry for axis servlet in web.xml already exist just like below
<servlet>
    <display-name>Apache-Axis Servlet</display-name>
    <servlet-name>AxisServlet</servlet-name>
    <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>/servlet/AxisServlet</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>*.jws</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>

WSS4j provides 4 kinds of ws security standerd
Now start applying security using user name token


A)    Server Side:
Make changes in the server-config.wsdd inside WEB-INF(server-config.wsdd is generated when creating wsdl from Java class using WSDL2Java toll or eclipse
Add <requestFlow> element in side <servive> element
For example
<ns1:service name="UserSoap" provider="java:RPC" style="wrapped" use="literal">
  <ns3:operation name="changePassword" qname="ns1:changePassword" returnQName="ns1:changePasswordReturn" returnType="ns2:UserBean" soapAction="" xmlns:ns1="http://user.services.soap.lcc.mycompany.com" xmlns:ns2="http://beans.user.services.soap.lcc.mycompany.com" xmlns:ns3="http://xml.apache.org/axis/wsdd/">
   <ns3:parameter qname="ns1:newPassword" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:userId" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:oldPassword" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:retypePassword" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:profile_chgpwd" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:last_pwdchg" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
   <ns3:parameter qname="ns1:submission" type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
  </ns3:operation>
  <ns1:requestFlow>
            <ns1:handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
               <parameter name="action" value="UsernameToken"/>
                <ns1:parameter name="passwordCallbackClass" value="com.mycompany.lcc.soap.services.user.ChangePasswordCallBackHandler"/>
            </ns1:handler>
   </ns1:requestFlow>
  <ns1:parameter name="allowedMethods" value="changePassword"/>
  <ns1:parameter name="typeMappingVersion" value="1.2"/>
  <ns1:parameter name="wsdlPortType" value="UserSoap"/>
  <ns1:parameter name="className" value="com.mycompany.lcc.soap.services.user.UserSoap"/>
  <ns1:parameter name="wsdlServicePort" value="UserSoap"/>
  <ns1:parameter name="schemaQualified" value="http://beans.user.services.soap.lcc.mycompany.com,http://user.services.soap.lcc.mycompany.com"/>
  <ns1:parameter name="wsdlTargetNamespace" value="http://user.services.soap.lcc.mycompany.com"/>
  <ns1:parameter name="wsdlServiceElement" value="UserSoapService"/>
  <ns1:typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="" qname="ns3:UserBean" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" type="java:com.mycompany.lcc.soap.services.user.beans.UserBean" xmlns:ns3="http://beans.user.services.soap.lcc.mycompany.com"/>
 </ns1:service>
Step2: Write a callBackHandler
package com.mycompany.lcc.soap.services.user;

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;

public class ChangePasswordCallBackHandler implements  CallbackHandler{

      @Override
      public void handle(Callback[] callbacks) throws IOException,
                  UnsupportedCallbackException {
            for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
            // set the password given a username

            if ("wss4j".equals(pwcb.getIdentifier())) {

                  pwcb.setPassword("security");

            }

      
         }
    }
           
      }



Redeploy the service. Your service should now be expecting a WSS Username Token in in the incoming requests, and clients should send the username "wss4j" and password "security" to get through.
Note:We can externalize the password in any config file
We can provide user Client jar which will contain user name and password
b) Client Side
Client Side:
Required Jars at client side: set in class path for writing calling class and Handler Class
1.       wss4j-1.5.12.jar
2.       xmlsec-1.4.5.jar
Step1:Create  a file client_deploy.wsdd in side your classes folder or  put in class path[for stand-alone and web client both]
Below is the sample client_deploy.wsdd
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
    xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender" />
    <globalConfiguration>
        <requestFlow>
           <handler type="java:org.apache.ws.axis.security.WSDoAllSender">
           <parameter name="user" value="wss4j"/>
           <parameter name="action" value="UsernameToken" />
           <parameter name="passwordType" value="PasswordDigest
           <parameter name="passwordCallbackClass"                 
                value="com.ChangePwdClientCallBackHandler" />
            </handler>
        </requestFlow>
    </globalConfiguration>
 </deployment>

Step2: Create a CallBack Handler Class to set the password
For example

package com;
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;

public class ChangePwdClientCallBackHandler implements CallbackHandler {

                @Override
                public void handle(Callback[] callbacks) throws IOException,
                                                UnsupportedCallbackException {
                               
for (int i = 0; i < callbacks.length; i++) {        
 if (callbacks[i] instanceof WSPasswordCallback) {
                WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
                // set the password given a username
                if ("wss4j".equals(pc.getIdentifier())) {
                    pc.setPassword("security");
                }
            } else {
                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
            }
    }
}
}

Step3: Calling the webservice
package com;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.configuration.FileProvider;
import com.mycompany.lcc.soap.services.user.UserSoap;
import com.mycompany.lcc.soap.services.user.UserSoapServiceLocator;
import com.mycompany.lcc.soap.services.user.beans.UserBean;
public class CpClient {
public static void main(String[] args) throws ServiceException ,RemoteException, MalformedURLException, FileNotFoundException{
                EngineConfiguration config = new FileProvider("./client_deploy.wsdd");
                UserSoapServiceLocator sl= new UserSoapServiceLocator(config);
                UserSoap us = sl.getUserSoap(new java.net.URL("http://localhost:8080/services/UserSoap"));
//this is actaul webservice method call
                UserBean u = us.changePassword("Test1234", "12345", "Test123", "Test1234", null, null,null);
                System.out.println("isSuccess:"+u.getServiceSuccess());
                System.out.println("Error mesg: "+u.getErrorMsg());
                System.out.println("Success msg: "+u.getSuccessMsg());
 }
}
Explanation:
Here I am calling the web service from an standalone client,we can also call from any POJO inside the web application
Please note that client_deploy.wsdd (client deployment descriptor file) is just one folder up from the calling class so
EngineConfiguration config = new FileProvider("./client_deploy.wsdd"); 
Above Line searches the descriptor file one level above ,if you have put descriptor in other location in class path,please specify relative path to wsdd file
ServiceLocator loads configuaration which calls the BackBackHandler Class defind in the wsdd file and Handler reads configuration and checks if user is ws4j then set the password as security and this information will now be passed to the webservice as part of SOAP header and Server will verify the password and then allow to service request if credentials matches.

No comments:

Post a Comment