11.4. Authentication
Task: Authenticate a Web Service User
Task Summary
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 { ... }
Secure POJO endpoints
Secure Plain Old Java Object (POJO) endpoints by defining a <security-constraint> in theWEB-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>
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.Define the security domain for POJO endpoints
Modify theJBOSS_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>
Define the security context
Configure the security context in theJBOSS_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 defaultUsersRolesLoginModule
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.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 { ... }
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>
Client side - set username and password
A web service client can use thejavax.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");
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
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 { ... }
Secure POJO endpoints
Secure Plain Old Java Object (POJO) endpoints by defining a <security-constraint> in theWEB-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.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.Define the security context
Configure the security context in theJBOSS_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
Procedure 11.1. On the Server
Specify Interceptors
Specify (possibly by using ajbossws-cxf.xml
descriptor):- An interceptor for performing authentication and populating a valid
SecurityContext
; the provided interceptor should extendorg.apache.cxf.ws.security.wss4j.AbstractUsernameTokenAuthenticatingInterceptor
.JBossWS integration comes withorg.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor
for this use. - An interceptor for performing authorization; CXF requires this to extend
org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor
.For instance, theSimpleAuthorizingInterceptor
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
Procedure 11.2. On the Client
- 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());
- 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 aWSS4JInInterceptor
: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 handlerpackage 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."); } }