Hi,

JBoss AS7 provides support for Apache CXF 2.4.4, so in this demonstration we will see how to develop a Simple EJB3 Based CXF webService with Basic Authentication enabled. In this Example we will also see how we can use some JBoss Specific Annotations like “org.jboss.ejb3.annotation.SecurityDomain” to map the Security domain defined inside our “standalone-full.xml” file. We will also see how can we enable the BASIC Authentication using another JBoss Specific annotation “org.jboss.ws.api.annotation.WebContext“, the most useful part of this annotation is using this annotation we can define whether the user will have access to the wsdl can be accessed outside of the Basic Authentication scope or not. This can be specified using the @WebContext annotation’s attribute “secureWSDLAccess“.

In this example we are going to use “jboss-as-7.1.0.Beta1” which can be downloaded from the following link: http://www.jboss.org/jbossas/downloads

Configuring “security-domain” inside the “standalone-full.xml” file

Here we will first create a security-domain inside the security subsystem “/home/userone/jboss-as-7.1.0.Beta1/standalone/configuration/standard-full.xml” file as following:

        <subsystem xmlns="urn:jboss:domain:security:1.1">
            <security-domains>
                <security-domain name="other" cache-type="default">
                    <authentication>
                        <login-module code="UsersRoles" flag="required"/>
                    </authentication>
                </security-domain>
                <security-domain name="jboss-web-policy" cache-type="default">
                    <authorization>
                        <policy-module code="Delegating" flag="required"/>
                    </authorization>
                </security-domain>
                <security-domain name="jboss-ejb-policy" cache-type="default">
                    <authorization>
                        <policy-module code="Delegating" flag="required"/>
                    </authorization>
                </security-domain>

                <!--****************************************************************--> 
                <!-- Following Part is added Here for Our CXF Application security -->
                <security-domain name="cxfservice-security-domain" cache-type="default">
                    <authentication>
                        <login-module code="UsersRoles" flag="required">
                            <module-option name="usersProperties" value="cxfservice-users.properties"/>
                            <module-option name="rolesProperties" value="cxfservice-roles.properties"/>
                            <module-option name="unauthenticatedIdentity" value="nobody"/>
                        </login-module>
                    </authentication>
                </security-domain>
            </security-domains>
        </subsystem>

NOTE: the properties files “cxfservice-users.properties” and “cxfservice-roles.properties” we will later add inside our CXFBasedEJB3 Service Jar file.

Developing EJB3 based CXFWebService

Step1). Create a Directory somewhere in your filesystem like “/home/userone/EJB_CXFService_BasicAuth” and then create “src” directory inside “/home/userone/EJB_CXFService_BasicAuth”.

Step2). Create a property file with name “cxfservice-roles.properties” inside “/home/userone/EJB_CXFService_BasicAuth/src” as following:

userone=TestRole

Step3). Create a property file with name “cxfservice-users.properties” inside “/home/userone/EJB_CXFService_BasicAuth/src” as following:

userone=passwordone

Step4).Now we will write the EJB3 webservice endpoint interface with name “SecureCXF_EJB_Intf.java” inside “/home/userone/EJB_CXFService_BasicAuth/src” as following:

package ws;
public interface SecureCXF_EJB_Intf
  {
     String sayHello(String name);
  }

Step5).Now we will write the EJB3 webservice implementation class with name “SecureCXF_EJB.java” inside “/home/userone/EJB_CXFService_BasicAuth/src” as following:

package ws;
//Standard Annotations
import javax.jws.WebService;
import javax.ejb.Stateless;
import javax.annotation.security.RolesAllowed;

//JBoss Specific Annotations
import org.jboss.ws.api.annotation.WebContext;
import org.jboss.ejb3.annotation.SecurityDomain;

@WebService(name = "SecureCXF_EJB", targetNamespace="http://ws/")
@Stateless
@SecurityDomain("cxfservice-security-domain")
@RolesAllowed("TestRole")
@WebContext(contextRoot="/EJB_CXF_BasicAuthDemo", urlPattern="/*", authMethod="BASIC", transportGuarantee="NONE", secureWSDLAccess=false)
public class SecureCXF_EJB implements SecureCXF_EJB_Intf
{
    public String sayHello(String name)
     {
        System.out.println("nt [SecureCXF_EJB] sayHello() invoked : Hello, Mr. "+name);
        return "Hello, Mr. "+name;
     }
}

Step6). Now we will write the WebService Client “Client.java” inside “/home/userone/EJB_CXFService_BasicAuth/src” as following which demonstrates two ways of clients code:

package client;
import java.net.URL;
import  javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
public class Client
  {
       public static void main(String ar[]) throws Exception
       {
          String BASIC_USER="userone";
          String BASIC_PWD="passwordone";
          //String SERVICE_ENDPOINT="http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB";
          String SERVICE_ENDPOINT=ar[0];
          URL wsdl_URL=new URL(SERVICE_ENDPOINT+"?wsdl");
          QName SERVICE = new QName("http://ws/", "SecureCXF_EJBService");

          JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
          factory.setServiceClass(SecureCXFEJB.class);
          factory.setAddress(SERVICE_ENDPOINT);
          SecureCXFEJB service = (SecureCXFEJB) factory.create();
          System.out.println("Got the Service: "+service);
          BindingProvider bp = (BindingProvider)service;
          bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, BASIC_USER);
          bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, BASIC_PWD);
          System.out.println(service.sayHello("MiddlewareMagic"));

        /* // Below is the alternative Client Code which can be used to invole the service //

          SecureCXFEJBService service=new SecureCXFEJBService(wsdl_URL,SERVICE);
          SecureCXFEJB port=service.getSecureCXFEJBPort();
          System.out.println("Got the Service port: "+port);
          BindingProvider bp = (BindingProvider)port;
          bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, BASIC_USER);
          bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, BASIC_PWD);
          System.out.println(port.sayHello("MiddlewareMagic"));
        */
       }
  }

Step7). AS we are going to deploy the WebService in Exploded format inside the JBoss AS7 container so we will need to create and deploy a File with name “${AppName}.dodeploy” means in our case the filename will be “EJB_CXF_BasicAuthDemo.jar.dodeploy”. Create this file and place it inside “/home/userone/EJB_CXFService_BasicAuth/src” directory.

Step8). As everything is done so we will write a ant build script in order to build/deploy and test the WebService and Basic Authentication, So create a file “build.xml” inside “/home/userone/EJB_CXFService_BasicAuth” as following:

<project name="JBoss_Service" default="post-deploy">
<property name="jboss.home" value="/home/userone/jboss-as-7.1.0.Beta1" />
<property name="jboss.module.dir" value="${jboss.home}/modules" />
<property name="java.home.dir" value="/home/userone/MyJdks/jdk1.6.0_05" />
<property name="basedir" value="." />
<property name="jar.name" value="EJB_CXF_BasicAuthDemo.jar" />
<property name="src.dir" value="src" />
<property name="client.src.dir" value="${basedir}/clientSrc" />
<property name="output.dir" value="build" />
<property name="client.dir" value="${basedir}/clientStuff" />
<property name="client.jar.name" value="EJB_CXF_BasicAuth_Client.jar" />
 
   <path id="jboss.classpath">
     <fileset dir="${jboss.module.dir}">
        <include name="**/*.jar"/>
     </fileset>  
   </path>

   <path id="client.classpath">
     <fileset dir="${jboss.module.dir}">
        <include name="**/*.jar"/>
     </fileset>  
     <fileset dir="${client.dir}">
        <include name="*.jar"/>
     </fileset>  
   </path>

   <taskdef name="wsprovide" classname="org.jboss.ws.tools.ant.WSProvideTask">
       <classpath refid="jboss.classpath"/>
   </taskdef>

   <taskdef name="wsconsume" classname="org.jboss.ws.tools.ant.WSConsumeTask">
       <classpath refid="jboss.classpath"/>
   </taskdef>

        <target name="init">
           <delete dir="${output.dir}" />
           <mkdir dir="${output.dir}" />
           <mkdir dir="${output.dir}/${jar.name}"/>
           <mkdir dir="${output.dir}/${jar.name}/META-INF/wsdl"/>
           <delete dir="${client.dir}" />
           <mkdir dir="${client.dir}"/>
        </target>

    <target name="build" depends="init">
       <javac srcdir="${src.dir}" destdir="${output.dir}/${jar.name}"  includes="*.java" excludes="Client.java" classpathref="jboss.classpath"/>
       <wsprovide
        	fork="false"
        	keep="true"
        	destdir="${output.dir}"
        	resourcedestdir="${output.dir}/${jar.name}/META-INF/wsdl"
        	sourcedestdir="${output.dir}"
        	genwsdl="true" 
        	verbose="true"
        	sei="ws.SecureCXF_EJB">
            	<classpath>
                	  <pathelement path="${output.dir}/${jar.name}"/>
            	</classpath>
      </wsprovide>
        <copy todir="${output.dir}/${jar.name}">
	  <fileset dir="${basedir}/src">
	      <include name="*.properties"/>
	  </fileset>
	</copy>  
    </target>


        <target name="deploy" depends="build">
            <echo message="*******************  Deploying   *********************" />  
            <echo message="********** ${jar.name} to ${jboss.home}/standalone/deployments **********" />  
            <copy todir="${jboss.home}/standalone/deployments/">
                <fileset dir="${output.dir}/">
                  <include name="${jar.name}/**"/> 
                </fileset>
            </copy>
            <echo message="The EMPTY '${jar.name}.dodeploy' tells JBoss AS7 to deploy the application"/>
            <echo message="This file is needed if we want to deploy an exploded application in JBossAS7"/>
            <copy todir="${jboss.home}/standalone/deployments/">
	        <fileset dir="${basedir}/src">
	          <include name="${jar.name}.dodeploy"/>
	        </fileset>
	    </copy>   
            <echo message="*******************  Deployed Successfully   *********************" />  
        </target>
  
        <target name="post-deploy" depends="deploy">
            <echo message="*******************  NOTE  *********************" />
            <echo message="***** You should be able to access your WSDL using Browser now *****" />
            <echo message="http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB?wsdl" />
            <echo message="            " />
            <echo message="            " />
        </target>


        <target name="client">
             <delete dir="${client.dir}" />
             <wsconsume
                      fork="true"
                      keep="true"
                      destdir="${client.dir}"
                      sourcedestdir="${client.dir}"
                      package="client"
                      wsdlLocation="service.wsdl"
                      wsdl="http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB?wsdl">
            </wsconsume>
            <jar jarfile="${client.dir}/${client.jar.name}" basedir="${client.dir}" compress="true" />  
       </target>

        <target name="run" depends="client">
            <javac srcdir="${src.dir}" destdir="${client.dir}"  includes="Client.java" classpathref="client.classpath"/>
            <java classname="client.Client"  >
	        <classpath>
	            <pathelement location="${client.dir}"/>
	            <path refid="client.classpath"/>
	        </classpath>
                <arg value="http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB"/>
            </java>
        </target>  
</project>

NOTE: The only change in the above file you need to do is to change the “jboss.home” directory path in the second line of the above script is to point to your own JBoss AS7 directory home directory.

Step9). Now before running your ANT script to build and deploy the above webapplication you should have the ANT as well as JAVA set in the $PATH variable of the Shell / command prompt as following:

For Unix Based OS:
export PATH=/home/userone/jdk1.6.0_21/bin:/home/userone/org.apache.ant_1.6.5/bin:$PATH

For Windows Based OS:
set PATH=C:/jdk1.6.0_21/bin;C:/org.apache.ant_1.6.5/bin;%PATH%

Step10). Run the command “ant” or “ant post-deploy” which will internally build the WebService and deploy it inside the JBoss AS7 standalone profile.

[userone@localhost EJB_CXFService_BasicAuth]$ ant
Buildfile: build.xml

init:
   [delete] Deleting directory /home/userone/EJB_CXFService_BasicAuth/build
    [mkdir] Created dir: /home/userone/EJB_CXFService_BasicAuth/build
    [mkdir] Created dir: /home/userone/EJB_CXFService_BasicAuth/build/EJB_CXF_BasicAuthDemo.jar
    [mkdir] Created dir: /home/userone/EJB_CXFService_BasicAuth/build/EJB_CXF_BasicAuthDemo.jar/META-INF/wsdl
   [delete] Deleting directory /home/userone/EJB_CXFService_BasicAuth/clientStuff
    [mkdir] Created dir: /home/userone/EJB_CXFService_BasicAuth/clientStuff

build:
    [javac] Compiling 2 source files to /home/userone/EJB_CXFService_BasicAuth/build/EJB_CXF_BasicAuthDemo.jar
[wsprovide] Generating from endpoint: ws.SecureCXF_EJB
[wsprovide] log4j:WARN No appenders could be found for logger (org.apache.cxf.common.logging.LogUtils).
[wsprovide] log4j:WARN Please initialize the log4j system properly.
[wsprovide] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[wsprovide] java2ws -s /home/userone/EJB_CXFService_BasicAuth/build -classdir /home/userone/EJB_CXFService_BasicAuth/build -d /home/userone/EJB_CXFService_BasicAuth/build/EJB_CXF_BasicAuthDemo.jar/META-INF/wsdl -verbose -wsdl -cp :::::: -wrapperbean -createxsdimports ws.SecureCXF_EJB
[wsprovide] java2ws - Apache CXF 2.4.4
[wsprovide]
     [copy] Copying 2 files to /home/userone/EJB_CXFService_BasicAuth/build/EJB_CXF_BasicAuthDemo.jar

deploy:
     [echo] *******************  Deploying   *********************
     [echo] ********** EJB_CXF_BasicAuthDemo.jar to /home/userone/jboss-as-7.1.0.Beta1/standalone/deployments **********
     [copy] Copying 6 files to /home/userone/jboss-as-7.1.0.Beta1/standalone/deployments
     [echo] The EMPTY 'EJB_CXF_BasicAuthDemo.jar.dodeploy' tells JBoss AS7 to deploy the application
     [echo] This file is needed if we want to deploy an exploded application in JBossAS7
     [copy] Copying 1 file to /home/userone/jboss-as-7.1.0.Beta1/standalone/deployments
     [echo] *******************  Deployed Successfully   *********************

post-deploy:
     [echo] *******************  NOTE  *********************
     [echo] ***** You should be able to access your WSDL using Browser now *****
     [echo] http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB?wsdl
     [echo]
     [echo]

BUILD SUCCESSFUL
Total time: 4 seconds

Step11). As soon as the service is deployed on JBoss AS7 you will see the following kind of output in the JBoss console :

00:14:52,862 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-7) Starting deployment of "EJB_CXF_BasicAuthDemo.jar"
00:14:52,883 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-7) JNDI bindings for session bean named SecureCXF_EJB in deployment unit deployment "EJB_CXF_BasicAuthDemo.jar" are as follows:

00:14:52,911 INFO  [org.jboss.wsf.stack.cxf.metadata.MetadataBuilder] (MSC service thread 1-7) Add Service
 id=SecureCXF_EJB
 address=http://localhost:8080/EJB_CXF_BasicAuthDemo
 implementor=ws.SecureCXF_EJB
 invoker=org.jboss.wsf.stack.cxf.JBossWSInvoker
 serviceName={http://ws/}SecureCXF_EJBService
 portName={http://ws/}SecureCXF_EJBPort
 wsdlLocation=null
 mtomEnabled=false
00:14:52,911 INFO  [org.jboss.ws.common.management.DefaultEndpointRegistry] (MSC service thread 1-7) register: jboss.ws:context=EJB_CXF_BasicAuthDemo,endpoint=SecureCXF_EJB
00:14:52,919 INFO  [org.apache.cxf.service.factory.ReflectionServiceFactoryBean] (MSC service thread 1-7) Creating Service {http://ws/}SecureCXF_EJBService from class ws.SecureCXF_EJB
00:14:52,937 INFO  [org.apache.cxf.endpoint.ServerImpl] (MSC service thread 1-7) Setting the server's publish address to be http://localhost:8080/EJB_CXF_BasicAuthDemo
00:14:52,945 INFO  [org.jboss.wsf.stack.cxf.deployment.WSDLFilePublisher] (MSC service thread 1-7) WSDL published to: file:/home/userone/jboss-as-7.1.0.Beta1/standalone/data/wsdl/EJB_CXF_BasicAuthDemo.jar/SecureCXF_EJBService.wsdl
00:14:52,952 INFO  [org.jboss.as.webservices] (MSC service thread 1-6) JBAS015539: Starting service jboss.ws.endpoint."EJB_CXF_BasicAuthDemo.jar".SecureCXF_EJB
00:14:52,954 INFO  [org.jboss.web] (MSC service thread 1-2) registering web context: /EJB_CXF_BasicAuthDemo
00:14:52,971 INFO  [org.jboss.as.server.controller] (DeploymentScanner-threads - 2) Replaced deployment "EJB_CXF_BasicAuthDemo.jar" with deployment "EJB_CXF_BasicAuthDemo.jar"

Step12). Now run the command “ant run” in your shell prompt to run the WebService client to access the WebService operation using BASIC Authentication credentials. “ant run” command will automatically build the client side artifacts using the “wsconsume” ant task as well and then it will run the client:

[userone@localhost EJB_CXFService_BasicAuth]$ ant run
Buildfile: build.xml

client:
   [delete] Deleting directory /home/userone/EJB_CXFService_BasicAuth/clientStuff
[wsconsume] Consuming wsdl: http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB?wsdl
[wsconsume] log4j:WARN No appenders could be found for logger (org.apache.cxf.common.logging.LogUtils).
[wsconsume] log4j:WARN Please initialize the log4j system properly.
[wsconsume] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
      [jar] Building jar: /home/userone/EJB_CXFService_BasicAuth/clientStuff/EJB_CXF_BasicAuth_Client.jar

run:
    [javac] Compiling 1 source file to /home/userone/EJB_CXFService_BasicAuth/clientStuff
      log4j:WARN No appenders could be found for logger (org.apache.cxf.common.logging.LogUtils).
      log4j:WARN Please initialize the log4j system properly.
      log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
      Got the Service: org.apache.cxf.jaxws.JaxWsClientProxy@7e4e194f
      Hello, Mr. MiddlewareMagic

BUILD SUCCESSFUL
Total time: 8 seconds

NOTE: If you will pass a wrong credential to JBoss then you may see following kind of exceptions:

*************************On Server Side ***********************
01:07:34,864 ERROR [org.jboss.security.authentication.JBossCachedAuthenticationManager] (http-localhost-127.0.0.1-8080-6) Login failure: javax.security.auth.login.LoginException: java.lang.NullPointerException
	at org.jboss.security.vault.SecurityVaultUtil.isVaultFormat(SecurityVaultUtil.java:59)
	at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:250)
	at org.jboss.security.auth.spi.UsersRolesLoginModule.login(UsersRolesLoginModule.java:155)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at javax.security.auth.login.LoginContext.invoke(LoginContext.java:784)
************************* On Client Side ***********************
 Got the Service: org.apache.cxf.jaxws.JaxWsClientProxy@4977fa9a
 javax.xml.ws.WebServiceException: Could not send Message.
 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:194)
 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:764)
 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:218)
      .
      .
      .
 Caused by: javax.xml.ws.WebServiceException: Could not send Message.
 	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:145)
 	at $Proxy36.sayHello(Unknown Source)
 	at client.Client.main(Unknown Source)
      .
      .
      .
  Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response '401: Unauthorized' when communicating with http://localhost:8080/EJB_CXF_BasicAuthDemo/SecureCXF_EJB
 	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1554)
 	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1493)
 	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1401)
 	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
 	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:648)

.
.
Thanks
MiddlewareMagic Team

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.