SEC-1262: Added new (replacement) AspectJ interceptor which wraps the JoinPoint in a MethodInvocation adapter to provide compatibility with classes which only support MethodInvocation instances.

Also deprecated the existing AspectJ interceptors. This will also allow future simplification of the AbstractMethodSecurityMetadataSource, as it no longer needs to support JoinPoints.
This commit is contained in:
Luke Taylor
2010-03-09 23:39:07 +00:00
parent 1b0ac9c785
commit 91153df78d
13 changed files with 315 additions and 56 deletions

View File

@@ -2,11 +2,12 @@ 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.prepost.*;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
/**
* Concrete AspectJ transaction aspect using Spring Security @Secured annotation
* Concrete AspectJ aspect using Spring Security @Secured annotation
* for JDK 1.5+.
*
* <p>
@@ -16,8 +17,8 @@ import org.springframework.security.access.intercept.aspectj.AspectJSecurityInte
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
*
* @author Mike Wiesner
* @since 1.0
* @version $Id$
* @author Luke Taylor
* @since 3.1
*/
public aspect AnnotationSecurityAspect implements InitializingBean {
@@ -34,11 +35,19 @@ public aspect AnnotationSecurityAspect implements InitializingBean {
private pointcut executionOfSecuredMethod() :
execution(* *(..)) && @annotation(Secured);
/**
* Matches the execution of any method with Pre/Post annotations.
*/
private pointcut executionOfPrePostAnnotatedMethod() :
execution(* *(..)) && (@annotation(PreAuthorize) || @annotation(PreFilter)
|| @annotation(PostAuthorize) || @annotation(PostFilter));
private pointcut securedMethodExecution() :
executionOfAnyPublicMethodInAtSecuredType() ||
executionOfSecuredMethod();
executionOfSecuredMethod() ||
executionOfPrePostAnnotatedMethod();
private AspectJSecurityInterceptor securityInterceptor;
private AspectJMethodSecurityInterceptor securityInterceptor;
Object around(): securedMethodExecution() {
if (this.securityInterceptor == null) {
@@ -54,13 +63,14 @@ public aspect AnnotationSecurityAspect implements InitializingBean {
return this.securityInterceptor.invoke(thisJoinPoint, callback);
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
public void setSecurityInterceptor(AspectJMethodSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
if (this.securityInterceptor == null) {
throw new IllegalArgumentException("securityInterceptor required");
}
}
}

View File

@@ -0,0 +1,110 @@
package org.springframework.security.access.intercept.aspectj.aspect;
import java.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
/**
*
* @author Luke Taylor
* @since 3.0.3
*/
public class AnnotationSecurityAspectTests {
private @Mock AccessDecisionManager adm;
private @Mock AuthenticationManager authman;
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
// private TestingAuthenticationToken bob = new TestingAuthenticationToken("bob", "", "ROLE_B");
private AspectJMethodSecurityInterceptor interceptor;
private SecuredImpl secured = new SecuredImpl();
private PrePostSecured prePostSecured = new PrePostSecured();
@Before
public final void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
interceptor = new AspectJMethodSecurityInterceptor();
interceptor.setAccessDecisionManager(adm);
interceptor.setAuthenticationManager(authman);
interceptor.setSecurityMetadataSource(new SecuredAnnotationSecurityMetadataSource());
AnnotationSecurityAspect secAspect = AnnotationSecurityAspect.aspectOf();
secAspect.setSecurityInterceptor(interceptor);
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
@Test
public void securedInterfaceMethodAllowsAllAccess() throws Exception {
secured.securedMethod();
}
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void securedClassMethodDeniesUnauthenticatedAccess() throws Exception {
secured.securedClassMethod();
}
@Test
public void securedClassMethodAllowsAccessToRoleA() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
secured.securedClassMethod();
}
// SEC-1262
@Test(expected=AccessDeniedException.class)
public void denyAllPreAuthorizeDeniesAccess() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
interceptor.setSecurityMetadataSource(new PrePostAnnotationSecurityMetadataSource(
new ExpressionBasedAnnotationAttributeFactory(new DefaultMethodSecurityExpressionHandler())));
AffirmativeBased adm = new AffirmativeBased();
AccessDecisionVoter[] voters = new AccessDecisionVoter[]
{new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice())};
adm.setDecisionVoters(Arrays.asList(voters));
interceptor.setAccessDecisionManager(adm);
prePostSecured.denyAllMethod();
}
}
interface SecuredInterface {
@Secured("ROLE_X")
void securedMethod();
}
class SecuredImpl implements SecuredInterface {
// Not really secured because AspectJ doesn't inherit annotations from interfaces
public void securedMethod() {
}
@Secured("ROLE_A")
public void securedClassMethod() {
}
}
class PrePostSecured {
@PreAuthorize("denyAll")
public void denyAllMethod() {
}
}