mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-27 12:48:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| [[concurrency]]
 | |
| = Concurrency Support
 | |
| 
 | |
| In most environments, Security is stored on a per-`Thread` basis.
 | |
| This means that when work is done on a new `Thread`, the `SecurityContext` is lost.
 | |
| Spring Security provides some infrastructure to help make this much easier to manage.
 | |
| Spring Security provides low-level abstractions for working with Spring Security in multi-threaded environments.
 | |
| In fact, this is what Spring Security builds on to integrate with xref:servlet/integrations/servlet-api.adoc#servletapi-start-runnable[`AsyncContext.start(Runnable)`] and xref:servlet/integrations/mvc.adoc#mvc-async[Spring MVC Async Integration].
 | |
| 
 | |
| == DelegatingSecurityContextRunnable
 | |
| 
 | |
| One of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`.
 | |
| It wraps a delegate `Runnable` to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate.
 | |
| It then invokes the delegate `Runnable`, ensuring to clear the `SecurityContextHolder` afterwards.
 | |
| The `DelegatingSecurityContextRunnable` looks something like this:
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| public void run() {
 | |
| try {
 | |
| 	SecurityContextHolder.setContext(securityContext);
 | |
| 	delegate.run();
 | |
| } finally {
 | |
| 	SecurityContextHolder.clearContext();
 | |
| }
 | |
| }
 | |
| ----
 | |
| 
 | |
| While very simple, it makes it seamless to transfer the `SecurityContext` from one `Thread` to another.
 | |
| This is important since, in most cases, the `SecurityContextHolder` acts on a per-`Thread` basis.
 | |
| For example, you might have used Spring Security's xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] support to secure one of your services.
 | |
| You can now transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.
 | |
| The following example show how you might do so:
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| Runnable originalRunnable = new Runnable() {
 | |
| public void run() {
 | |
| 	// invoke secured service
 | |
| }
 | |
| };
 | |
| 
 | |
| SecurityContext context = SecurityContextHolder.getContext();
 | |
| DelegatingSecurityContextRunnable wrappedRunnable =
 | |
| 	new DelegatingSecurityContextRunnable(originalRunnable, context);
 | |
| 
 | |
| new Thread(wrappedRunnable).start();
 | |
| ----
 | |
| 
 | |
| The preceding code:
 | |
| 
 | |
| * Creates a `Runnable` that invokes our secured service.
 | |
| Note that it is not aware of Spring Security.
 | |
| * Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`.
 | |
| * Uses the `DelegatingSecurityContextRunnable` to create a `Thread`.
 | |
| * Starts the `Thread` we created.
 | |
| 
 | |
| Since it is common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder`, there is a shortcut constructor for it.
 | |
| The following code has the same effect as the preceding code:
 | |
| 
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| Runnable originalRunnable = new Runnable() {
 | |
| public void run() {
 | |
| 	// invoke secured service
 | |
| }
 | |
| };
 | |
| 
 | |
| DelegatingSecurityContextRunnable wrappedRunnable =
 | |
| 	new DelegatingSecurityContextRunnable(originalRunnable);
 | |
| 
 | |
| new Thread(wrappedRunnable).start();
 | |
| ----
 | |
| 
 | |
| The code we have is simple to use, but it still requires knowledge that we are using Spring Security.
 | |
| In the next section we will take a look at how we can utilize `DelegatingSecurityContextExecutor` to hide the fact that we are using Spring Security.
 | |
| 
 | |
| == DelegatingSecurityContextExecutor
 | |
| 
 | |
| In the previous section, we found that it was easy to use the `DelegatingSecurityContextRunnable`, but it was not ideal since we had to be aware of Spring Security to use it.
 | |
| Now we look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.
 | |
| 
 | |
| The design of `DelegatingSecurityContextExecutor` is similar to that of `DelegatingSecurityContextRunnable`, except that it accepts a delegate `Executor` instead of a delegate `Runnable`.
 | |
| The following example shows how to use it:
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| SecurityContext context = SecurityContextHolder.createEmptyContext();
 | |
| Authentication authentication =
 | |
| 	UsernamePasswordAuthenticationToken.authenticated("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
 | |
| context.setAuthentication(authentication);
 | |
| 
 | |
| SimpleAsyncTaskExecutor delegateExecutor =
 | |
| 	new SimpleAsyncTaskExecutor();
 | |
| DelegatingSecurityContextExecutor executor =
 | |
| 	new DelegatingSecurityContextExecutor(delegateExecutor, context);
 | |
| 
 | |
| Runnable originalRunnable = new Runnable() {
 | |
| public void run() {
 | |
| 	// invoke secured service
 | |
| }
 | |
| };
 | |
| 
 | |
| executor.execute(originalRunnable);
 | |
| ----
 | |
| 
 | |
| This code:
 | |
| 
 | |
| Note that, in this example, we create the `SecurityContext` by hand.
 | |
| However, it does not matter where or how we get the `SecurityContext` (for example, we could obtain it from the `SecurityContextHolder`).
 | |
| * Creates a `delegateExecutor` that is in charge of executing submitted `Runnable` objects.
 | |
| * Finally, we create a `DelegatingSecurityContextExecutor`, which is in charge of wrapping any `Runnable` that is passed into the `execute` method with a `DelegatingSecurityContextRunnable`.
 | |
| It then passes the wrapped `Runnable` to the `delegateExecutor`.
 | |
| In this case, the same `SecurityContext` is used for every `Runnable` submitted to our `DelegatingSecurityContextExecutor`.
 | |
| This is nice if we run background tasks that need to be run by a user with elevated privileges.
 | |
| * At this point, you may ask yourself, "`How does this shield my code of any knowledge of Spring Security?`" Instead of creating the `SecurityContext` and the `DelegatingSecurityContextExecutor` in our own code, we can inject an already initialized instance of `DelegatingSecurityContextExecutor`.
 | |
| 
 | |
| Consider the following example:
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| @Autowired
 | |
| private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor
 | |
| 
 | |
| public void submitRunnable() {
 | |
| Runnable originalRunnable = new Runnable() {
 | |
| 	public void run() {
 | |
| 	// invoke secured service
 | |
| 	}
 | |
| };
 | |
| executor.execute(originalRunnable);
 | |
| }
 | |
| ----
 | |
| 
 | |
| Now our code is unaware that the `SecurityContext` is being propagated to the `Thread`, the `originalRunnable` is run, and the `SecurityContextHolder` is cleared out.
 | |
| In this example, the same user is being used to run each thread.
 | |
| What if we wanted to use the user from `SecurityContextHolder` (that is, the currently logged in-user) at the time we invoked `executor.execute(Runnable)` to process `originalRunnable`?
 | |
| You can do so by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor:
 | |
| 
 | |
| [source,java]
 | |
| ----
 | |
| SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
 | |
| DelegatingSecurityContextExecutor executor =
 | |
| 	new DelegatingSecurityContextExecutor(delegateExecutor);
 | |
| ----
 | |
| 
 | |
| Now, any time `executor.execute(Runnable)` is run, the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`.
 | |
| This means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.
 | |
| 
 | |
| == Spring Security Concurrency Classes
 | |
| 
 | |
| See the {security-api-url}index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
 | |
| They are self-explanatory once you understand the previous code.
 | |
| 
 | |
| * javadoc:org.springframework.security.concurrent.DelegatingSecurityContextCallable[]
 | |
| * javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutor[]
 | |
| * javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutorService[]
 | |
| * javadoc:org.springframework.security.concurrent.DelegatingSecurityContextRunnable[]
 | |
| * javadoc:org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService[]
 | |
| * javadoc:org.springframework.security.scheduling.DelegatingSecurityContextSchedulingTaskExecutor[]
 | |
| * javadoc:org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor[]
 | |
| * javadoc:org.springframework.security.task.DelegatingSecurityContextTaskExecutor[]
 | |
| * javadoc:org.springframework.security.scheduling.DelegatingSecurityContextTaskScheduler[]
 |