diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
index 3cfba87101..2114fc32b5 100644
--- a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
+++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
@@ -19,9 +19,17 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
- * Wraps a delegate {@link Callable} with logic for setting up a {@link SecurityContext}
- * before invoking the delegate {@link Callable} and then removing the
- * {@link SecurityContext} after the delegate has completed.
+ *
+ * Wraps a delegate {@link Callable} with logic for setting up a
+ * {@link SecurityContext} before invoking the delegate {@link Callable} and
+ * then removing the {@link SecurityContext} after the delegate has completed.
+ *
+ *
+ * By default the {@link SecurityContext} is only setup if {@link #call()} is
+ * invoked on a separate {@link Thread} than the
+ * {@link DelegatingSecurityContextCallable} was created on. This can be
+ * overridden by setting {@link #setEnableOnOriginalThread(boolean)} to true.
+ *
*
* @author Rob Winch
* @since 3.2
@@ -32,6 +40,10 @@ public final class DelegatingSecurityContextCallable implements Callable {
private final SecurityContext securityContext;
+ private final Thread originalThread;
+
+ private boolean enableOnOriginalThread;
+
/**
* Creates a new {@link DelegatingSecurityContextCallable} with a specific
* {@link SecurityContext}.
@@ -46,6 +58,7 @@ public final class DelegatingSecurityContextCallable implements Callable {
Assert.notNull(securityContext, "securityContext cannot be null");
this.delegate = delegate;
this.securityContext = securityContext;
+ this.originalThread = Thread.currentThread();
}
/**
@@ -58,7 +71,27 @@ public final class DelegatingSecurityContextCallable implements Callable {
this(delegate, SecurityContextHolder.getContext());
}
+ /**
+ * Determines if the SecurityContext should be transfered if {@link #call()}
+ * is invoked on the same {@link Thread} the
+ * {@link DelegatingSecurityContextCallable} was created on.
+ *
+ * @param enableOnOriginalThread
+ * if false (default), will only transfer the
+ * {@link SecurityContext} if {@link #call()} is invoked on a
+ * different {@link Thread} than the
+ * {@link DelegatingSecurityContextCallable} was created on.
+ *
+ * @since 4.0.2
+ */
+ public void setEnableOnOriginalThread(boolean enableOnOriginalThread) {
+ this.enableOnOriginalThread = enableOnOriginalThread;
+ }
+
public V call() throws Exception {
+ if(!enableOnOriginalThread && originalThread == Thread.currentThread()) {
+ return delegate.call();
+ }
try {
SecurityContextHolder.setContext(securityContext);
return delegate.call();
diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java
index 6674c8c6ec..30f9d58f29 100644
--- a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java
+++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java
@@ -17,9 +17,17 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
+ *
* Wraps a delegate {@link Runnable} with logic for setting up a {@link SecurityContext}
* before invoking the delegate {@link Runnable} and then removing the
* {@link SecurityContext} after the delegate has completed.
+ *
+ *
+ * By default the {@link SecurityContext} is only setup if {@link #run()} is
+ * invoked on a separate {@link Thread} than the
+ * {@link DelegatingSecurityContextRunnable} was created on. This can be
+ * overridden by setting {@link #setEnableOnOriginalThread(boolean)} to true.
+ *
*
* @author Rob Winch
* @since 3.2
@@ -30,6 +38,10 @@ public final class DelegatingSecurityContextRunnable implements Runnable {
private final SecurityContext securityContext;
+ private final Thread originalThread;
+
+ private boolean enableOnOriginalThread;
+
/**
* Creates a new {@link DelegatingSecurityContextRunnable} with a specific
* {@link SecurityContext}.
@@ -44,6 +56,7 @@ public final class DelegatingSecurityContextRunnable implements Runnable {
Assert.notNull(securityContext, "securityContext cannot be null");
this.delegate = delegate;
this.securityContext = securityContext;
+ this.originalThread = Thread.currentThread();
}
/**
@@ -56,7 +69,27 @@ public final class DelegatingSecurityContextRunnable implements Runnable {
this(delegate, SecurityContextHolder.getContext());
}
+ /**
+ * Determines if the SecurityContext should be transfered if {@link #call()}
+ * is invoked on the same {@link Thread} the
+ * {@link DelegatingSecurityContextCallable} was created on.
+ *
+ * @param enableOnOriginalThread
+ * if false (default), will only transfer the
+ * {@link SecurityContext} if {@link #call()} is invoked on a
+ * different {@link Thread} than the
+ * {@link DelegatingSecurityContextCallable} was created on.
+ * @since 4.0.2
+ */
+ public void setEnableOnOriginalThread(boolean enableOnOriginalThread) {
+ this.enableOnOriginalThread = enableOnOriginalThread;
+ }
+
public void run() {
+ if(!enableOnOriginalThread && originalThread == Thread.currentThread()) {
+ delegate.run();
+ return;
+ }
try {
SecurityContextHolder.setContext(securityContext);
delegate.run();
diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java
index c3eeafaf92..d6644de83d 100644
--- a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java
+++ b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java
@@ -17,6 +17,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import org.junit.After;
import org.junit.Before;
@@ -45,6 +48,8 @@ public class DelegatingSecurityContextCallableTests {
private Callable