Răsfoiți Sursa

Merge remote-tracking branch 'origin/5.8.x' into main

Josh Cummings 3 ani în urmă
părinte
comite
b1fd9af723
24 a modificat fișierele cu 1223 adăugiri și 13 ștergeri
  1. 61 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/AbstractMethodInterceptorAspect.aj
  2. 98 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/JoinPointMethodInvocation.aj
  3. 44 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspect.aj
  4. 45 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostFilterAspect.aj
  5. 44 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspect.aj
  6. 45 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreFilterAspect.aj
  7. 56 0
      aspects/src/main/java/org/springframework/security/authorization/method/aspectj/SecuredAspect.aj
  8. 160 0
      aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspectTests.java
  9. 66 0
      aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostFilterAspectTests.java
  10. 160 0
      aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspectTests.java
  11. 66 0
      aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreFilterAspectTests.java
  12. 143 0
      aspects/src/test/java/org/springframework/security/authorization/method/aspectj/SecuredAspectTests.java
  13. 78 0
      config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAspectJAutoProxyRegistrar.java
  14. 7 1
      config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java
  15. 55 12
      config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java
  16. 3 0
      config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc
  17. 11 0
      config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd
  18. 27 0
      config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java
  19. 11 0
      config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java
  20. 28 0
      config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-AspectJMethodSecurityServiceEnabled.xml
  21. 4 0
      core/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJCallback.java
  22. 4 0
      core/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJMethodSecurityInterceptor.java
  23. 3 0
      core/src/main/java/org/springframework/security/access/intercept/aspectj/MethodInvocationAdapter.java
  24. 4 0
      docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc

+ 61 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/AbstractMethodInterceptorAspect.aj

@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.prepost.PostAuthorize;
+
+/**
+ * Abstract AspectJ aspect for adapting a {@link MethodInvocation}
+ *
+ * @author Josh Cummings
+ * @since 5.8
+ */
+abstract aspect AbstractMethodInterceptorAspect {
+
+	protected abstract pointcut executionOfAnnotatedMethod();
+
+	private MethodInterceptor securityInterceptor;
+
+	Object around(): executionOfAnnotatedMethod() {
+		if (this.securityInterceptor == null) {
+			return proceed();
+		}
+		MethodInvocation invocation = new JoinPointMethodInvocation(thisJoinPoint, () -> proceed());
+		try {
+			return this.securityInterceptor.invoke(invocation);
+		} catch (Throwable t) {
+			throwUnchecked(t);
+			throw new IllegalStateException("Code unexpectedly reached", t);
+		}
+	}
+
+	public void setSecurityInterceptor(MethodInterceptor securityInterceptor) {
+		this.securityInterceptor = securityInterceptor;
+	}
+
+	private static void throwUnchecked(Throwable ex) {
+		AbstractMethodInterceptorAspect.<RuntimeException>throwAny(ex);
+	}
+
+	@SuppressWarnings("unchecked")
+	private static <E extends Throwable> void throwAny(Throwable ex) throws E {
+		throw (E) ex;
+	}
+}

+ 98 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/JoinPointMethodInvocation.aj

@@ -0,0 +1,98 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+import java.util.function.Supplier;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.CodeSignature;
+
+import org.springframework.util.Assert;
+
+class JoinPointMethodInvocation implements MethodInvocation {
+
+	private final JoinPoint jp;
+
+	private final Method method;
+
+	private final Object target;
+
+	private final Supplier<Object> proceed;
+
+	JoinPointMethodInvocation(JoinPoint jp, Supplier<Object> proceed) {
+		this.jp = jp;
+		if (jp.getTarget() != null) {
+			this.target = jp.getTarget();
+		}
+		else {
+			// SEC-1295: target may be null if an ITD is in use
+			this.target = jp.getSignature().getDeclaringType();
+		}
+		String targetMethodName = jp.getStaticPart().getSignature().getName();
+		Class<?>[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes();
+		Class<?> declaringType = jp.getStaticPart().getSignature().getDeclaringType();
+		this.method = findMethod(targetMethodName, declaringType, types);
+		Assert.notNull(this.method, () -> "Could not obtain target method from JoinPoint: '" + jp + "'");
+		this.proceed = proceed;
+	}
+
+	private Method findMethod(String name, Class<?> declaringType, Class<?>[] params) {
+		Method method = null;
+		try {
+			method = declaringType.getMethod(name, params);
+		}
+		catch (NoSuchMethodException ignored) {
+		}
+		if (method == null) {
+			try {
+				method = declaringType.getDeclaredMethod(name, params);
+			}
+			catch (NoSuchMethodException ignored) {
+			}
+		}
+		return method;
+	}
+
+	@Override
+	public Method getMethod() {
+		return this.method;
+	}
+
+	@Override
+	public Object[] getArguments() {
+		return this.jp.getArgs();
+	}
+
+	@Override
+	public AccessibleObject getStaticPart() {
+		return this.method;
+	}
+
+	@Override
+	public Object getThis() {
+		return this.target;
+	}
+
+	@Override
+	public Object proceed() throws Throwable {
+		return this.proceed.get();
+	}
+
+}

+ 44 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspect.aj

@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.prepost.PostAuthorize;
+
+/**
+ * Concrete AspectJ aspect using Spring Security @PostAuthorize annotation.
+ *
+ * <p>
+ * When using this aspect, you <i>must</i> annotate the implementation class
+ * (and/or methods within that class), <i>not</i> the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are <i>not</i> inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @author Luke Taylor
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public aspect PostAuthorizeAspect extends AbstractMethodInterceptorAspect {
+
+	/**
+	 * Matches the execution of any method with a PostAuthorize annotation.
+	 */
+	protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostAuthorize);
+}

+ 45 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostFilterAspect.aj

@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.prepost.PostFilter;
+
+/**
+ * Concrete AspectJ aspect using Spring Security @PostFilter annotation.
+ *
+ * <p>
+ * When using this aspect, you <i>must</i> annotate the implementation class
+ * (and/or methods within that class), <i>not</i> the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are <i>not</i> inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @author Luke Taylor
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public aspect PostFilterAspect extends AbstractMethodInterceptorAspect {
+
+	/**
+	 * Matches the execution of any method with a PostFilter annotation.
+	 */
+	protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostFilter);
+
+}

+ 44 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspect.aj

@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * Concrete AspectJ aspect using Spring Security @PreAuthorize annotation.
+ *
+ * <p>
+ * When using this aspect, you <i>must</i> annotate the implementation class
+ * (and/or methods within that class), <i>not</i> the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are <i>not</i> inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @author Luke Taylor
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public aspect PreAuthorizeAspect extends AbstractMethodInterceptorAspect {
+
+	/**
+	 * Matches the execution of any method with a PreAuthorize annotation.
+	 */
+	protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreAuthorize);
+}

+ 45 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreFilterAspect.aj

@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.prepost.PreFilter;
+
+/**
+ * Concrete AspectJ aspect using Spring Security @PreFilter annotation.
+ *
+ * <p>
+ * When using this aspect, you <i>must</i> annotate the implementation class
+ * (and/or methods within that class), <i>not</i> the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are <i>not</i> inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @author Luke Taylor
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public aspect PreFilterAspect extends AbstractMethodInterceptorAspect {
+
+	/**
+	 * Matches the execution of any method with a PreFilter annotation.
+	 */
+	protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreFilter);
+
+}

+ 56 - 0
aspects/src/main/java/org/springframework/security/authorization/method/aspectj/SecuredAspect.aj

@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.annotation.Secured;
+
+/**
+ * Concrete AspectJ aspect using Spring Security @Secured annotation.
+ *
+ * <p>
+ * When using this aspect, you <i>must</i> annotate the implementation class
+ * (and/or methods within that class), <i>not</i> the interface (if any) that
+ * the class implements. AspectJ follows Java's rule that annotations on
+ * interfaces are <i>not</i> inherited. This will vary from Spring AOP.
+ *
+ * @author Mike Wiesner
+ * @author Luke Taylor
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public aspect SecuredAspect extends AbstractMethodInterceptorAspect {
+
+	/**
+	 * 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);
+
+	protected pointcut executionOfAnnotatedMethod() :
+			executionOfAnyPublicMethodInAtSecuredType() ||
+			executionOfSecuredMethod();
+}

+ 160 - 0
aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspectTests.java

@@ -0,0 +1,160 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.prepost.PostAuthorize;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public class PostAuthorizeAspectTests {
+
+	private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
+
+	private MethodInterceptor interceptor;
+
+	private SecuredImpl secured = new SecuredImpl();
+
+	private SecuredImplSubclass securedSub = new SecuredImplSubclass();
+
+	private PrePostSecured prePostSecured = new PrePostSecured();
+
+	@BeforeEach
+	public final void setUp() {
+		MockitoAnnotations.initMocks(this);
+		this.interceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize();
+		PostAuthorizeAspect secAspect = PostAuthorizeAspect.aspectOf();
+		secAspect.setSecurityInterceptor(this.interceptor);
+	}
+
+	@AfterEach
+	public void clearContext() {
+		SecurityContextHolder.clearContext();
+	}
+
+	@Test
+	public void securedInterfaceMethodAllowsAllAccess() {
+		this.secured.securedMethod();
+	}
+
+	@Test
+	public void securedClassMethodDeniesUnauthenticatedAccess() {
+		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
+				.isThrownBy(() -> this.secured.securedClassMethod());
+	}
+
+	@Test
+	public void securedClassMethodAllowsAccessToRoleA() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		this.secured.securedClassMethod();
+	}
+
+	@Test
+	public void internalPrivateCallIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());
+	}
+
+	@Test
+	public void protectedMethodIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());
+	}
+
+	@Test
+	public void overriddenProtectedMethodIsNotIntercepted() {
+		// AspectJ doesn't inherit annotations
+		this.securedSub.protectedMethod();
+	}
+
+	// SEC-1262
+	@Test
+	public void denyAllPreAuthorizeDeniesAccess() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
+	}
+
+	interface SecuredInterface {
+
+		@PostAuthorize("hasRole('X')")
+		void securedMethod();
+
+	}
+
+	static class SecuredImpl implements SecuredInterface {
+
+		// Not really secured because AspectJ doesn't inherit annotations from interfaces
+		@Override
+		public void securedMethod() {
+		}
+
+		@PostAuthorize("hasRole('A')")
+		void securedClassMethod() {
+		}
+
+		@PostAuthorize("hasRole('X')")
+		private void privateMethod() {
+		}
+
+		@PostAuthorize("hasRole('X')")
+		protected void protectedMethod() {
+		}
+
+		@PostAuthorize("hasRole('X')")
+		void publicCallsPrivate() {
+			privateMethod();
+		}
+
+	}
+
+	static class SecuredImplSubclass extends SecuredImpl {
+
+		@Override
+		protected void protectedMethod() {
+		}
+
+		@Override
+		public void publicCallsPrivate() {
+			super.publicCallsPrivate();
+		}
+
+	}
+
+	static class PrePostSecured {
+
+		@PostAuthorize("denyAll")
+		void denyAllMethod() {
+		}
+
+	}
+
+}

+ 66 - 0
aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostFilterAspectTests.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.security.access.prepost.PostFilter;
+import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public class PostFilterAspectTests {
+
+	private MethodInterceptor interceptor;
+
+	private PrePostSecured prePostSecured = new PrePostSecured();
+
+	@BeforeEach
+	public final void setUp() {
+		MockitoAnnotations.initMocks(this);
+		this.interceptor = new PostFilterAuthorizationMethodInterceptor();
+		PostFilterAspect secAspect = PostFilterAspect.aspectOf();
+		secAspect.setSecurityInterceptor(this.interceptor);
+	}
+
+	@Test
+	public void preFilterMethodWhenListThenFilters() {
+		List<String> objects = new ArrayList<>(Arrays.asList("apple", "banana", "aubergine", "orange"));
+		assertThat(this.prePostSecured.postFilterMethod(objects)).containsExactly("apple", "aubergine");
+	}
+
+	static class PrePostSecured {
+
+		@PostFilter("filterObject.startsWith('a')")
+		List<String> postFilterMethod(List<String> objects) {
+			return objects;
+		}
+
+	}
+
+}

+ 160 - 0
aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspectTests.java

@@ -0,0 +1,160 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public class PreAuthorizeAspectTests {
+
+	private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
+
+	private MethodInterceptor interceptor;
+
+	private SecuredImpl secured = new SecuredImpl();
+
+	private SecuredImplSubclass securedSub = new SecuredImplSubclass();
+
+	private PrePostSecured prePostSecured = new PrePostSecured();
+
+	@BeforeEach
+	public final void setUp() {
+		MockitoAnnotations.initMocks(this);
+		this.interceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
+		PreAuthorizeAspect secAspect = PreAuthorizeAspect.aspectOf();
+		secAspect.setSecurityInterceptor(this.interceptor);
+	}
+
+	@AfterEach
+	public void clearContext() {
+		SecurityContextHolder.clearContext();
+	}
+
+	@Test
+	public void securedInterfaceMethodAllowsAllAccess() {
+		this.secured.securedMethod();
+	}
+
+	@Test
+	public void securedClassMethodDeniesUnauthenticatedAccess() {
+		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
+				.isThrownBy(() -> this.secured.securedClassMethod());
+	}
+
+	@Test
+	public void securedClassMethodAllowsAccessToRoleA() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		this.secured.securedClassMethod();
+	}
+
+	@Test
+	public void internalPrivateCallIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());
+	}
+
+	@Test
+	public void protectedMethodIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());
+	}
+
+	@Test
+	public void overriddenProtectedMethodIsNotIntercepted() {
+		// AspectJ doesn't inherit annotations
+		this.securedSub.protectedMethod();
+	}
+
+	// SEC-1262
+	@Test
+	public void denyAllPreAuthorizeDeniesAccess() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
+	}
+
+	interface SecuredInterface {
+
+		@PreAuthorize("hasRole('X')")
+		void securedMethod();
+
+	}
+
+	static class SecuredImpl implements SecuredInterface {
+
+		// Not really secured because AspectJ doesn't inherit annotations from interfaces
+		@Override
+		public void securedMethod() {
+		}
+
+		@PreAuthorize("hasRole('A')")
+		void securedClassMethod() {
+		}
+
+		@PreAuthorize("hasRole('X')")
+		private void privateMethod() {
+		}
+
+		@PreAuthorize("hasRole('X')")
+		protected void protectedMethod() {
+		}
+
+		@PreAuthorize("hasRole('X')")
+		void publicCallsPrivate() {
+			privateMethod();
+		}
+
+	}
+
+	static class SecuredImplSubclass extends SecuredImpl {
+
+		@Override
+		protected void protectedMethod() {
+		}
+
+		@Override
+		public void publicCallsPrivate() {
+			super.publicCallsPrivate();
+		}
+
+	}
+
+	static class PrePostSecured {
+
+		@PreAuthorize("denyAll")
+		void denyAllMethod() {
+		}
+
+	}
+
+}

+ 66 - 0
aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreFilterAspectTests.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.security.access.prepost.PreFilter;
+import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public class PreFilterAspectTests {
+
+	private MethodInterceptor interceptor;
+
+	private PrePostSecured prePostSecured = new PrePostSecured();
+
+	@BeforeEach
+	public final void setUp() {
+		MockitoAnnotations.initMocks(this);
+		this.interceptor = new PreFilterAuthorizationMethodInterceptor();
+		PreFilterAspect secAspect = PreFilterAspect.aspectOf();
+		secAspect.setSecurityInterceptor(this.interceptor);
+	}
+
+	@Test
+	public void preFilterMethodWhenListThenFilters() {
+		List<String> objects = new ArrayList<>(Arrays.asList("apple", "banana", "aubergine", "orange"));
+		assertThat(this.prePostSecured.preFilterMethod(objects)).containsExactly("apple", "aubergine");
+	}
+
+	static class PrePostSecured {
+
+		@PreFilter("filterObject.startsWith('a')")
+		List<String> preFilterMethod(List<String> objects) {
+			return objects;
+		}
+
+	}
+
+}

+ 143 - 0
aspects/src/test/java/org/springframework/security/authorization/method/aspectj/SecuredAspectTests.java

@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.authorization.method.aspectj;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * @author Luke Taylor
+ * @since 3.0.3
+ */
+public class SecuredAspectTests {
+
+	private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
+
+	private MethodInterceptor interceptor;
+
+	private SecuredImpl secured = new SecuredImpl();
+
+	private SecuredImplSubclass securedSub = new SecuredImplSubclass();
+
+	@BeforeEach
+	public final void setUp() {
+		MockitoAnnotations.initMocks(this);
+		this.interceptor = AuthorizationManagerBeforeMethodInterceptor.secured();
+		SecuredAspect secAspect = SecuredAspect.aspectOf();
+		secAspect.setSecurityInterceptor(this.interceptor);
+	}
+
+	@AfterEach
+	public void clearContext() {
+		SecurityContextHolder.clearContext();
+	}
+
+	@Test
+	public void securedInterfaceMethodAllowsAllAccess() {
+		this.secured.securedMethod();
+	}
+
+	@Test
+	public void securedClassMethodDeniesUnauthenticatedAccess() {
+		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
+				.isThrownBy(() -> this.secured.securedClassMethod());
+	}
+
+	@Test
+	public void securedClassMethodAllowsAccessToRoleA() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		this.secured.securedClassMethod();
+	}
+
+	@Test
+	public void internalPrivateCallIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());
+	}
+
+	@Test
+	public void protectedMethodIsIntercepted() {
+		SecurityContextHolder.getContext().setAuthentication(this.anne);
+		assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());
+	}
+
+	@Test
+	public void overriddenProtectedMethodIsNotIntercepted() {
+		// AspectJ doesn't inherit annotations
+		this.securedSub.protectedMethod();
+	}
+
+	interface SecuredInterface {
+
+		@Secured("ROLE_X")
+		void securedMethod();
+
+	}
+
+	static class SecuredImpl implements SecuredInterface {
+
+		// Not really secured because AspectJ doesn't inherit annotations from interfaces
+		@Override
+		public void securedMethod() {
+		}
+
+		@Secured("ROLE_A")
+		void securedClassMethod() {
+		}
+
+		@Secured("ROLE_X")
+		private void privateMethod() {
+		}
+
+		@Secured("ROLE_X")
+		protected void protectedMethod() {
+		}
+
+		@Secured("ROLE_X")
+		void publicCallsPrivate() {
+			privateMethod();
+		}
+
+	}
+
+	static class SecuredImplSubclass extends SecuredImpl {
+
+		@Override
+		protected void protectedMethod() {
+		}
+
+		@Override
+		public void publicCallsPrivate() {
+			super.publicCallsPrivate();
+		}
+
+	}
+
+}

+ 78 - 0
config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAspectJAutoProxyRegistrar.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * 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
+ *
+ *      https://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 org.springframework.security.config.annotation.method.configuration;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.type.AnnotationMetadata;
+
+/**
+ * Registers an
+ * {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
+ * AnnotationAwareAspectJAutoProxyCreator} against the current
+ * {@link BeanDefinitionRegistry} as appropriate based on a given @
+ * {@link EnableMethodSecurity} annotation.
+ *
+ * <p>
+ * Note: This class is necessary because AspectJAutoProxyRegistrar only supports
+ * EnableAspectJAutoProxy.
+ * </p>
+ *
+ * @author Josh Cummings
+ * @since 5.8
+ */
+class MethodSecurityAspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
+
+	/**
+	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
+	 * of the @{@link EnableMethodSecurity#proxyTargetClass()} attribute on the importing
+	 * {@code @Configuration} class.
+	 */
+	@Override
+	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+		registerBeanDefinition("preFilterAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PreFilterAspect", "preFilterAspect$0",
+				registry);
+		registerBeanDefinition("postFilterAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PostFilterAspect", "postFilterAspect$0",
+				registry);
+		registerBeanDefinition("preAuthorizeAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PreAuthorizeAspect", "preAuthorizeAspect$0",
+				registry);
+		registerBeanDefinition("postAuthorizeAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PostAuthorizeAspect",
+				"postAuthorizeAspect$0", registry);
+		registerBeanDefinition("securedAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.SecuredAspect", "securedAspect$0", registry);
+	}
+
+	private void registerBeanDefinition(String beanName, String aspectClassName, String aspectBeanName,
+			BeanDefinitionRegistry registry) {
+		if (!registry.containsBeanDefinition(beanName)) {
+			return;
+		}
+		BeanDefinition interceptor = registry.getBeanDefinition(beanName);
+		BeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(aspectClassName);
+		aspect.setFactoryMethod("aspectOf");
+		aspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+		aspect.addPropertyValue("securityInterceptor", interceptor);
+		registry.registerBeanDefinition(aspectBeanName, aspect.getBeanDefinition());
+	}
+
+}

+ 7 - 1
config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -62,11 +62,17 @@ final class MethodSecuritySelector implements ImportSelector {
 
 		private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName() };
 
+		private static final String[] ASPECTJ_IMPORTS = new String[] {
+				MethodSecurityAspectJAutoProxyRegistrar.class.getName() };
+
 		@Override
 		protected String[] selectImports(@NonNull AdviceMode adviceMode) {
 			if (adviceMode == AdviceMode.PROXY) {
 				return IMPORTS;
 			}
+			if (adviceMode == AdviceMode.ASPECTJ) {
+				return ASPECTJ_IMPORTS;
+			}
 			throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
 		}
 

+ 55 - 12
config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java

@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.beans.factory.support.ManagedMap;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
@@ -78,6 +79,8 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
 
 	private static final String ATT_EXPRESSION = "expression";
 
+	private static final String ATT_MODE = "mode";
+
 	private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF = "security-context-holder-strategy-ref";
 
 	@Override
@@ -88,6 +91,7 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
 		BeanMetadataElement securityContextHolderStrategy = getSecurityContextHolderStrategy(element);
 		boolean prePostAnnotationsEnabled = !element.hasAttribute(ATT_USE_PREPOST)
 				|| "true".equals(element.getAttribute(ATT_USE_PREPOST));
+		boolean useAspectJ = "aspectj".equals(element.getAttribute(ATT_MODE));
 		if (prePostAnnotationsEnabled) {
 			BeanDefinitionBuilder preFilterInterceptor = BeanDefinitionBuilder
 					.rootBeanDefinition(PreFilterAuthorizationMethodInterceptor.class)
@@ -151,20 +155,29 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
 		}
 		Map<Pointcut, BeanMetadataElement> managers = new ManagedMap<>();
 		List<Element> methods = DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT);
-		if (!methods.isEmpty()) {
-			for (Element protectElt : methods) {
-				managers.put(pointcut(protectElt), authorizationManager(element, protectElt));
+		if (useAspectJ) {
+			if (!methods.isEmpty()) {
+				pc.getReaderContext().error("Cannot use <protect-pointcut> and mode='aspectj' together",
+						pc.extractSource(element));
 			}
-			BeanDefinitionBuilder protectPointcutInterceptor = BeanDefinitionBuilder
-					.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class)
-					.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
-					.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
-					.addConstructorArgValue(pointcut(managers.keySet()))
-					.addConstructorArgValue(authorizationManager(managers));
-			pc.getRegistry().registerBeanDefinition("protectPointcutInterceptor",
-					protectPointcutInterceptor.getBeanDefinition());
+			registerInterceptors(pc.getRegistry());
+		}
+		else {
+			if (!methods.isEmpty()) {
+				for (Element protectElt : methods) {
+					managers.put(pointcut(protectElt), authorizationManager(element, protectElt));
+				}
+				BeanDefinitionBuilder protectPointcutInterceptor = BeanDefinitionBuilder
+						.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class)
+						.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
+						.addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy)
+						.addConstructorArgValue(pointcut(managers.keySet()))
+						.addConstructorArgValue(authorizationManager(managers));
+				pc.getRegistry().registerBeanDefinition("protectPointcutInterceptor",
+						protectPointcutInterceptor.getBeanDefinition());
+			}
+			AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);
 		}
-		AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);
 		pc.popAndRegisterContainingComponent();
 		return null;
 	}
@@ -218,6 +231,36 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
 				.addConstructorArgValue(managers).getBeanDefinition();
 	}
 
+	private void registerInterceptors(BeanDefinitionRegistry registry) {
+		registerBeanDefinition("preFilterAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PreFilterAspect", "preFilterAspect$0",
+				registry);
+		registerBeanDefinition("postFilterAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PostFilterAspect", "postFilterAspect$0",
+				registry);
+		registerBeanDefinition("preAuthorizeAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PreAuthorizeAspect", "preAuthorizeAspect$0",
+				registry);
+		registerBeanDefinition("postAuthorizeAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.PostAuthorizeAspect",
+				"postAuthorizeAspect$0", registry);
+		registerBeanDefinition("securedAuthorizationMethodInterceptor",
+				"org.springframework.security.authorization.method.aspectj.SecuredAspect", "securedAspect$0", registry);
+	}
+
+	private void registerBeanDefinition(String beanName, String aspectClassName, String aspectBeanName,
+			BeanDefinitionRegistry registry) {
+		if (!registry.containsBeanDefinition(beanName)) {
+			return;
+		}
+		BeanDefinition interceptor = registry.getBeanDefinition(beanName);
+		BeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(aspectClassName);
+		aspect.setFactoryMethod("aspectOf");
+		aspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+		aspect.addPropertyValue("securityInterceptor", interceptor);
+		registry.registerBeanDefinition(aspectBeanName, aspect.getBeanDefinition());
+	}
+
 	public static final class MethodSecurityExpressionHandlerBean
 			implements FactoryBean<MethodSecurityExpressionHandler>, ApplicationContextAware {
 

+ 3 - 0
config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc

@@ -216,6 +216,9 @@ method-security.attlist &=
 method-security.attlist &=
 	## If true, class-based proxying will be used instead of interface-based proxying.
 	attribute proxy-target-class {xsd:boolean}?
+method-security.attlist &=
+	## If set to aspectj, then use AspectJ to intercept method invocation
+	attribute mode {"aspectj"}?
 method-security.attlist &=
 	## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy
 	attribute security-context-holder-strategy-ref {xsd:string}?

+ 11 - 0
config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd

@@ -677,6 +677,17 @@
                 </xs:documentation>
          </xs:annotation>
       </xs:attribute>
+      <xs:attribute name="mode">
+         <xs:annotation>
+            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation
+                </xs:documentation>
+         </xs:annotation>
+         <xs:simpleType>
+            <xs:restriction base="xs:token">
+               <xs:enumeration value="aspectj"/>
+            </xs:restriction>
+         </xs:simpleType>
+      </xs:attribute>
       <xs:attribute name="security-context-holder-strategy-ref" type="xs:string">
          <xs:annotation>
             <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based

+ 27 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java

@@ -32,6 +32,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
 import org.springframework.aop.support.JdkRegexpMethodPointcut;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.AdviceMode;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Role;
@@ -406,6 +407,17 @@ public class PrePostMethodSecurityConfigurationTests {
 		this.methodSecurityService.preAuthorizeBean(true);
 	}
 
+	@Test
+	public void configureWhenAspectJThenRegistersAspects() {
+		this.spring.register(AspectJMethodSecurityServiceConfig.class).autowire();
+		assertThat(this.spring.getContext().containsBean("preFilterAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("postFilterAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("preAuthorizeAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("postAuthorizeAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("securedAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("annotationSecurityAspect$0")).isFalse();
+	}
+
 	@Configuration
 	@EnableMethodSecurity
 	static class MethodSecurityServiceConfig {
@@ -556,4 +568,19 @@ public class PrePostMethodSecurityConfigurationTests {
 
 	}
 
+	@EnableMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true)
+	static class AspectJMethodSecurityServiceConfig {
+
+		@Bean
+		MethodSecurityService methodSecurityService() {
+			return new MethodSecurityServiceImpl();
+		}
+
+		@Bean
+		Authz authz() {
+			return new Authz();
+		}
+
+	}
+
 }

+ 11 - 0
config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java

@@ -85,6 +85,17 @@ public class MethodSecurityBeanDefinitionParserTests {
 				.withMessage("Access Denied");
 	}
 
+	@Test
+	public void configureWhenAspectJThenRegistersAspects() {
+		this.spring.configLocations(xml("AspectJMethodSecurityServiceEnabled")).autowire();
+		assertThat(this.spring.getContext().containsBean("preFilterAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("postFilterAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("preAuthorizeAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("postAuthorizeAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("securedAspect$0")).isTrue();
+		assertThat(this.spring.getContext().containsBean("annotationSecurityAspect$0")).isFalse();
+	}
+
 	@WithAnonymousUser
 	@Test
 	public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {

+ 28 - 0
config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-AspectJMethodSecurityServiceEnabled.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2002-2021 the original author or authors.
+  ~
+  ~ 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
+  ~
+  ~      https://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.
+  -->
+
+<b:beans xmlns:b="http://www.springframework.org/schema/beans"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xmlns="http://www.springframework.org/schema/security"
+		 xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
+		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<method-security mode="aspectj" secured-enabled="true" jsr250-enabled="true"/>
+
+	<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
+
+</b:beans>

+ 4 - 0
core/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJCallback.java

@@ -22,7 +22,11 @@ package org.springframework.security.access.intercept.aspectj;
  * simple <code>return proceed();</code> statement.
  *
  * @author Ben Alex
+ * @deprecated This class will be removed from the public API. Please either use
+ * `spring-security-aspects`, Spring Security's method security support or create your own
+ * class that uses Spring AOP annotations.
  */
+@Deprecated
 public interface AspectJCallback {
 
 	Object proceedWithObject();

+ 4 - 0
core/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJMethodSecurityInterceptor.java

@@ -33,7 +33,11 @@ import org.springframework.security.access.intercept.aopalliance.MethodSecurityI
  * @author Luke Taylor
  * @author Rob Winch
  * @since 3.0.3
+ * @deprecated This class will be removed from the public API. Please either use
+ * `spring-security-aspects`, Spring Security's method security support or create your own
+ * class that uses Spring AOP annotations.
  */
+@Deprecated
 public final class AspectJMethodSecurityInterceptor extends MethodSecurityInterceptor {
 
 	/**

+ 3 - 0
core/src/main/java/org/springframework/security/access/intercept/aspectj/MethodInvocationAdapter.java

@@ -32,7 +32,10 @@ import org.springframework.util.Assert;
  *
  * @author Luke Taylor
  * @since 3.0.3
+ * @deprecated This class will be removed from the public API. See
+ * `JoinPointMethodInvocation` in `spring-security-aspects` for its replacement
  */
+@Deprecated
 public final class MethodInvocationAdapter implements MethodInvocation {
 
 	private final ProceedingJoinPoint jp;

+ 4 - 0
docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc

@@ -23,6 +23,10 @@ Defaults to "false".
 Enables JSR-250 authorization annotations (@RolesAllowed, @PermitAll, @DenyAll) for this application context.
 Defaults to "false".
 
+[[nsa-method-security-mode]]
+* **mode**
+If set to "aspectj", then uses AspectJ to intercept method invocations.
+
 [[nsa-method-security-proxy-target-class]]
 * **proxy-target-class**
 If true, class based proxying will be used instead of interface based proxying.