Initial AspectJ support.
This commit is contained in:
parent
992cf44b36
commit
7b0145fba7
|
@ -28,5 +28,6 @@
|
|||
<classpathentry kind="lib" path="lib/cas/casclient-2.0.10.jar"/>
|
||||
<classpathentry kind="lib" path="lib/spring/spring-mock.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jakarta-commons/commons-collections.jar"/>
|
||||
<classpathentry kind="lib" path="lib/aspectj/aspectjrt.jar"/>
|
||||
<classpathentry kind="output" path="target/eclipseclasses"/>
|
||||
</classpath>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
Changes in version 0.7 (2004-xx-xx)
|
||||
-----------------------------------
|
||||
|
||||
* Added AspectJ support (especially useful for instance-level security)
|
||||
* Added MethodDefinitionSourceAdvisor for performance and autoproxying
|
||||
* Added MethodDefinitionMap querying of interfaces defined by secure objects
|
||||
* Refactored MethodDefinitionSource to work with Method, not MethodInvocation
|
||||
* Refactored AbstractSecurityInterceptor to better support other AOP libraries
|
||||
* Moved MethodSecurityInterceptor to ...intercept.method.aopalliance package
|
||||
* Documentation improvements
|
||||
|
||||
Changes in version 0.6.1 (2004-09-25)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.intercept.method.aspectj;
|
||||
|
||||
/**
|
||||
* Called by the {@link AspectJSecurityInterceptor} when it wishes for the
|
||||
* AspectJ processing to continue. Typically implemented in the
|
||||
* <code>around()</code> advice as a simple <code>return proceed();</code>
|
||||
* statement.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AspectJCallback {
|
||||
//~ Methods ================================================================
|
||||
|
||||
public Object proceedWithObject();
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.intercept.method.aspectj;
|
||||
|
||||
import net.sf.acegisecurity.intercept.AbstractSecurityInterceptor;
|
||||
import net.sf.acegisecurity.intercept.InterceptorStatusToken;
|
||||
import net.sf.acegisecurity.intercept.ObjectDefinitionSource;
|
||||
import net.sf.acegisecurity.intercept.method.MethodDefinitionSource;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
|
||||
|
||||
/**
|
||||
* Provides security interception of AspectJ method invocations.
|
||||
*
|
||||
* <p>
|
||||
* The <code>ObjectDefinitionSource</code> required by this security
|
||||
* interceptor is of type {@link MethodDefinitionSource}. This is shared with
|
||||
* the AOP Alliance based security interceptor
|
||||
* (<code>MethodSecurityInterceptor</code>), since both work with Java
|
||||
* <code>Method</code>s.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The secure object type is <code>org.aspectj.lang.JointPoint</code>, which is
|
||||
* passed from the relevant <code>around()</code> advice. The
|
||||
* <code>around()</code> advice also passes an anonymous implementation of
|
||||
* {@link AspectJCallback} which contains the call for AspectJ to continue
|
||||
* processing: <code>return proceed();</code>.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AspectJSecurityInterceptor extends AbstractSecurityInterceptor {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private MethodDefinitionSource objectDefinitionSource;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setObjectDefinitionSource(MethodDefinitionSource newSource) {
|
||||
this.objectDefinitionSource = newSource;
|
||||
}
|
||||
|
||||
public MethodDefinitionSource getObjectDefinitionSource() {
|
||||
return this.objectDefinitionSource;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
if (!this.getAccessDecisionManager().supports(JoinPoint.class)) {
|
||||
throw new IllegalArgumentException(
|
||||
"AccessDecisionManager does not support JointPoint");
|
||||
}
|
||||
|
||||
if (!this.getRunAsManager().supports(JoinPoint.class)) {
|
||||
throw new IllegalArgumentException(
|
||||
"RunAsManager does not support JointPoint");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be used to enforce security on a
|
||||
* <code>JoinPoint</code>.
|
||||
*
|
||||
* @param jp The AspectJ joint point being invoked which requires a
|
||||
* security decision
|
||||
* @param advisorProceed the advice-defined anonymous class that implements
|
||||
* <code>AspectJCallback</code> containing a simple <code>return
|
||||
* proceed();</code> statement
|
||||
*
|
||||
* @return The returned value from the method invocation
|
||||
*/
|
||||
public Object invoke(JoinPoint jp, AspectJCallback advisorProceed) {
|
||||
Object result;
|
||||
InterceptorStatusToken token = super.beforeInvocation(jp);
|
||||
|
||||
try {
|
||||
result = advisorProceed.proceedWithObject();
|
||||
} finally {
|
||||
super.afterInvocation(token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ObjectDefinitionSource obtainObjectDefinitionSource() {
|
||||
return this.objectDefinitionSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
Enforces security for AspectJ <code>JointPoint</code>s, delegating secure
|
||||
object callbacks to the calling aspect.
|
||||
|
||||
<p>Refer to the reference guide for information on usage.
|
||||
</body>
|
||||
</html>
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<subtitle>Reference Documentation</subtitle>
|
||||
|
||||
<releaseinfo>0.6.1</releaseinfo>
|
||||
<releaseinfo>0.7</releaseinfo>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
|
@ -202,8 +202,8 @@
|
|||
directly. For example, it would be possible to build a new secure
|
||||
object to secure calls to a messaging system that does not use
|
||||
<literal>MethodInvocation</literal>s. Most Spring applications will
|
||||
simply use the two currently supported secure object types
|
||||
(<literal>MethodInvocation</literal> and
|
||||
simply use the three currently supported secure object types
|
||||
(<literal>MethodInvocation</literal>, <literal>JoinPoint</literal> and
|
||||
<literal>FilterInterceptor</literal>) with complete
|
||||
transparency.</para>
|
||||
|
||||
|
@ -214,7 +214,7 @@
|
|||
<sect2 id="security-high-level-design-supported-secure-objects">
|
||||
<title>Supported Secure Objects</title>
|
||||
|
||||
<para>The Acegi Security System for Spring currently supports two
|
||||
<para>The Acegi Security System for Spring currently supports three
|
||||
secure objects.</para>
|
||||
|
||||
<para>The first handles an AOP Alliance
|
||||
|
@ -224,12 +224,24 @@
|
|||
standard Spring-hosted bean available as a
|
||||
<literal>MethodInvocation</literal>, the bean is simply published
|
||||
through a <literal>ProxyFactoryBean</literal> or
|
||||
<literal>BeanNameAutoProxyCreator</literal>. Most Spring developers
|
||||
would already be familiar with these due to their use in transactions
|
||||
and other areas of Spring.</para>
|
||||
<literal>BeanNameAutoProxyCreator</literal> or
|
||||
<literal>DefaultAdvisorAutoProxyCreator</literal>. Most Spring
|
||||
developers would already be familiar with these due to their use in
|
||||
transactions and other areas of Spring.</para>
|
||||
|
||||
<para>The second type is a <literal>FilterInvocation</literal>. This
|
||||
is an object included with the Acegi Security System for Spring. It is
|
||||
<para>The second type is an AspectJ <literal>JoinPoint</literal>.
|
||||
AspectJ has a particular use in securing domain object instances, as
|
||||
these are most often managed outside the Spring bean container. By
|
||||
using AspectJ, standard constructs such as <literal>new
|
||||
Person();</literal> can be used and full security will be applied to
|
||||
them by Acegi Security. The
|
||||
<literal>AspectJSecurityInterceptor</literal> is still managed by
|
||||
Spring, which creates the aspect singleton and wires it with the
|
||||
appropriate authentication managers, access decision managers and so
|
||||
on.</para>
|
||||
|
||||
<para>The third type is a <literal>FilterInvocation</literal>. This is
|
||||
an object included with the Acegi Security System for Spring. It is
|
||||
created by an included filter and simply wraps the HTTP
|
||||
<literal>ServletRequest</literal>, <literal>ServletResponse</literal>
|
||||
and <literal>FilterChain</literal>. The
|
||||
|
@ -410,9 +422,8 @@
|
|||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Call a secure object-specific
|
||||
<literal>SecurityInterceptorCallback</literal> so that the request
|
||||
execution can proceed.</para>
|
||||
<para>Proceed with the request execution of the secure
|
||||
object.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
@ -424,8 +435,8 @@
|
|||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Return any result received from the
|
||||
<literal>SecurityInterceptorCallback</literal>.</para>
|
||||
<para>Return any result received from the secure object
|
||||
execution.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
|
@ -441,8 +452,8 @@
|
|||
object-specific security interceptors are discussed below.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-interception-methodinvocation">
|
||||
<title>MethodInvocation Security Interceptor</title>
|
||||
<sect2 id="security-interception-aopalliance">
|
||||
<title>AOP Alliance (MethodInvocation) Security Interceptor</title>
|
||||
|
||||
<para>To secure <literal>MethodInvocation</literal>s, developers
|
||||
simply add a properly configured
|
||||
|
@ -452,10 +463,15 @@
|
|||
<literal>ProxyFactoryBean</literal> or
|
||||
<literal>BeanNameAutoProxyCreator</literal>, as commonly used by many
|
||||
other parts of Spring (refer to the sample application for examples).
|
||||
The <literal>MethodSecurityInterceptor</literal> is configured as
|
||||
Alternatively, Acegi Security provides a
|
||||
<literal>MethodDefinitionSourceAdvisor</literal> which may be used
|
||||
with Spring's <literal>DefaultAdvisorAutoProxyCreator</literal> to
|
||||
automatically chain the security interceptor in front of any beans
|
||||
defined against the <literal>MethodSecurityInterceptor</literal>. The
|
||||
<literal>MethodSecurityInterceptor</literal> itself is configured as
|
||||
follows:</para>
|
||||
|
||||
<para><programlisting><bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
|
||||
<para><programlisting><bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
|
||||
<property name="validateConfigAttributes"><value>true</value></property>
|
||||
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
|
||||
|
@ -572,6 +588,124 @@
|
|||
<literal>false</literal>.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-interception-aspectj">
|
||||
<title>AspectJ (JoinPoint) Security Interceptor</title>
|
||||
|
||||
<para>The AspectJ security interceptor is very similar to the AOP
|
||||
Alliance security interceptor discussed in the previous section.
|
||||
Indeed we will only discuss the differences in this section.</para>
|
||||
|
||||
<para>The AspectJ interceptor is named
|
||||
<literal>AspectJSecurityInterceptor</literal>. Unlike the AOP Alliance
|
||||
security interceptor, which relies on the Spring application context
|
||||
to weave in the security interceptor via proxying, the
|
||||
<literal>AspectJSecurityInterceptor</literal> is weaved in via the
|
||||
AspectJ compiler. It would not be uncommon to use both types of
|
||||
security interceptors in the same application, with
|
||||
<literal>AspectJSecurityInterceptor</literal> being used for domain
|
||||
object instance security and the AOP Alliance
|
||||
<literal>MethodSecurityInterceptor</literal> being used for services
|
||||
layer security.</para>
|
||||
|
||||
<para>Let's first consider how the
|
||||
<literal>AspectJSecurityInterceptor</literal> is configured in the
|
||||
Spring application context:</para>
|
||||
|
||||
<para><programlisting><bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor">
|
||||
<property name="validateConfigAttributes"><value>true</value></property>
|
||||
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
|
||||
<property name="runAsManager"><ref bean="runAsManager"/></property>
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER
|
||||
net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER
|
||||
</value>
|
||||
</property>
|
||||
</bean></programlisting></para>
|
||||
|
||||
<para>As you can see, aside from the class name, the
|
||||
<literal>AspectJSecurityInterceptor</literal> is exactly the same as
|
||||
the AOP Alliance security interceptor. Indeed the two interceptors can
|
||||
share the same <literal>objectDefinitionSource</literal>, as the
|
||||
<literal>ObjectDefinitionSource</literal> works with
|
||||
<literal>java.lang.reflect.Method</literal>s rather than an AOP
|
||||
library-specific class. Of course, your access decisions have access
|
||||
to the relevant AOP library-specific invocation (ie
|
||||
<literal>MethodInvocation</literal> or <literal>JoinPoint</literal>)
|
||||
and as such can consider a range of addition criteria when making
|
||||
access decisions (such as method arguments).</para>
|
||||
|
||||
<para>Next you'll need to define an AspectJ <literal>aspect</literal>.
|
||||
For example:</para>
|
||||
|
||||
<para><programlisting>package net.sf.acegisecurity.samples.aspectj;
|
||||
|
||||
import net.sf.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor;
|
||||
import net.sf.acegisecurity.intercept.method.aspectj.AspectJCallback;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
|
||||
|
||||
private AspectJSecurityInterceptor securityInterceptor;
|
||||
|
||||
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
|
||||
&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);
|
||||
|
||||
Object around(): domainObjectInstanceExecution() {
|
||||
if (this.securityInterceptor != null) {
|
||||
AspectJCallback callback = new AspectJCallback() {
|
||||
public Object proceedWithObject() {
|
||||
return proceed();
|
||||
}
|
||||
};
|
||||
return this.securityInterceptor.invoke(thisJoinPoint, callback);
|
||||
} else {
|
||||
return proceed();
|
||||
}
|
||||
}
|
||||
|
||||
public AspectJSecurityInterceptor getSecurityInterceptor() {
|
||||
return securityInterceptor;
|
||||
}
|
||||
|
||||
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
|
||||
this.securityInterceptor = securityInterceptor;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.securityInterceptor == null)
|
||||
throw new IllegalArgumentException("securityInterceptor required");
|
||||
}
|
||||
}</programlisting></para>
|
||||
|
||||
<para>In the above example, the security interceptor will be applied
|
||||
to every instance of <literal>PersistableEntity</literal>, which is an
|
||||
abstract class not shown (you can use any other class or
|
||||
<literal>pointcut</literal> expression you like). For those curious,
|
||||
<literal>AspectJCallback</literal> is needed because the
|
||||
<literal>proceed();</literal> statement has special meaning only
|
||||
within an <literal>around()</literal> body. The
|
||||
<literal>AspectJSecurityInterceptor</literal> calls this anonymous
|
||||
<literal>AspectJCallback</literal> class when it wants the target
|
||||
object to continue.</para>
|
||||
|
||||
<para>You will need to configure Spring to load the aspect and wire it
|
||||
with the <literal>AspectJSecurityInterceptor</literal>. A bean
|
||||
declaration which achieves this is shown below:</para>
|
||||
|
||||
<para><programlisting><bean id="domainObjectInstanceSecurityAspect"
|
||||
class="net.sf.acegisecurity.samples.aspectj.DomainObjectInstanceSecurityAspect"
|
||||
factory-method="aspectOf">
|
||||
<property name="securityInterceptor"><ref bean="aspectJSecurityInterceptor"/></property>
|
||||
</bean></programlisting></para>
|
||||
|
||||
<para>That's it! Now you can create your beans from anywhere within
|
||||
your application, using whatever means you think fit (eg <literal>new
|
||||
Person();</literal>) and they will have the security interceptor
|
||||
applied.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-interception-filterinvocation">
|
||||
<title>FilterInvocation Security Interceptor</title>
|
||||
|
||||
|
|
13
project.xml
13
project.xml
|
@ -1,8 +1,8 @@
|
|||
<project>
|
||||
<pomVersion>3</pomVersion>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<name>Acegi Security System for Spring</name>
|
||||
<groupId>acegi</groupId>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
<currentVersion>0.7-SNAPSHOT</currentVersion>
|
||||
<package>net.sf.acegisecurity</package>
|
||||
<description>Acegi Security System for Spring</description>
|
||||
|
@ -306,9 +306,18 @@
|
|||
<groupId>acegi</groupId>
|
||||
<artifactId>resin-extracted</artifactId>
|
||||
<version>unknown</version>
|
||||
<jar>${basedir}/lib/extracted/resin/resin-extracted.jar</jar>
|
||||
<type>jar</type>
|
||||
<properties/>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>1.2</version>
|
||||
<type>jar</type>
|
||||
<url>http://eclipse.org/aspectj/</url>
|
||||
<properties/>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<sourceDirectory>${basedir}/src</sourceDirectory>
|
||||
|
@ -359,4 +368,4 @@
|
|||
<report>maven-clover-plugin</report>
|
||||
</reports>
|
||||
<properties/>
|
||||
</project>
|
||||
</project>
|
|
@ -26,4 +26,9 @@ applications:
|
|||
providing a performance benefit as MethodSecurityInterceptor is not called
|
||||
for public (non-secure) objects. It also simplifies configuration.
|
||||
|
||||
- MethodSecurityInterceptor has moved from
|
||||
net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor to
|
||||
net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor.
|
||||
A simple find and replace will suffice to update your application contexts.
|
||||
|
||||
$Id$
|
||||
|
|
Loading…
Reference in New Issue