mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-30 15:52:15 +00:00
SEC-1387: Support serialization of security advised beans.
MethodSecurityMetadataSourceAdvisor now takes the SecurityMetadataSource bean name as an extra constructor argument and re-obtains the bean from the BeanFactory in its readObject method. Beans that are advised using <global-method-security> should therefore now be serializable.
This commit is contained in:
parent
14ae36ac3b
commit
10dc72b017
@ -324,6 +324,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
|
|||||||
advisor.setSource(source);
|
advisor.setSource(source);
|
||||||
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor.getBeanName());
|
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor.getBeanName());
|
||||||
advisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource);
|
advisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource);
|
||||||
|
advisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource.getBeanName());
|
||||||
|
|
||||||
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR, advisor);
|
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR, advisor);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
package org.springframework.security.config.method;
|
package org.springframework.security.config.method;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.access.annotation.BusinessService;
|
import org.springframework.security.access.annotation.BusinessService;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.config.ConfigTestUtils;
|
import org.springframework.security.config.ConfigTestUtils;
|
||||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||||
@ -60,4 +67,39 @@ public class SecuredAnnotationDrivenBeanDefinitionParserTests {
|
|||||||
|
|
||||||
target.someAdminMethod();
|
target.someAdminMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SEC-1387
|
||||||
|
@Test(expected=AuthenticationCredentialsNotFoundException.class)
|
||||||
|
public void targetIsSerializableBeforeUse() throws Exception {
|
||||||
|
BusinessService chompedTarget = (BusinessService) serializeAndDeserialize(target);
|
||||||
|
chompedTarget.someAdminMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=AccessDeniedException.class)
|
||||||
|
public void targetIsSerializableAfterUse() throws Exception {
|
||||||
|
try {
|
||||||
|
target.someAdminMethod();
|
||||||
|
} catch (AuthenticationCredentialsNotFoundException expected) {
|
||||||
|
}
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("u","p","ROLE_A"));
|
||||||
|
|
||||||
|
BusinessService chompedTarget = (BusinessService) serializeAndDeserialize(target);
|
||||||
|
chompedTarget.someAdminMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object serializeAndDeserialize(Object o) throws IOException, ClassNotFoundException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
|
oos.writeObject(o);
|
||||||
|
oos.flush();
|
||||||
|
baos.flush();
|
||||||
|
byte[] bytes = baos.toByteArray();
|
||||||
|
|
||||||
|
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(is);
|
||||||
|
Object o2 = ois.readObject();
|
||||||
|
|
||||||
|
return o2;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.access.intercept.aopalliance;
|
package org.springframework.security.access.intercept.aopalliance;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.AccessibleObject;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ import org.springframework.util.Assert;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Advisor driven by a {@link MethodSecurityMetadataSource}, used to exclude a {@link MethodSecurityInterceptor} from
|
* Advisor driven by a {@link MethodSecurityMetadataSource}, used to exclude a {@link MethodSecurityInterceptor} from
|
||||||
* public (ie non-secure) methods.
|
* public (non-secure) methods.
|
||||||
* <p>
|
* <p>
|
||||||
* Because the AOP framework caches advice calculations, this is normally faster than just letting the
|
* Because the AOP framework caches advice calculations, this is normally faster than just letting the
|
||||||
* <code>MethodSecurityInterceptor</code> run and find out itself that it has no work to do.
|
* <code>MethodSecurityInterceptor</code> run and find out itself that it has no work to do.
|
||||||
@ -44,23 +47,25 @@ import org.springframework.util.Assert;
|
|||||||
* Based on Spring's TransactionAttributeSourceAdvisor.
|
* Based on Spring's TransactionAttributeSourceAdvisor.
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
* @author Luke Taylor
|
||||||
*/
|
*/
|
||||||
public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private MethodSecurityMetadataSource attributeSource;
|
private transient MethodSecurityMetadataSource attributeSource;
|
||||||
private MethodSecurityInterceptor interceptor;
|
private transient MethodSecurityInterceptor interceptor;
|
||||||
private Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();
|
private final Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
private String adviceBeanName;
|
private String adviceBeanName;
|
||||||
private final Object adviceMonitor = new Object();
|
private String metadataSourceBeanName;
|
||||||
|
private final Serializable adviceMonitor = new Serializable() {};
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use the decoupled approach instead
|
* @deprecated use the decoupled approach instead
|
||||||
*/
|
*/
|
||||||
public MethodSecurityMetadataSourceAdvisor(MethodSecurityInterceptor advice) {
|
MethodSecurityMetadataSourceAdvisor(MethodSecurityInterceptor advice) {
|
||||||
Assert.notNull(advice.getSecurityMetadataSource(), "Cannot construct a MethodSecurityMetadataSourceAdvisor using a " +
|
Assert.notNull(advice.getSecurityMetadataSource(), "Cannot construct a MethodSecurityMetadataSourceAdvisor using a " +
|
||||||
"MethodSecurityInterceptor that has no SecurityMetadataSource configured");
|
"MethodSecurityInterceptor that has no SecurityMetadataSource configured");
|
||||||
|
|
||||||
@ -71,21 +76,22 @@ public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor
|
|||||||
/**
|
/**
|
||||||
* Alternative constructor for situations where we want the advisor decoupled from the advice. Instead the advice
|
* Alternative constructor for situations where we want the advisor decoupled from the advice. Instead the advice
|
||||||
* bean name should be set. This prevents eager instantiation of the interceptor
|
* bean name should be set. This prevents eager instantiation of the interceptor
|
||||||
* (and hence the AuthenticationManager). See SEC-773, for example.
|
* (and hence the AuthenticationManager). See SEC-773, for example. The metadataSourceBeanName is used rather than
|
||||||
* <p>
|
* a direct reference to support serialization via a bean factory lookup.
|
||||||
* This is essentially the approach taken by subclasses of Spring's {@code AbstractBeanFactoryPointcutAdvisor},
|
|
||||||
* which this class should extend in future. The original hierarchy and constructor have been retained for backwards
|
|
||||||
* compatibility.
|
|
||||||
*
|
*
|
||||||
* @param adviceBeanName name of the MethodSecurityInterceptor bean
|
* @param adviceBeanName name of the MethodSecurityInterceptor bean
|
||||||
* @param attributeSource the attribute source (should be the same as the one used on the interceptor)
|
* @param attributeSource the SecurityMetadataSource (should be the same as the one used on the interceptor)
|
||||||
|
* @param attributeSourceBeanName the bean name of the attributeSource (required for serialization)
|
||||||
*/
|
*/
|
||||||
public MethodSecurityMetadataSourceAdvisor(String adviceBeanName, MethodSecurityMetadataSource attributeSource) {
|
public MethodSecurityMetadataSourceAdvisor(String adviceBeanName, MethodSecurityMetadataSource attributeSource,
|
||||||
|
String attributeSourceBeanName) {
|
||||||
Assert.notNull(adviceBeanName, "The adviceBeanName cannot be null");
|
Assert.notNull(adviceBeanName, "The adviceBeanName cannot be null");
|
||||||
Assert.notNull(attributeSource, "The attributeSource cannot be null");
|
Assert.notNull(attributeSource, "The attributeSource cannot be null");
|
||||||
|
Assert.notNull(attributeSourceBeanName, "The attributeSourceBeanName cannot be null");
|
||||||
|
|
||||||
this.adviceBeanName = adviceBeanName;
|
this.adviceBeanName = adviceBeanName;
|
||||||
this.attributeSource = attributeSource;
|
this.attributeSource = attributeSource;
|
||||||
|
this.metadataSourceBeanName = attributeSourceBeanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
@ -99,8 +105,7 @@ public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor
|
|||||||
if (interceptor == null) {
|
if (interceptor == null) {
|
||||||
Assert.notNull(adviceBeanName, "'adviceBeanName' must be set for use with bean factory lookup.");
|
Assert.notNull(adviceBeanName, "'adviceBeanName' must be set for use with bean factory lookup.");
|
||||||
Assert.state(beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
|
Assert.state(beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
|
||||||
interceptor = (MethodSecurityInterceptor)
|
interceptor = beanFactory.getBean(this.adviceBeanName, MethodSecurityInterceptor.class);
|
||||||
beanFactory.getBean(this.adviceBeanName, MethodSecurityInterceptor.class);
|
|
||||||
}
|
}
|
||||||
return interceptor;
|
return interceptor;
|
||||||
}
|
}
|
||||||
@ -110,9 +115,15 @@ public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor
|
|||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||||
|
ois.defaultReadObject();
|
||||||
|
|
||||||
|
attributeSource = beanFactory.getBean(metadataSourceBeanName, MethodSecurityMetadataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
//~ Inner Classes ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
||||||
class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut {
|
class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public boolean matches(Method m, Class targetClass) {
|
public boolean matches(Method m, Class targetClass) {
|
||||||
return attributeSource.getAttributes(m, targetClass) != null;
|
return attributeSource.getAttributes(m, targetClass) != null;
|
||||||
|
@ -15,19 +15,19 @@
|
|||||||
|
|
||||||
package org.springframework.security.access.annotation;
|
package org.springframework.security.access.annotation;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.security.RolesAllowed;
|
|
||||||
import javax.annotation.security.PermitAll;
|
import javax.annotation.security.PermitAll;
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
import org.springframework.security.access.annotation.Secured;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@Secured({"ROLE_USER"})
|
@Secured({"ROLE_USER"})
|
||||||
@PermitAll
|
@PermitAll
|
||||||
public interface BusinessService {
|
public interface BusinessService extends Serializable {
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
@Secured({"ROLE_ADMIN"})
|
@Secured({"ROLE_ADMIN"})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user