mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 22:32:43 +00:00
SEC-25: Rolled back addition of EJB integration docbook to ref manual.
This commit is contained in:
parent
934e59a562
commit
f0c15f5b1a
@ -4485,376 +4485,6 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|||||||
<para>Finally, restart Tomcat.</para>
|
<para>Finally, restart Tomcat.</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
<chapter>
|
|
||||||
<title>Context propagation support for EJBs</title>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Rationale</title>
|
|
||||||
|
|
||||||
<para>Spring Security does provide transparent propagation of security
|
|
||||||
context information in specific remoting scenarios. That means that
|
|
||||||
the security context of the invoker is passed along with each method
|
|
||||||
invocation to the server. There the context is being reestablished so
|
|
||||||
that a service method can take place in the security context of the
|
|
||||||
invoker. There is out-of-the-box support for RMI or Spring's own
|
|
||||||
HttpInvoker protocol. Now many applications around are based on EJB
|
|
||||||
remoting because of requirements or policy, which are precluded from
|
|
||||||
that benefit.</para>
|
|
||||||
|
|
||||||
<para>If you have an EJB application you have two options for
|
|
||||||
integrating Spring Security:</para>
|
|
||||||
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Use a container adapter for integrating with that
|
|
||||||
application server's proprietary security mechanism (Spring
|
|
||||||
Security provides some ready usable adapters for use in legacy
|
|
||||||
scenarios)</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para>Create wrappers on the client side (and on the server side
|
|
||||||
when using POJO delegation) to propagate the security
|
|
||||||
context</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
|
|
||||||
<para>Both approaches have the benefit that the implementation of your
|
|
||||||
service, as well as its clients, are agnostic to remoting and security
|
|
||||||
context propagation taking place. The drawback of the first approach
|
|
||||||
is, that you have to reimplement parts of your security infrastructure
|
|
||||||
when switching the container (or maybe upgrading it, as sometimes the
|
|
||||||
security strategy changes over time). That issue does not arise in the
|
|
||||||
second approach, but the problem here is the implementation overhead
|
|
||||||
of creating according wrappers to hide the context propagation.</para>
|
|
||||||
|
|
||||||
<para>The following chapters describe the infrastructure classes which
|
|
||||||
wrap the context propagation and abstract it away from both client
|
|
||||||
usage and service implementation.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>How it works</title>
|
|
||||||
|
|
||||||
<para>See figure 1 which describes the recommended POJO delegation
|
|
||||||
model, where an EJB session bean only serves as remoting wrapper. The
|
|
||||||
actual service implementation resides inside a POJO that implements
|
|
||||||
the given business interface. The EJB implementation does nothing more
|
|
||||||
than just delegating invocations of service operations to the POJO.
|
|
||||||
Using Spring's support classes for wiring the POJO to the EJB is
|
|
||||||
simple. Spring also allows wiring the service interface to the client
|
|
||||||
by transparently wrapping the remote interface.</para>
|
|
||||||
|
|
||||||
<para><figure>
|
|
||||||
<title>Figure 1: Using EJB as remoting wrapper</title>
|
|
||||||
|
|
||||||
<mediaobject>
|
|
||||||
<imageobject>
|
|
||||||
<imagedata fileref="images/ejb_pojo_delegation.gif" />
|
|
||||||
</imageobject>
|
|
||||||
</mediaobject>
|
|
||||||
</figure></para>
|
|
||||||
|
|
||||||
<para>This concept is now slightly enhanced by the context propagation
|
|
||||||
module. As it is not possible to intercept the invocation of a remote
|
|
||||||
interface in a portable manner, we have to extend the remote interface
|
|
||||||
methods to include an additional parameter that holdes the
|
|
||||||
SecurityContext we wish to propagate. In consequence we also have to
|
|
||||||
modify the method implementations in the EJB implementation class to
|
|
||||||
take that additional argument. This is depicted in figure 2. Note
|
|
||||||
however, that the client still works with the plain business interface
|
|
||||||
we had before and that the actual service implementation class is
|
|
||||||
still the same.</para>
|
|
||||||
|
|
||||||
<para><figure>
|
|
||||||
<title>Figure 2: Using EJB as enhanced remoting wrapper</title>
|
|
||||||
|
|
||||||
<mediaobject>
|
|
||||||
<imageobject>
|
|
||||||
<imagedata fileref="images/extended_ejb_pojo_delegation.gif" />
|
|
||||||
</imageobject>
|
|
||||||
</mediaobject>
|
|
||||||
</figure></para>
|
|
||||||
|
|
||||||
<para>Now how does this work? On the client side a proxy translates
|
|
||||||
invocations from the business interface (Service) to invocations of
|
|
||||||
the extended business interface (XService) which is actually exposed
|
|
||||||
by the remote interface. The current security context is extracted and
|
|
||||||
passed into the additionally provided method parameter.</para>
|
|
||||||
|
|
||||||
<para>On the server side the EJB implementation refers to a delegate
|
|
||||||
which also exposes the extended service interface (XService). Behind
|
|
||||||
that service interface hides a proxy which extracts the passed-in
|
|
||||||
SecurityContext and establishes it in the current thread. Then it
|
|
||||||
delegates the invocation to the actual delegate class (ServiceImpl),
|
|
||||||
translating the method invocation as the target does not know about
|
|
||||||
the SecurityContext parameter. The invocation chain is exemplified in
|
|
||||||
figure 3.</para>
|
|
||||||
|
|
||||||
<para><figure>
|
|
||||||
<title>Figure 3: Invocation chain</title>
|
|
||||||
|
|
||||||
<mediaobject>
|
|
||||||
<imageobject>
|
|
||||||
<imagedata fileref="images/invocation_chain.gif" />
|
|
||||||
</imageobject>
|
|
||||||
</mediaobject>
|
|
||||||
</figure>The yellow colored elements indicate instances exposing the
|
|
||||||
extended business interface while the green colored elements indicate
|
|
||||||
instances exposing the real business interface.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>How to use it</title>
|
|
||||||
|
|
||||||
<para>The following subsections show how to use the provided
|
|
||||||
infrastructure classes and how to enable your code to use them.</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>The building blocks</title>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>The business interface</title>
|
|
||||||
|
|
||||||
<para><programlisting format="linespecific">public interface Service {
|
|
||||||
|
|
||||||
Result operation(Parameter param);
|
|
||||||
}</programlisting>How the business interface looks like depends just on,
|
|
||||||
well, your business requirements. Nothing special about it.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>The service implementation</title>
|
|
||||||
|
|
||||||
<para><programlisting>public class ServiceImpl implements Service {
|
|
||||||
|
|
||||||
public Result operation(Parameter param) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}</programlisting>The service implementation just implements the business
|
|
||||||
interface, nothing more.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>The extended business interface and the remote
|
|
||||||
interface</title>
|
|
||||||
|
|
||||||
<para><programlisting>// Extended business interface
|
|
||||||
public interface XService {
|
|
||||||
|
|
||||||
public Result operation(SecurityContext securityCtx, Parameter param) throws SecurityServiceException, RemoteException;
|
|
||||||
} </programlisting></para>
|
|
||||||
|
|
||||||
<para><programlisting>// Remote interface
|
|
||||||
public interface ServiceRemote extends XService, EJBObject {}</programlisting>The
|
|
||||||
extended business interface introduces an additional parameter for
|
|
||||||
each method (which is by convention the first parameter). Besides
|
|
||||||
that augmentation of the parameter list we have to consider that
|
|
||||||
this interface is being used in an EJB remoting scenario, so we
|
|
||||||
specify a "throws RemoteException" for each method. This is not
|
|
||||||
strictly necessary but it allows us to greatly simplify the
|
|
||||||
definition of the actual remote interface as could be seen in the
|
|
||||||
second listing.</para>
|
|
||||||
|
|
||||||
<para>In addition to the RemoteException, each method also should
|
|
||||||
declare throwing a
|
|
||||||
org.springframework.security.ejb.server.SecurityServiceException.
|
|
||||||
This is because of the EJB spec: If an EJB method throws an
|
|
||||||
unchecked exception (all Spring Security exceptions are unchecked)
|
|
||||||
that exception will be wrapped into a RemoteException and the bean
|
|
||||||
instance will be dropped from the container. As this is unwanted
|
|
||||||
and negatively impacts performance we declare the
|
|
||||||
SecurityServiceException to be thrown. So any security exceptions
|
|
||||||
will be wrapped into a SecurityServiceException to be passed to
|
|
||||||
the client. Note that everything still works when that declaration
|
|
||||||
is omitted, but consider the negative consequences for the EJB in
|
|
||||||
question.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>The EJB implementation</title>
|
|
||||||
|
|
||||||
<para><programlisting>public class ServiceBean extends AbstractStatelessSessionBean implements XService {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegate bean name.
|
|
||||||
*/
|
|
||||||
private static final String DELEGATE_BEAN_NAME = "xService";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The delegate.
|
|
||||||
*/
|
|
||||||
private XService delegate;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.springframework.ejb.support.AbstractStatelessSessionBean#onEjbCreate()
|
|
||||||
*/
|
|
||||||
protected void onEjbCreate() throws CreateException {
|
|
||||||
this.delegate = (XService) getBeanFactory().getBean(DELEGATE_BEAN_NAME, XService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see XService#operation(SecurityContext, Parameter)
|
|
||||||
*/
|
|
||||||
public Result operation(SecurityContext securityCtx, Parameter param) throws SecurityServiceException, RemoteException {
|
|
||||||
return this.delegate.operation(ctx, name);
|
|
||||||
}
|
|
||||||
}</programlisting></para>
|
|
||||||
|
|
||||||
<para>It is recommended to use the convenience
|
|
||||||
AbstractStatelessSessionBean base class provided by the Spring
|
|
||||||
Framework. Implementation is then reduced to implementing the
|
|
||||||
onEjbCreate()-callback method to retrieve the delegate bean and to
|
|
||||||
implement the methods to do the delegation.</para>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Wiring the client-side</title>
|
|
||||||
|
|
||||||
<para><programlisting><?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
|
||||||
|
|
||||||
<beans>
|
|
||||||
|
|
||||||
<bean id="client" class="client.Client">
|
|
||||||
<property name="service" ref="service"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
|
|
||||||
<property name="environment">
|
|
||||||
<value>
|
|
||||||
// ...
|
|
||||||
</value>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="service" class="org.springframework.security.ejb.client.SecurityContextInjectingRemoteSlsbProxyFactoryBean">
|
|
||||||
|
|
||||||
<property name="businessInterface" value="server.Service"/>
|
|
||||||
|
|
||||||
<property name="slsbBusinessInterface" value="server.XService"/>
|
|
||||||
|
|
||||||
<property name="jndiTemplate" ref="jndiTemplate"/>
|
|
||||||
|
|
||||||
<property name="jndiName" value="ejb/Service"/>
|
|
||||||
|
|
||||||
<property name="extraArgumentPosition" value="0"/>
|
|
||||||
|
|
||||||
<property name="methodRegexpPatterns">
|
|
||||||
<list>
|
|
||||||
<value>.*</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
</bean>
|
|
||||||
</beans></programlisting>The above listing shows how the the service
|
|
||||||
is being wired to the client. Instead of using a
|
|
||||||
SimpleRemoteStatelessSessionProxyFactoryBean we use a
|
|
||||||
SecurityContextInjectingRemoteSlsbProxyFactoryBean which provides
|
|
||||||
the same functionality but is enhanced for hiding context
|
|
||||||
propagation.</para>
|
|
||||||
|
|
||||||
<para>The specified "businessInterface" property designates the
|
|
||||||
actual business interface while the property "slsbBusinessInterface"
|
|
||||||
specifies the extended business interface (it conforms to the
|
|
||||||
SimpleRemoteStatelessSessionProxyFactoryBean#businessInterface
|
|
||||||
property).</para>
|
|
||||||
|
|
||||||
<para>The "extraArgumentPosition" and "methodRegexpPatterns"
|
|
||||||
properties are optional. The former specifies the index of the
|
|
||||||
parameter to insert. The default is 0 which means that the
|
|
||||||
SecurityContext parameter will be the first one in the parameter
|
|
||||||
list. The latter parameter specifies which methods to enhance by an
|
|
||||||
additional SecurityContext parameter. By default all exposed methods
|
|
||||||
are enhanced.</para>
|
|
||||||
|
|
||||||
<para>Most other properties correspond to those in
|
|
||||||
SimpleRemoteStatelessSessionProxyFactoryBean. Please refer to the
|
|
||||||
API documentation for more details.</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Wiring the server-side</title>
|
|
||||||
|
|
||||||
<para><programlisting><?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
|
||||||
|
|
||||||
<beans>
|
|
||||||
|
|
||||||
<!-- Proxy for extracting secure context information -->
|
|
||||||
<bean id="xService" class="org.springframework.security.ejb.server.support.SecurityContextExtractingProxyFactoryBean">
|
|
||||||
|
|
||||||
<property name="businessInterface" value="server.XService"/>
|
|
||||||
|
|
||||||
<property name="interceptorNames">
|
|
||||||
<list>
|
|
||||||
<value>serviceSecurityInterceptor</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<property name="target" ref="serviceTarget"/>
|
|
||||||
|
|
||||||
<property name="extraArgumentPosition" value="0"/>
|
|
||||||
|
|
||||||
<property name="methodRegexpPatterns">
|
|
||||||
<list>
|
|
||||||
<value>.*</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<!-- Security interceptor -->
|
|
||||||
<bean id="helloServiceSecurityInterceptor" class="org.springframework.security.ejb.server.support.TranslatingMethodSecurityInterceptor">
|
|
||||||
<property name="validateConfigAttributes" value="true"/>
|
|
||||||
<property name="authenticationManager" ref="authenticationManager"/>
|
|
||||||
<property name="accessDecisionManager" ref="accessDecisionManager"/>
|
|
||||||
<property name="runAsManager" ref="runAsManager"/>
|
|
||||||
|
|
||||||
<property name="objectDefinitionSource">
|
|
||||||
<value>server.Service.*=ROLE_USER</value>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<!-- Service implementation -->
|
|
||||||
<bean id="serviceTarget" class="server.ServiceImpl">
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
</beans></programlisting></para>
|
|
||||||
|
|
||||||
<para>The definition of the EJB's delegate bean (xService) is
|
|
||||||
actually a SecurityContextExtractingProxyFactoryBean which creates a
|
|
||||||
proxy that takes care of extracting the SecurityContext,
|
|
||||||
establishing it, and delegating to the target bean
|
|
||||||
(serviceTarget).</para>
|
|
||||||
|
|
||||||
<para>The "businessInterface" property is again the extended
|
|
||||||
business interface exposed by proxies created by the factory bean.
|
|
||||||
The "target" refers to the actual target bean, while the
|
|
||||||
"interceptorNames" property refers to a list of interceptor names
|
|
||||||
which have to be defined in the application context.</para>
|
|
||||||
|
|
||||||
<para>Note that the security interceptor is of type
|
|
||||||
"TranslatingMethodSecurityInterceptor". It is a subclass of
|
|
||||||
org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor
|
|
||||||
and additionaly wraps all security exceptions into a
|
|
||||||
SecurityServiceException, if possible. For its parameterization
|
|
||||||
refer to the documentation of MethodSecurityInterceptor.</para>
|
|
||||||
|
|
||||||
<para>The "extraArgumentPosition" and "methodRegexpPatterns"
|
|
||||||
properties are again optional, specifying the position of the
|
|
||||||
additional SecurityContext parameter and which methods will be
|
|
||||||
augmented by it. It is important that both properties are specified
|
|
||||||
the same on client and server. As recommendation just omit
|
|
||||||
specifying these properties and stick with the default
|
|
||||||
values.</para>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</chapter>
|
|
||||||
</part>
|
</part>
|
||||||
|
|
||||||
<part id="authorization">
|
<part id="authorization">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user