Initial AspectJ support.

This commit is contained in:
Ben Alex 2004-10-18 06:41:20 +00:00
parent 992cf44b36
commit 7b0145fba7
8 changed files with 321 additions and 20 deletions

View File

@ -28,5 +28,6 @@
<classpathentry kind="lib" path="lib/cas/casclient-2.0.10.jar"/> <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/spring/spring-mock.jar"/>
<classpathentry kind="lib" path="lib/jakarta-commons/commons-collections.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"/> <classpathentry kind="output" path="target/eclipseclasses"/>
</classpath> </classpath>

View File

@ -1,8 +1,12 @@
Changes in version 0.7 (2004-xx-xx) Changes in version 0.7 (2004-xx-xx)
----------------------------------- -----------------------------------
* Added AspectJ support (especially useful for instance-level security)
* Added MethodDefinitionSourceAdvisor for performance and autoproxying * Added MethodDefinitionSourceAdvisor for performance and autoproxying
* Added MethodDefinitionMap querying of interfaces defined by secure objects * 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 * Documentation improvements
Changes in version 0.6.1 (2004-09-25) Changes in version 0.6.1 (2004-09-25)

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -7,7 +7,7 @@
<subtitle>Reference Documentation</subtitle> <subtitle>Reference Documentation</subtitle>
<releaseinfo>0.6.1</releaseinfo> <releaseinfo>0.7</releaseinfo>
<authorgroup> <authorgroup>
<author> <author>
@ -202,8 +202,8 @@
directly. For example, it would be possible to build a new secure directly. For example, it would be possible to build a new secure
object to secure calls to a messaging system that does not use object to secure calls to a messaging system that does not use
<literal>MethodInvocation</literal>s. Most Spring applications will <literal>MethodInvocation</literal>s. Most Spring applications will
simply use the two currently supported secure object types simply use the three currently supported secure object types
(<literal>MethodInvocation</literal> and (<literal>MethodInvocation</literal>, <literal>JoinPoint</literal> and
<literal>FilterInterceptor</literal>) with complete <literal>FilterInterceptor</literal>) with complete
transparency.</para> transparency.</para>
@ -214,7 +214,7 @@
<sect2 id="security-high-level-design-supported-secure-objects"> <sect2 id="security-high-level-design-supported-secure-objects">
<title>Supported Secure Objects</title> <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> secure objects.</para>
<para>The first handles an AOP Alliance <para>The first handles an AOP Alliance
@ -224,12 +224,24 @@
standard Spring-hosted bean available as a standard Spring-hosted bean available as a
<literal>MethodInvocation</literal>, the bean is simply published <literal>MethodInvocation</literal>, the bean is simply published
through a <literal>ProxyFactoryBean</literal> or through a <literal>ProxyFactoryBean</literal> or
<literal>BeanNameAutoProxyCreator</literal>. Most Spring developers <literal>BeanNameAutoProxyCreator</literal> or
would already be familiar with these due to their use in transactions <literal>DefaultAdvisorAutoProxyCreator</literal>. Most Spring
and other areas of Spring.</para> 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 <para>The second type is an AspectJ <literal>JoinPoint</literal>.
is an object included with the Acegi Security System for Spring. It is 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 created by an included filter and simply wraps the HTTP
<literal>ServletRequest</literal>, <literal>ServletResponse</literal> <literal>ServletRequest</literal>, <literal>ServletResponse</literal>
and <literal>FilterChain</literal>. The and <literal>FilterChain</literal>. The
@ -410,9 +422,8 @@
</listitem> </listitem>
<listitem> <listitem>
<para>Call a secure object-specific <para>Proceed with the request execution of the secure
<literal>SecurityInterceptorCallback</literal> so that the request object.</para>
execution can proceed.</para>
</listitem> </listitem>
<listitem> <listitem>
@ -424,8 +435,8 @@
</listitem> </listitem>
<listitem> <listitem>
<para>Return any result received from the <para>Return any result received from the secure object
<literal>SecurityInterceptorCallback</literal>.</para> execution.</para>
</listitem> </listitem>
</orderedlist> </orderedlist>
@ -441,8 +452,8 @@
object-specific security interceptors are discussed below.</para> object-specific security interceptors are discussed below.</para>
</sect2> </sect2>
<sect2 id="security-interception-methodinvocation"> <sect2 id="security-interception-aopalliance">
<title>MethodInvocation Security Interceptor</title> <title>AOP Alliance (MethodInvocation) Security Interceptor</title>
<para>To secure <literal>MethodInvocation</literal>s, developers <para>To secure <literal>MethodInvocation</literal>s, developers
simply add a properly configured simply add a properly configured
@ -452,10 +463,15 @@
<literal>ProxyFactoryBean</literal> or <literal>ProxyFactoryBean</literal> or
<literal>BeanNameAutoProxyCreator</literal>, as commonly used by many <literal>BeanNameAutoProxyCreator</literal>, as commonly used by many
other parts of Spring (refer to the sample application for examples). 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> follows:</para>
<para><programlisting>&lt;bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor"&gt; <para><programlisting>&lt;bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;
&lt;property name="validateConfigAttributes"&gt;&lt;value&gt;true&lt;/value&gt;&lt;/property&gt; &lt;property name="validateConfigAttributes"&gt;&lt;value&gt;true&lt;/value&gt;&lt;/property&gt;
&lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt; &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
&lt;property name="accessDecisionManager"&gt;&lt;ref bean="accessDecisionManager"/&gt;&lt;/property&gt; &lt;property name="accessDecisionManager"&gt;&lt;ref bean="accessDecisionManager"/&gt;&lt;/property&gt;
@ -572,6 +588,124 @@
<literal>false</literal>.</para> <literal>false</literal>.</para>
</sect2> </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>&lt;bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor"&gt;
&lt;property name="validateConfigAttributes"&gt;&lt;value&gt;true&lt;/value&gt;&lt;/property&gt;
&lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
&lt;property name="accessDecisionManager"&gt;&lt;ref bean="accessDecisionManager"/&gt;&lt;/property&gt;
&lt;property name="runAsManager"&gt;&lt;ref bean="runAsManager"/&gt;&lt;/property&gt;
&lt;property name="objectDefinitionSource"&gt;
&lt;value&gt;
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
&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;</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)
&amp;&amp; execution(public * *(..)) &amp;&amp; !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>&lt;bean id="domainObjectInstanceSecurityAspect"
class="net.sf.acegisecurity.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf"&gt;
&lt;property name="securityInterceptor"&gt;&lt;ref bean="aspectJSecurityInterceptor"/&gt;&lt;/property&gt;
&lt;/bean&gt;</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"> <sect2 id="security-interception-filterinvocation">
<title>FilterInvocation Security Interceptor</title> <title>FilterInvocation Security Interceptor</title>

View File

@ -1,8 +1,8 @@
<project> <project>
<pomVersion>3</pomVersion> <pomVersion>3</pomVersion>
<artifactId>acegi-security</artifactId>
<name>Acegi Security System for Spring</name> <name>Acegi Security System for Spring</name>
<groupId>acegi</groupId> <groupId>acegi</groupId>
<artifactId>acegi-security</artifactId>
<currentVersion>0.7-SNAPSHOT</currentVersion> <currentVersion>0.7-SNAPSHOT</currentVersion>
<package>net.sf.acegisecurity</package> <package>net.sf.acegisecurity</package>
<description>Acegi Security System for Spring</description> <description>Acegi Security System for Spring</description>
@ -306,9 +306,18 @@
<groupId>acegi</groupId> <groupId>acegi</groupId>
<artifactId>resin-extracted</artifactId> <artifactId>resin-extracted</artifactId>
<version>unknown</version> <version>unknown</version>
<jar>${basedir}/lib/extracted/resin/resin-extracted.jar</jar>
<type>jar</type> <type>jar</type>
<properties/> <properties/>
</dependency> </dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.2</version>
<type>jar</type>
<url>http://eclipse.org/aspectj/</url>
<properties/>
</dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>${basedir}/src</sourceDirectory> <sourceDirectory>${basedir}/src</sourceDirectory>
@ -359,4 +368,4 @@
<report>maven-clover-plugin</report> <report>maven-clover-plugin</report>
</reports> </reports>
<properties/> <properties/>
</project> </project>

View File

@ -26,4 +26,9 @@ applications:
providing a performance benefit as MethodSecurityInterceptor is not called providing a performance benefit as MethodSecurityInterceptor is not called
for public (non-secure) objects. It also simplifies configuration. 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$ $Id$