11.4. Authentication

Read this section to learn how to authenticate a web service user using a number of available methods.

Task: Authenticate a Web Service User

Task Summary

The following procedure describes how to authenticate a web service user with JBossWS.
  1. Secure access to the Stateless Session Bean

    Secure access to the Stateless Session Bean (SLSB) using the @RolesAllowed, @PermitAll, @DenyAll annotations.
    The allowed user roles can be set with these annotations both on the bean class and on any of its business methods.
    @Stateless
    @RolesAllowed("friend")
    public class EndpointEJB implements EndpointInterface
    {
      ...
    }
  2. Secure POJO endpoints

    Secure Plain Old Java Object (POJO) endpoints by defining a <security-constraint> in the WEB-INF/web.xml file of the application. The <auth-constraint> <role-name> element specifies whether authentication is mandatory. It can be set to "not required" by specifying an asterisk value in the <role-name> element.
    <security-constraint>
      <web-resource-collection>
        <web-resource-name>All resources</web-resource-name>
        <url-pattern>/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>friend</role-name>
      </auth-constraint>
    </security-constraint>
    
    <security-role>
      <role-name>friend</role-name>
    </security-role>
  3. Define the security domain for EJB3 endpoints

    Declare the security domain by appending the @SecurityDomain annotation
    @Stateless
    @SecurityDomain("JBossWS")
    @RolesAllowed("friend")
    public class EndpointEJB implements EndpointInterface
    {
      ...
    }
    • You can also modify JBOSS_HOME/server/PROFILE/deploy/jbossws.sar/jboss-management.war/WEB-INF/jboss-web.xml and specify the security domain.
      <jboss-web>
        <security-domain>JBossWS</security-domain>
      </jboss-web>

    Note

    For more information about Security Domains, refer to the JBoss Security Guide.
  4. Define the security domain for POJO endpoints

    Modify the JBOSS_HOME/server/PROFILE/deploy/jbossws.sar/jboss-management.war/WEB-INF/jboss-web.xml and specify the security domain.
    <jboss-web>
    <security-domain>JBossWS</security-domain>
    </jboss-web>
    
  5. Define the security context

    Configure the security context in the JBOSS_HOME/server/PROFILE/conf/login-config.xml file.
    <!-- 
        A template configuration for the JBossWS security domain.
        This defaults to the UsersRolesLoginModule the same as other and should be
        changed to a stronger authentication mechanism as required.
    -->
    <application-policy name="JBossWS">
      <authentication>
        <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
          <module-option name="usersProperties">props/jbossws-users.properties</module-option>
          <module-option name="rolesProperties">props/jbossws-roles.properties</module-option>
          <module-option name="unauthenticatedIdentity">anonymous</module-option>
        </login-module>
      </authentication>
    </application-policy>

    Note

    The default UsersRolesLoginModule should be changed to another login module that offers security suitable for your enterprise deployment. Follow Task: Enable LDAP Authentication for steps to use the LdapLoginModule to control user authentication.
  6. Define HTTP basic authentication for EJB3 endpoints

    Use @WebContext annotation on the bean class.
    @Stateless
    @SecurityDomain("JBossWS")
    @RolesAllowed("friend")
    @WebContext(contextRoot="/my-cxt", urlPattern="/*", authMethod="BASIC", transportGuarantee="NONE", secureWSDLAccess=false)
    public class EndpointEJB implements EndpointInterface
    {
      ...
    }
    
  7. Define HTTP basic authentication for POJO endpoints

    Add into WEB-INF/web.xml of your web application
    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>Test Realm</realm-name>
    </login-config>
    
  8. Client side - set username and password

    A web service client can use the javax.xml.ws.BindingProvider interface to set the username and password combination.
    URL wsdlURL = new File("resources/jaxws/samples/context/WEB-INF/wsdl/TestEndpoint.wsdl").toURL();
    QName qname = new QName("http://org.jboss.ws/jaxws/context", "TestEndpointService");
    Service service = Service.create(wsdlURL, qname);
    port = (TestEndpoint)service.getPort(TestEndpoint.class);
     
    BindingProvider bp = (BindingProvider)port;
    bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "jsmith");
    bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "PaSSw0rd");
    
  9. Client side - WSDL secured

    Use java.net.Authenticator to set username and password when accessing wsdl file.
    Authenticator.setDefault(new Authenticator() {
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username,password.toCharArray());
        }
    });
    Service service = Service.create(wsdlURL, qname);
    

Task: Enable LDAP Authentication

Task Summary

Follow this task to configure Lightweight Directory Access Protocol (LDAP) authentication for a JBossWS application. You use the LdapLoginModule as described in the JBoss Security Guide.
The initial configuration is the same as Task: Authenticate a Web Service User.
  1. Secure access to the Stateless Session Bean

    Secure access to the Stateless Session Bean (SLSB) using the @RolesAllowed, @PermitAll, @DenyAll annotations.
    The allowed user roles can be set with these annotations both on the bean class and on any of its business methods.
    @Stateless
    @RolesAllowed("friend")
    public class EndpointEJB implements EndpointInterface
    {
      ...
    }
  2. Secure POJO endpoints

    Secure Plain Old Java Object (POJO) endpoints by defining a <security-constraint> in the WEB-INF/web.xml file of the application.
    The <auth-constraint> <role-name> element specifies whether authentication is mandatory. It can be set to "not required" by specifying an asterisk (*) value in the <role-name> element.
    <security-constraint>
      <web-resource-collection>
        <web-resource-name>All resources</web-resource-name>
        <url-pattern>/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>*</role-name>
      </auth-constraint>
    </security-constraint>
    
    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>JBossWS</realm-name>
    </login-config>

    Note

    For more information about valid <auth-method> values, refer to the Web Content Security Constraints section of the JBoss Security Guide.
  3. Define the security domain

    Declare the security domain by appending the @SecurityDomain annotation
    @Stateless
    @SecurityDomain("JBossWS")
    @RolesAllowed("friend")
    public class EndpointEJB implements EndpointInterface
    {
      ...
    }
    • You can also modify JBOSS_HOME/server/PROFILE/deploy/jbossws.sar/jboss-management.war/WEB-INF/jboss-web.xml and specify the security domain.
      <jboss-web>
        <security-domain>JBossWS</security-domain>
      </jboss-web>

    Note

    For more information about Security Domains, refer to the JBoss Security Guide.
  4. Define the security context

    Configure the security context in the JBOSS_HOME/server/PROFILE/conf/login-config.xml file.
                <application-policy name="JBossWS">
                  <authentication>
                    <login-module code="org.jboss.security.auth.spi.LdapLoginModule" flag="required">
                       <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
                       <module-option name="java.naming.provider.url">ldap://ldaphost.jboss.org:1389/</module-option>
                       <module-option name="java.naming.security.authentication">simple</module-option>
                       <module-option name="principalDNPrefix">uid=</module-option>
                       <module-option name="principalDNSuffix">,ou=People,dc=jboss,dc=org</module-option>
                       <module-option name="rolesCtxDN">ou=Roles,dc=jboss,dc=org</module-option>
                       <module-option name="uidAttributeID">member</module-option>
                       <module-option name="matchOnUserDN">true</module-option>
                       <module-option name="roleAttributeID">cn</module-option>
                       <module-option name="roleAttributeIsDN">false </module-option>
                    </login-module>
                  </authentication>
                </application-policy>
    

    Note

    Refer to the Security Guide for information about the LdapLoginModule and other available login modules.

11.4.1. Java Authentication and Authorization Service

Starting EAP 5.1.1 the username token information can be used for Java Authentication and Authorization Service (JAAS) on JBoss EA

Procedure 11.1. On the Server

  • Specify Interceptors

    Specify (possibly by using a jbossws-cxf.xml descriptor):
    1. An interceptor for performing authentication and populating a valid SecurityContext; the provided interceptor should extend org.apache.cxf.ws.security.wss4j.AbstractUsernameTokenAuthenticatingInterceptor.
      JBossWS integration comes with org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor for this use.
    2. An interceptor for performing authorization; CXF requires this to extend org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor.
      For instance, the SimpleAuthorizingInterceptor can be used for mapping endpoint operations to allowed roles.

    Example 11.1. SimpleAuthorizingInterceptor

    <beans
         xmlns='http://www.springframework.org/schema/beans'
         xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
         xmlns:beans='http://www.springframework.org/schema/beans'
         xmlns:jaxws='http://cxf.apache.org/jaxws'
         xmlns:util='http://www.springframework.org/schema/util'
         xsi:schemaLocation='http://cxf.apache.org/core
           http://cxf.apache.org/schemas/core.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-2.0.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd'>
         
         <bean id="SecurityContextIn"
            class="org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor">
           <constructor-arg>
             <map>
               <entry key="action" value="UsernameToken"/> 
             </map>
           </constructor-arg>
         </bean>
        
         <util:map id="methodPermissions">
            <entry key="sayHello" value="friend"/> 
            <entry key="greetMe" value="snoopies"/> 
         </util:map>
        
         <bean id="AuthorizeIn"
           class="org.apache.cxf.interceptor.security.SimpleAuthorizingInterceptor">
          <property name="methodRolesMap" ref="methodPermissions"/> 
         </bean>
         
         <jaxws:endpoint
           id='ServiceImpl'
           address='http://@jboss.bind.address@:8080/jaxws-samples-wsse-username-authorize'
           implementor='org.jboss.test.ws.jaxws.samples.wsse.ServiceImpl'>
           <jaxws:inInterceptors>
               <ref bean="SecurityContextIn"/>
               <ref bean="AuthorizeIn"/>
               <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
           </jaxws:inInterceptors>
         </jaxws:endpoint>
       </beans>
    Authentication and authorization will be delegated to the security domain configured for the endpoint.

Note

You can specify the login module you prefer for that security domain.
Refer to the Login Modules chapter of the JBoss Enterprise Application Platform Security Guide at docs.redhat.com for more information on this topic.

Procedure 11.2. On the Client

  1. Ensure the username is provided through the API (or a custom Spring configuration used to load the Bus):

    Example 11.2. Username API

    Endpoint cxfEndpoint = client.getEndpoint();
    Map<String, Object> outProps = new HashMap<String, Object>();
    outProps.put("action", "UsernameToken");
    outProps.put("user", username);
    outProps.put("passwordType", "PasswordText");
    outProps.put("passwordCallbackClass", "org.jboss.test.ws.jaxws.samples.wsse.UsernamePasswordCallback");
    WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps); //request
    cxfEndpoint.getOutInterceptors().add(wssOut);
    cxfEndpoint.getOutInterceptors().add(new SAAJOutInterceptor());
    
  2. The password instead is provided through a password callback handler that needs to implement javax.security.auth.callback.CallbackHandler, similarly to the keystore's password callback handler.
    If you are using an older JBossWS-CXF version, or you are not configuring the application server authorization integration, you can use a password callback handler on server side too, configured through a WSS4JInInterceptor:

    Example 11.3. Callback Handler

    <bean id="UsernameToken_Request" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
      <constructor-arg>
        <map>
          <entry key="action" value="UsernameToken"/> 
          <entry key="passwordCallbackClass" value="org.jboss.test.ws.jaxws.samples.wsse.ServerUsernamePasswordCallback"/> 
        </map>
      </constructor-arg>
    </bean>

    Example 11.4. WSS4JInInterceptor callback handler

    package org.jboss.test.ws.jaxws.samples.wsse;
     
    import java.io.IOException;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.UnsupportedCallbackException;
    import org.apache.ws.security.WSPasswordCallback;
     
    public class ServerUsernamePasswordCallback implements CallbackHandler
    {
       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
       {
          WSPasswordCallback pc = (WSPasswordCallback)callbacks[0];
          if (!("kermit".equals(pc.getIdentifier()) && "thefrog".equals(pc.getPassword())))
             throw new SecurityException("User '" + pc.getIdentifier() + "' with password '" + pc.getPassword() + "' not allowed.");
       }
    }