concurrency.adoc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. [[concurrency]]
  2. = Concurrency Support
  3. In most environments, Security is stored on a per-`Thread` basis.
  4. This means that when work is done on a new `Thread`, the `SecurityContext` is lost.
  5. Spring Security provides some infrastructure to help make this much easier to manage.
  6. Spring Security provides low-level abstractions for working with Spring Security in multi-threaded environments.
  7. 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].
  8. == DelegatingSecurityContextRunnable
  9. One of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`.
  10. It wraps a delegate `Runnable` to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate.
  11. It then invokes the delegate `Runnable`, ensuring to clear the `SecurityContextHolder` afterwards.
  12. The `DelegatingSecurityContextRunnable` looks something like this:
  13. ====
  14. [source,java]
  15. ----
  16. public void run() {
  17. try {
  18. SecurityContextHolder.setContext(securityContext);
  19. delegate.run();
  20. } finally {
  21. SecurityContextHolder.clearContext();
  22. }
  23. }
  24. ----
  25. ====
  26. While very simple, it makes it seamless to transfer the `SecurityContext` from one `Thread` to another.
  27. This is important since, in most cases, the `SecurityContextHolder` acts on a per-`Thread` basis.
  28. 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.
  29. You can now transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.
  30. The following example show how you might do so:
  31. ====
  32. [source,java]
  33. ----
  34. Runnable originalRunnable = new Runnable() {
  35. public void run() {
  36. // invoke secured service
  37. }
  38. };
  39. SecurityContext context = SecurityContextHolder.getContext();
  40. DelegatingSecurityContextRunnable wrappedRunnable =
  41. new DelegatingSecurityContextRunnable(originalRunnable, context);
  42. new Thread(wrappedRunnable).start();
  43. ----
  44. ====
  45. The preceding code:
  46. * Creates a `Runnable` that invokes our secured service.
  47. Note that it is not aware of Spring Security.
  48. * Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`.
  49. * Uses the `DelegatingSecurityContextRunnable` to create a `Thread`.
  50. * Starts the `Thread` we created.
  51. Since it is common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder`, there is a shortcut constructor for it.
  52. The following code has the same effect as the preceding code:
  53. ====
  54. [source,java]
  55. ----
  56. Runnable originalRunnable = new Runnable() {
  57. public void run() {
  58. // invoke secured service
  59. }
  60. };
  61. DelegatingSecurityContextRunnable wrappedRunnable =
  62. new DelegatingSecurityContextRunnable(originalRunnable);
  63. new Thread(wrappedRunnable).start();
  64. ----
  65. ====
  66. The code we have is simple to use, but it still requires knowledge that we are using Spring Security.
  67. 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.
  68. == DelegatingSecurityContextExecutor
  69. 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.
  70. Now we look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.
  71. The design of `DelegatingSecurityContextExecutor` is similar to that of `DelegatingSecurityContextRunnable`, except that it accepts a delegate `Executor` instead of a delegate `Runnable`.
  72. The following example shows how to use it:
  73. ====
  74. [source,java]
  75. ----
  76. SecurityContext context = SecurityContextHolder.createEmptyContext();
  77. Authentication authentication =
  78. UsernamePasswordAuthenticationToken.authenticated("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
  79. context.setAuthentication(authentication);
  80. SimpleAsyncTaskExecutor delegateExecutor =
  81. new SimpleAsyncTaskExecutor();
  82. DelegatingSecurityContextExecutor executor =
  83. new DelegatingSecurityContextExecutor(delegateExecutor, context);
  84. Runnable originalRunnable = new Runnable() {
  85. public void run() {
  86. // invoke secured service
  87. }
  88. };
  89. executor.execute(originalRunnable);
  90. ----
  91. ====
  92. This code:
  93. Note that, in this example, we create the `SecurityContext` by hand.
  94. However, it does not matter where or how we get the `SecurityContext` (for example, we could obtain it from the `SecurityContextHolder`).
  95. * Creates a `delegateExecutor` that is in charge of executing submitted `Runnable` objects.
  96. * Finally, we create a `DelegatingSecurityContextExecutor`, which is in charge of wrapping any `Runnable` that is passed into the `execute` method with a `DelegatingSecurityContextRunnable`.
  97. It then passes the wrapped `Runnable` to the `delegateExecutor`.
  98. In this case, the same `SecurityContext` is used for every `Runnable` submitted to our `DelegatingSecurityContextExecutor`.
  99. This is nice if we run background tasks that need to be run by a user with elevated privileges.
  100. * 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`.
  101. Consider the following example:
  102. ====
  103. [source,java]
  104. ----
  105. @Autowired
  106. private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor
  107. public void submitRunnable() {
  108. Runnable originalRunnable = new Runnable() {
  109. public void run() {
  110. // invoke secured service
  111. }
  112. };
  113. executor.execute(originalRunnable);
  114. }
  115. ----
  116. ====
  117. Now our code is unaware that the `SecurityContext` is being propagated to the `Thread`, the `originalRunnable` is run, and the `SecurityContextHolder` is cleared out.
  118. In this example, the same user is being used to run each thread.
  119. 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`?
  120. You can do so by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor:
  121. ====
  122. [source,java]
  123. ----
  124. SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
  125. DelegatingSecurityContextExecutor executor =
  126. new DelegatingSecurityContextExecutor(delegateExecutor);
  127. ----
  128. ====
  129. 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`.
  130. This means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.
  131. == Spring Security Concurrency Classes
  132. See the {security-api-url}index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
  133. They are self-explanatory once you understand the previous code.
  134. * {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextCallable.html[`DelegatingSecurityContextCallable`]
  135. * {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextExecutor.html[`DelegatingSecurityContextExecutor`]
  136. * {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.html[`DelegatingSecurityContextExecutorService`]
  137. * {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextRunnable.html[`DelegatingSecurityContextRunnable`]
  138. * {security-api-url}org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.html[`DelegatingSecurityContextScheduledExecutorService`]
  139. * {security-api-url}org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.html[`DelegatingSecurityContextSchedulingTaskExecutor`]
  140. * {security-api-url}org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.html[`DelegatingSecurityContextAsyncTaskExecutor`]
  141. * {security-api-url}org/springframework/security/task/DelegatingSecurityContextTaskExecutor.html[`DelegatingSecurityContextTaskExecutor`]
  142. * {security-api-url}org/springframework/security/scheduling/DelegatingSecurityContextTaskScheduler.html[`DelegatingSecurityContextTaskScheduler`]