mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-14 06:13:30 +00:00
SEC-1741: Modify ContextPropagatingRemoteInvocation to pass a simple combination of principal/credentials as Strings, rather than serializing the whole SecurityContext object from the client.
This commit is contained in:
parent
28e70db8f2
commit
ba719dc0e1
@ -15,32 +15,30 @@
|
|||||||
|
|
||||||
package org.springframework.security.remoting.rmi;
|
package org.springframework.security.remoting.rmi;
|
||||||
|
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.remoting.support.RemoteInvocation;
|
import org.springframework.remoting.support.RemoteInvocation;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual <code>RemoteInvocation</code> that is passed from the client to the server, which contains the
|
* The actual {@code RemoteInvocation} that is passed from the client to the server.
|
||||||
* contents of {@link SecurityContextHolder}, being a {@link SecurityContext} object.
|
|
||||||
* <p>
|
* <p>
|
||||||
* When constructed on the client via {@link ContextPropagatingRemoteInvocationFactory}, the contents of the
|
* The principal and credentials information will be extracted from the current
|
||||||
* <code>SecurityContext</code> are stored inside the object. The object is then passed to the server that is
|
* security context and passed to the server as part of the invocation object.
|
||||||
* processing the remote invocation. Upon the server invoking the remote invocation, it will retrieve the passed
|
* <p>
|
||||||
* contents of the <code>SecurityContextHolder</code> and set them on the server-side
|
* To avoid potential serialization-based attacks, this implementation interprets the values as {@code String}s
|
||||||
* <code>SecurityContextHolder</code> while the target object is invoked. When the target invocation has been
|
* and creates a {@code UsernamePasswordAuthenticationToken} on the server side to hold them. If a different
|
||||||
* completed, the security context will be cleared using a call to {@link SecurityContextHolder#clearContext()}.
|
* token type is required you can override the {@code createAuthenticationRequest} method.
|
||||||
*
|
*
|
||||||
* @author James Monaghan
|
* @author James Monaghan
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
* @author Luke Taylor
|
||||||
*/
|
*/
|
||||||
public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
|
public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
|
||||||
//~ Static fields/initializers =====================================================================================
|
//~ Static fields/initializers =====================================================================================
|
||||||
@ -49,34 +47,40 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
|
|||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private SecurityContext securityContext;
|
private final String principal;
|
||||||
|
private final String credentials;
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the object, storing the value of the client-side
|
* Constructs the object, storing the principal and credentials extracted from the client-side
|
||||||
* <code>SecurityContextHolder</code> inside the object.
|
* security context.
|
||||||
*
|
*
|
||||||
* @param methodInvocation the method to invoke
|
* @param methodInvocation the method to invoke
|
||||||
*/
|
*/
|
||||||
public ContextPropagatingRemoteInvocation(MethodInvocation methodInvocation) {
|
public ContextPropagatingRemoteInvocation(MethodInvocation methodInvocation) {
|
||||||
super(methodInvocation);
|
super(methodInvocation);
|
||||||
securityContext = SecurityContextHolder.getContext();
|
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
if (currentUser != null) {
|
||||||
|
principal = currentUser.getPrincipal().toString();
|
||||||
|
credentials = currentUser.getCredentials().toString();
|
||||||
|
} else {
|
||||||
|
principal = credentials = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("RemoteInvocation now has SecurityContext: " + securityContext);
|
logger.debug("RemoteInvocation now has principal: " + principal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked on the server-side as described in the class JavaDocs.
|
* Invoked on the server-side.
|
||||||
* <p>
|
* <p>
|
||||||
* Invocations will always have their {@link org.springframework.security.core.Authentication#setAuthenticated(boolean)}
|
* The transmitted principal and credentials will be used to create an unauthenticated {@code Authentication}
|
||||||
* set to <code>false</code>, which is guaranteed to always be accepted by <code>Authentication</code>
|
* instance for processing by the {@code AuthenticationManager}.
|
||||||
* implementations. This ensures that even remotely authenticated <code>Authentication</code>s will be untrusted by
|
|
||||||
* the server-side, which is an appropriate security measure.
|
|
||||||
*
|
*
|
||||||
* @param targetObject the target object to apply the invocation to
|
* @param targetObject the target object to apply the invocation to
|
||||||
*
|
*
|
||||||
@ -88,15 +92,15 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
|
|||||||
*/
|
*/
|
||||||
public Object invoke(Object targetObject)
|
public Object invoke(Object targetObject)
|
||||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
SecurityContextHolder.setContext(securityContext);
|
|
||||||
|
|
||||||
if ((SecurityContextHolder.getContext() != null)
|
if (principal != null) {
|
||||||
&& (SecurityContextHolder.getContext().getAuthentication() != null)) {
|
Authentication request = createAuthenticationRequest(principal, credentials);
|
||||||
SecurityContextHolder.getContext().getAuthentication().setAuthenticated(false);
|
request.setAuthenticated(false);
|
||||||
}
|
SecurityContextHolder.getContext().setAuthentication(request);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Set SecurityContextHolder to contain: " + securityContext);
|
logger.debug("Set SecurityContextHolder to contain: " + request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -109,4 +113,11 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the server-side authentication request object.
|
||||||
|
*/
|
||||||
|
protected Authentication createAuthenticationRequest(String principal, String credentials) {
|
||||||
|
return new UsernamePasswordAuthenticationToken(principal, credentials);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,20 +16,13 @@
|
|||||||
package org.springframework.security.remoting.rmi;
|
package org.springframework.security.remoting.rmi;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.springframework.security.TargetObject;
|
import org.springframework.security.TargetObject;
|
||||||
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
import org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocation;
|
|
||||||
import org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocationFactory;
|
|
||||||
|
|
||||||
import org.springframework.security.util.SimpleMethodInvocation;
|
import org.springframework.security.util.SimpleMethodInvocation;
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
|
||||||
@ -57,8 +50,7 @@ public class ContextPropagatingRemoteInvocationTests extends TestCase {
|
|||||||
return (ContextPropagatingRemoteInvocation) factory.createRemoteInvocation(mi);
|
return (ContextPropagatingRemoteInvocation) factory.createRemoteInvocation(mi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testContextIsResetEvenIfExceptionOccurs()
|
public void testContextIsResetEvenIfExceptionOccurs() throws Exception {
|
||||||
throws Exception {
|
|
||||||
// Setup client-side context
|
// Setup client-side context
|
||||||
Authentication clientSideAuthentication = new UsernamePasswordAuthenticationToken("rod", "koala");
|
Authentication clientSideAuthentication = new UsernamePasswordAuthenticationToken("rod", "koala");
|
||||||
SecurityContextHolder.getContext().setAuthentication(clientSideAuthentication);
|
SecurityContextHolder.getContext().setAuthentication(clientSideAuthentication);
|
||||||
@ -96,10 +88,10 @@ public class ContextPropagatingRemoteInvocationTests extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNullContextHolderDoesNotCauseInvocationProblems() throws Exception {
|
public void testNullContextHolderDoesNotCauseInvocationProblems() throws Exception {
|
||||||
SecurityContextHolder.getContext().setAuthentication(null); // just to be explicit
|
SecurityContextHolder.clearContext(); // just to be explicit
|
||||||
|
|
||||||
ContextPropagatingRemoteInvocation remoteInvocation = getRemoteInvocation();
|
ContextPropagatingRemoteInvocation remoteInvocation = getRemoteInvocation();
|
||||||
SecurityContextHolder.getContext().setAuthentication(null); // unnecessary, but for explicitness
|
SecurityContextHolder.clearContext(); // unnecessary, but for explicitness
|
||||||
|
|
||||||
assertEquals("some_string Authentication empty", remoteInvocation.invoke(new TargetObject()));
|
assertEquals("some_string Authentication empty", remoteInvocation.invoke(new TargetObject()));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user