Este conteúdo não está disponível no idioma selecionado.
8.6. Container Interceptors
8.6.1. About Container Interceptors
ejb-jar.xml
file for the 3.1 version of the ejb-jar deployment descriptor.
The container interceptors configured for an EJB are guaranteed to be run before the JBoss EAP provided security interceptors, transaction management interceptors, and other server provided interceptors. This allows specific application container interceptors to process or configure relevant context data before the invocation proceeds.
Although container interceptors are modeled to be similar to Java EE interceptors, there are some differences in the semantics of the API. For example, it is illegal for container interceptors to invoke the javax.interceptor.InvocationContext.getTarget()
method because these interceptors are invoked long before the EJB components are setup or instantiated.
8.6.2. Create a Container Interceptor Class
Container interceptor classes are simple Plain Old Java Objects (POJOs). They use the @javax.annotation.AroundInvoke
to mark the method that is invoked during the invocation on the bean.
iAmAround
method for invocation:
Example 8.2. Container Interceptor Class Example
public class ClassLevelContainerInterceptor { @AroundInvoke private Object iAmAround(final InvocationContext invocationContext) throws Exception { return this.getClass().getName() + " " + invocationContext.proceed(); } }
jboss-ejb3.xml
file described here: Section 8.6.3, “Configure a Container Interceptor”.
8.6.3. Configure a Container Interceptor
Container interceptors use the standard Java EE interceptor libraries, meaning they use the same XSD elements that are allowed in ejb-jar.xml
file for the 3.1 version of the ejb-jar deployment descriptor. Because they are based on the standard Jave EE interceptor libraries, container interceptors may only be configured using deployment descriptors. This was done by design so applications would not require any JBoss specific annotation or other library dependencies. For more information about container interceptors, refer to: Section 8.6.1, “About Container Interceptors”.
Procedure 8.11. Create the Descriptor File to Configure the Container Interceptor
- Create a
jboss-ejb3.xml
file in theMETA-INF
directory of the EJB deployment. - Configure the container interceptor elements in the descriptor file.
- Use the
urn:container-interceptors:1.0
namespace to specify configuration of container interceptor elements. - Use the
<container-interceptors>
element to specify the container interceptors. - Use the
<interceptor-binding>
elements to bind the container interceptor to the EJBs. The interceptors can be bound in either of the following ways:- Bind the interceptor to all the EJBs in the deployment using the
*
wildcard. - Bind the interceptor at the individual bean level using the specific EJB name.
- Bind the interceptor at the specific method level for the EJBs.
Note
These elements are configured using the EJB 3.1 XSD in the same way it is done for Java EE interceptors.
- Review the following descriptor file for examples of the above elements.
Example 8.3. jboss-ejb3.xml
<jboss xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:jee="http://java.sun.com/xml/ns/javaee" xmlns:ci ="urn:container-interceptors:1.0"> <jee:assembly-descriptor> <ci:container-interceptors> <!-- Default interceptor --> <jee:interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class> </jee:interceptor-binding> <!-- Class level container-interceptor --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class> </jee:interceptor-binding> <!-- Method specific container-interceptor --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class> <method> <method-name>echoWithMethodSpecificContainerInterceptor</method-name> </method> </jee:interceptor-binding> <!-- container interceptors in a specific order --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-order> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class> </interceptor-order> <method> <method-name>echoInSpecificOrderOfContainerInterceptors</method-name> </method> </jee:interceptor-binding> </ci:container-interceptors> </jee:assembly-descriptor> </jboss>
The XSD for theurn:container-interceptors:1.0
namespace is available atEAP_HOME/docs/schema/jboss-ejb-container-interceptors_1_0.xsd
.
8.6.4. Change the Security Context Identity
By default, when you make a remote call to an EJB deployed to the application server, the connection to the server is authenticated and any request received over this connection is executed as the identity that authenticated the connection. This is true for both client-to-server and server-to-server calls. If you need to use different identities from the same client, you normally need to open multiple connections to the server so that each one is authenticated as a different identity. Rather than open multiple client connections, you can give permission to the authenticated user to execute a request as a different user.
ejb-security-interceptors
quickstart for a complete working example.
Procedure 8.12. Change the Identity of the Security Context
Create the client side interceptor
The client side interceptor must implement theorg.jboss.ejb.client.EJBClientInterceptor
interface. The interceptor must pass the requested identity through the context data map, which can be obtained via a call toEJBClientInvocationContext.getContextData()
. The following is an example of client side interceptor code:public class ClientSecurityInterceptor implements EJBClientInterceptor { public void handleInvocation(EJBClientInvocationContext context) throws Exception { Principal currentPrincipal = SecurityActions.securityContextGetPrincipal(); if (currentPrincipal != null) { Map<String, Object> contextData = context.getContextData(); contextData.put(ServerSecurityInterceptor.DELEGATED_USER_KEY, currentPrincipal.getName()); } context.sendRequest(); } public Object handleInvocationResult(EJBClientInvocationContext context) throws Exception { return context.getResult(); } }
User applications can insert the interceptor into the interceptor chain in theEJBClientContext
in one of the following ways:Programmatically
With this approach, you call theorg.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
method and pass theorder
and theinterceptor
instance. Theorder
determines where this client interceptor is placed in the interceptor chain.ServiceLoader Mechanism
With this approach, you create aMETA-INF/services/org.jboss.ejb.client.EJBClientInterceptor
file and place or package it in the classpath of the client application. The rules for the file are dictated by the Java ServiceLoader Mechanism. This file is expected to contain a separate line for each fully qualified class name of the EJB client interceptor implementation. The EJB client interceptor classes must be available in the classpath. EJB client interceptors added using theServiceLoader
mechanism are added to the end of the client interceptor chain, in the order they are found in the classpath. Theejb-security-interceptors
quickstart uses this approach.
Create and configure the server side container interceptor
Container interceptor classes are simple Plain Old Java Objects (POJOs). They use the@javax.annotation.AroundInvoke
to mark the method that will be invoked during the invocation on the bean. For more information about container interceptors, refer to: Section 8.6.1, “About Container Interceptors”.Create the container interceptor
This interceptor receives theInvocationContext
with the identity and requests the switch to that new identity. The following is an abridged version of the actual code example:public class ServerSecurityInterceptor { private static final Logger logger = Logger.getLogger(ServerSecurityInterceptor.class); static final String DELEGATED_USER_KEY = ServerSecurityInterceptor.class.getName() + ".DelegationUser"; @AroundInvoke public Object aroundInvoke(final InvocationContext invocationContext) throws Exception { Principal desiredUser = null; UserPrincipal connectionUser = null; Map<String, Object> contextData = invocationContext.getContextData(); if (contextData.containsKey(DELEGATED_USER_KEY)) { desiredUser = new SimplePrincipal((String) contextData.get(DELEGATED_USER_KEY)); Collection<Principal> connectionPrincipals = SecurityActions.getConnectionPrincipals(); if (connectionPrincipals != null) { for (Principal current : connectionPrincipals) { if (current instanceof UserPrincipal) { connectionUser = (UserPrincipal) current; break; } } } else { throw new IllegalStateException("Delegation user requested but no user on connection found."); } } ContextStateCache stateCache = null; try { if (desiredUser != null && connectionUser != null && (desiredUser.getName().equals(connectionUser.getName()) == false)) { // The final part of this check is to verify that the change does actually indicate a change in user. try { // We have been requested to use an authentication token // so now we attempt the switch. stateCache = SecurityActions.pushIdentity(desiredUser, new OuterUserCredential(connectionUser)); } catch (Exception e) { logger.error("Failed to switch security context for user", e); // Don't propagate the exception stacktrace back to the client for security reasons throw new EJBAccessException("Unable to attempt switching of user."); } } return invocationContext.proceed(); } finally { // switch back to original context if (stateCache != null) { SecurityActions.popIdentity(stateCache);; } } }
Configure the container interceptor
For information on how to configure server side container interceptors, refer to: Section 8.6.3, “Configure a Container Interceptor”.
Create the JAAS LoginModule
This component is responsible for verifying that user is allowed to execute requests as the requested identity. The following abridged code examples show the methods that peform the login and validation:@SuppressWarnings("unchecked") @Override public boolean login() throws LoginException { if (super.login() == true) { log.debug("super.login()==true"); return true; } // Time to see if this is a delegation request. NameCallback ncb = new NameCallback("Username:"); ObjectCallback ocb = new ObjectCallback("Password:"); try { callbackHandler.handle(new Callback[] { ncb, ocb }); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } return false; // If the CallbackHandler can not handle the required callbacks then no chance. } String name = ncb.getName(); Object credential = ocb.getCredential(); if (credential instanceof OuterUserCredential) { // This credential type will only be seen for a delegation request, if not seen then the request is not for us. if (delegationAcceptable(name, (OuterUserCredential) credential)) { identity = new SimplePrincipal(name); if (getUseFirstPass()) { String userName = identity.getName(); if (log.isDebugEnabled()) log.debug("Storing username '" + userName + "' and empty password"); // Add the username and an empty password to the shared state map sharedState.put("javax.security.auth.login.name", identity); sharedState.put("javax.security.auth.login.password", ""); } loginOk = true; return true; } } return false; // Attempted login but not successful. } protected boolean delegationAcceptable(String requestedUser, OuterUserCredential connectionUser) { if (delegationMappings == null) { return false; } String[] allowedMappings = loadPropertyValue(connectionUser.getName(), connectionUser.getRealm()); if (allowedMappings.length == 1 && "*".equals(allowedMappings[1])) { // A wild card mapping was found. return true; } for (String current : allowedMappings) { if (requestedUser.equals(current)) { return true; } } return false; }
ejb-security-interceptors
quickstart README.html
file for complete instructions and more detailed information about the code.
8.6.5. Use a Client Side Interceptor in an Application
With this approach, you call the org.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
API and pass the order
and the interceptor
instance. The order
is used to determine where exactly in the client interceptor chain this interceptor
is placed.
With this approach, you create a META-INF/services/org.jboss.ejb.client.EJBClientInterceptor
file and place or package it in the classpath of the client application. The rules for the file are dictated by the Java ServiceLoader Mechanism. This file is expected to contain a separate line for each fully qualified class name of the EJB client interceptor implementation. The EJB client interceptor classes must be available in the classpath. EJB client interceptors added using the ServiceLoader
mechanism are added to the end of the client interceptor chain, in the order they are found in the classpath. The ejb-security-interceptors
quickstart uses this approach.