diff --git a/aspects/pom.xml b/aspects/pom.xml
new file mode 100644
index 0000000000..ec739b485d
--- /dev/null
+++ b/aspects/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ org.springframework.security
+ spring-security-parent
+ 3.0.0.CI-SNAPSHOT
+
+ jar
+ spring-security-aspects
+ Spring Security - Aspects
+
+
+ commons-logging
+ commons-logging
+
+
+ org.aspectj
+ aspectjweaver
+
+
+
+ org.springframework.security
+ spring-security-core
+ 3.0.0.CI-SNAPSHOT
+
+
+
+
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.0
+
+
+
+ org.aspectj
+ com.springsource.org.aspectj.runtime
+ 1.6.3.RELEASE
+
+
+ org.aspectj
+ com.springsource.org.aspectj.tools
+ 1.6.3.RELEASE
+
+
+
+
+
+ compile
+ test-compile
+
+
+
+
+
+ 1.6
+
+
+
+
+
diff --git a/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj b/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj
new file mode 100644
index 0000000000..b2d94bbcba
--- /dev/null
+++ b/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj
@@ -0,0 +1,66 @@
+package org.springframework.security.access.intercept.aspectj.aspect;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.access.intercept.aspectj.AspectJCallback;
+import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
+
+/**
+ * Concrete AspectJ transaction aspect using Spring Security @Secured annotation
+ * for JDK 1.5+.
+ *
+ *
+ * When using this aspect, you must annotate the implementation class
+ * (and/or methods within that class), not the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are not inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @since 1.0
+ * @version $Id$
+ */
+public aspect AnnotationSecurityAspect implements InitializingBean {
+
+ /**
+ * Matches the execution of any public method in a type with the Secured
+ * annotation, or any subtype of a type with the Secured annotation.
+ */
+ private pointcut executionOfAnyPublicMethodInAtSecuredType() :
+ execution(public * ((@Secured *)+).*(..)) && @this(Secured);
+
+ /**
+ * Matches the execution of any method with the Secured annotation.
+ */
+ private pointcut executionOfSecuredMethod() :
+ execution(* *(..)) && @annotation(Secured);
+
+ private pointcut securedMethodExecution() :
+ executionOfAnyPublicMethodInAtSecuredType() ||
+ executionOfSecuredMethod();
+
+ private AspectJSecurityInterceptor securityInterceptor;
+
+ Object around(): securedMethodExecution() {
+ if (this.securityInterceptor == null) {
+ return proceed();
+ }
+
+ AspectJCallback callback = new AspectJCallback() {
+ public Object proceedWithObject() {
+ return proceed();
+ }
+ };
+
+ return this.securityInterceptor.invoke(thisJoinPoint, callback);
+ }
+
+ public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
+ this.securityInterceptor = securityInterceptor;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (this.securityInterceptor == null)
+ throw new IllegalArgumentException("securityInterceptor required");
+ }
+
+}
diff --git a/aspects/src/main/resources/META-INF/aop.xml b/aspects/src/main/resources/META-INF/aop.xml
new file mode 100644
index 0000000000..a9d76e8e42
--- /dev/null
+++ b/aspects/src/main/resources/META-INF/aop.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index b58ebf5764..9d738b9137 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
ntlm
taglibs
portlet
+ aspects
samples
diff --git a/samples/aspectj/pom.xml b/samples/aspectj/pom.xml
new file mode 100644
index 0000000000..5226197829
--- /dev/null
+++ b/samples/aspectj/pom.xml
@@ -0,0 +1,89 @@
+
+
+ 4.0.0
+
+ org.springframework.security
+ spring-security-parent
+ 3.0.0.CI-SNAPSHOT
+
+ spring-security-samples-aspectj
+ jar
+ Spring Security Sample AspectJ
+
+
+
+ junit
+ junit
+ 4.6
+ test
+
+
+ org.springframework.security
+ spring-security-core
+ ${project.version}
+
+
+ org.springframework.security
+ spring-security-aspects
+ ${project.version}
+
+
+ org.springframework
+ spring-test
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.0.2
+
+
+ 1.6
+
+
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.0
+
+
+
+ org.aspectj
+ com.springsource.org.aspectj.runtime
+ 1.6.3.RELEASE
+
+
+ org.aspectj
+ com.springsource.org.aspectj.tools
+ 1.6.3.RELEASE
+
+
+
+
+
+ compile
+ test-compile
+
+
+
+
+
+
+ org.springframework.security
+ spring-security-aspects
+
+
+
+ 1.6
+
+
+
+
+
diff --git a/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java b/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java
new file mode 100644
index 0000000000..fee03b106b
--- /dev/null
+++ b/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java
@@ -0,0 +1,19 @@
+package sample.aspectj;
+
+import org.springframework.security.access.annotation.Secured;
+
+/**
+ * Service which is secured on the class level
+ *
+ * @author Mike Wiesner
+ * @since 3.0
+ * @version $Id$
+ */
+@Secured("ROLE_USER")
+public class SecuredService {
+
+ public void secureMethod() {
+ // nothing
+ }
+
+}
diff --git a/samples/aspectj/src/main/java/sample/aspectj/Service.java b/samples/aspectj/src/main/java/sample/aspectj/Service.java
new file mode 100644
index 0000000000..6a65009f62
--- /dev/null
+++ b/samples/aspectj/src/main/java/sample/aspectj/Service.java
@@ -0,0 +1,23 @@
+package sample.aspectj;
+
+import org.springframework.security.access.annotation.Secured;
+
+/**
+ * Service which is secured on method level
+ *
+ * @author Mike Wiesner
+ * @since 1.0
+ * @version $Id$
+ */
+public class Service {
+
+ @Secured("ROLE_USER")
+ public void secureMethod() {
+ // nothing
+ }
+
+ public void publicMethod() {
+ // nothing
+ }
+
+}
diff --git a/samples/aspectj/src/main/resources/aspectj-context.xml b/samples/aspectj/src/main/resources/aspectj-context.xml
new file mode 100644
index 0000000000..c21d2c2891
--- /dev/null
+++ b/samples/aspectj/src/main/resources/aspectj-context.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java b/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java
new file mode 100644
index 0000000000..40f618ac1b
--- /dev/null
+++ b/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java
@@ -0,0 +1,78 @@
+package sample.aspectj;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath:aspectj-context.xml")
+public class AspectJInterceptorTests {
+
+ @Autowired
+ private Service service;
+
+ @Autowired
+ private SecuredService securedService;
+
+ @Test
+ public void testPublicMethod() throws Exception {
+ service.publicMethod();
+ }
+
+ @Test(expected = AuthenticationCredentialsNotFoundException.class)
+ public void testSecuredMethodNotAuthenticated() throws Exception {
+ service.secureMethod();
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ public void testSecuredMethodWrongRole() throws Exception {
+ Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils
+ .createAuthorityList("ROLE_ADMIN"));
+ SecurityContextHolder.getContext().setAuthentication(token);
+ service.secureMethod();
+ }
+
+ @Test
+ public void testSecuredMethodEverythingOk() throws Exception {
+ Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils
+ .createAuthorityList("ROLE_USER"));
+ SecurityContextHolder.getContext().setAuthentication(token);
+ service.secureMethod();
+ }
+
+ @Test(expected = AuthenticationCredentialsNotFoundException.class)
+ public void testSecuredClassNotAuthenticated() throws Exception {
+ securedService.secureMethod();
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ public void testSecuredClassWrongRole() throws Exception {
+ Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils
+ .createAuthorityList("ROLE_ADMIN"));
+ SecurityContextHolder.getContext().setAuthentication(token);
+ securedService.secureMethod();
+ }
+
+ @Test
+ public void testSecuredClassEverythingOk() throws Exception {
+ Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils
+ .createAuthorityList("ROLE_USER"));
+ SecurityContextHolder.getContext().setAuthentication(token);
+ securedService.secureMethod();
+ }
+
+ @After
+ public void tearDown() {
+ SecurityContextHolder.clearContext();
+ }
+
+}
diff --git a/samples/pom.xml b/samples/pom.xml
index e16d5efcce..b7901f49b6 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -17,7 +17,8 @@
openid
ldap
portlet
- cas
+ cas
+ aspectj