Pārlūkot izejas kodu

Initial AspectJ support.

Ben Alex 21 gadi atpakaļ
vecāks
revīzija
7b0145fba7

+ 1 - 0
.classpath

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

+ 4 - 0
changelog.txt

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

+ 31 - 0
core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJCallback.java

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

+ 109 - 0
core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptor.java

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

+ 8 - 0
core/src/main/java/org/acegisecurity/intercept/method/aspectj/package.html

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

+ 153 - 19
docs/reference/src/index.xml

@@ -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
-        (<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
         <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
-        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
+        <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 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
         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
-            <literal>SecurityInterceptorCallback</literal> so that the request
-            execution can proceed.</para>
+            <para>Proceed with the request execution of the secure
+            object.</para>
           </listitem>
           </listitem>
 
 
           <listitem>
           <listitem>
@@ -424,8 +435,8 @@
           </listitem>
           </listitem>
 
 
           <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>
           </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">
-        <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
         <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>
 
 

+ 11 - 2
project.xml

@@ -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,7 +306,16 @@
             <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>
+            <properties/>
+        </dependency>
+        <dependency>
+            <groupId>aspectj</groupId>
+            <artifactId>aspectjrt</artifactId>
+            <version>1.2</version>
             <type>jar</type>
             <type>jar</type>
+            <url>http://eclipse.org/aspectj/</url>
             <properties/>
             <properties/>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>
@@ -359,4 +368,4 @@
         <report>maven-clover-plugin</report>
         <report>maven-clover-plugin</report>
     </reports>
     </reports>
     <properties/>
     <properties/>
-</project>
+</project>

+ 5 - 0
upgrade-06-07.txt

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