concurrency.adoc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 for users.
  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 integration 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` in order 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. [source,java]
  14. ----
  15. public void run() {
  16. try {
  17. SecurityContextHolder.setContext(securityContext);
  18. delegate.run();
  19. } finally {
  20. SecurityContextHolder.clearContext();
  21. }
  22. }
  23. ----
  24. While very simple, it makes it seamless to transfer the SecurityContext from one Thread to another.
  25. This is important since, in most cases, the SecurityContextHolder acts on a per Thread basis.
  26. 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.
  27. You can now easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.
  28. An example of how you might do this can be found below:
  29. [source,java]
  30. ----
  31. Runnable originalRunnable = new Runnable() {
  32. public void run() {
  33. // invoke secured service
  34. }
  35. };
  36. SecurityContext context = SecurityContextHolder.getContext();
  37. DelegatingSecurityContextRunnable wrappedRunnable =
  38. new DelegatingSecurityContextRunnable(originalRunnable, context);
  39. new Thread(wrappedRunnable).start();
  40. ----
  41. The code above performs the following steps:
  42. * Creates a `Runnable` that will be invoking our secured service.
  43. Notice that it is not aware of Spring Security
  44. * Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`
  45. * Use the `DelegatingSecurityContextRunnable` to create a Thread
  46. * Start the Thread we created
  47. Since it is quite common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder` there is a shortcut constructor for it.
  48. The following code is the same as the code above:
  49. [source,java]
  50. ----
  51. Runnable originalRunnable = new Runnable() {
  52. public void run() {
  53. // invoke secured service
  54. }
  55. };
  56. DelegatingSecurityContextRunnable wrappedRunnable =
  57. new DelegatingSecurityContextRunnable(originalRunnable);
  58. new Thread(wrappedRunnable).start();
  59. ----
  60. The code we have is simple to use, but it still requires knowledge that we are using Spring Security.
  61. 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.
  62. == DelegatingSecurityContextExecutor
  63. 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 in order to use it.
  64. Let's take a look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.
  65. The design of `DelegatingSecurityContextExecutor` is very similar to that of `DelegatingSecurityContextRunnable` except it accepts a delegate `Executor` instead of a delegate `Runnable`.
  66. You can see an example of how it might be used below:
  67. [source,java]
  68. ----
  69. SecurityContext context = SecurityContextHolder.createEmptyContext();
  70. Authentication authentication =
  71. UsernamePasswordAuthenticationToken.authenticated("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
  72. context.setAuthentication(authentication);
  73. SimpleAsyncTaskExecutor delegateExecutor =
  74. new SimpleAsyncTaskExecutor();
  75. DelegatingSecurityContextExecutor executor =
  76. new DelegatingSecurityContextExecutor(delegateExecutor, context);
  77. Runnable originalRunnable = new Runnable() {
  78. public void run() {
  79. // invoke secured service
  80. }
  81. };
  82. executor.execute(originalRunnable);
  83. ----
  84. The code performs the following steps:
  85. * Creates the `SecurityContext` to be used for our `DelegatingSecurityContextExecutor`.
  86. Note that in this example we simply create the `SecurityContext` by hand.
  87. However, it does not matter where or how we get the `SecurityContext` (i.e. we could obtain it from the `SecurityContextHolder` if we wanted).
  88. * Creates a delegateExecutor that is in charge of executing submitted ``Runnable``s
  89. * Finally we create a `DelegatingSecurityContextExecutor` which is in charge of wrapping any Runnable that is passed into the execute method with a `DelegatingSecurityContextRunnable`.
  90. It then passes the wrapped Runnable to the delegateExecutor.
  91. In this instance, the same `SecurityContext` will be used for every Runnable submitted to our `DelegatingSecurityContextExecutor`.
  92. This is nice if we are running background tasks that need to be run by a user with elevated privileges.
  93. * At this point you may be asking 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`.
  94. [source,java]
  95. ----
  96. @Autowired
  97. private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor
  98. public void submitRunnable() {
  99. Runnable originalRunnable = new Runnable() {
  100. public void run() {
  101. // invoke secured service
  102. }
  103. };
  104. executor.execute(originalRunnable);
  105. }
  106. ----
  107. Now our code is unaware that the `SecurityContext` is being propagated to the `Thread`, then the `originalRunnable` is run, and then the `SecurityContextHolder` is cleared out.
  108. In this example, the same user is being used to run each thread.
  109. What if we wanted to use the user from `SecurityContextHolder` at the time we invoked `executor.execute(Runnable)` (i.e. the currently logged in user) to process ``originalRunnable``?
  110. This can be done by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor.
  111. For example:
  112. [source,java]
  113. ----
  114. SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
  115. DelegatingSecurityContextExecutor executor =
  116. new DelegatingSecurityContextExecutor(delegateExecutor);
  117. ----
  118. Now anytime `executor.execute(Runnable)` is executed the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`.
  119. This means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.
  120. == Spring Security Concurrency Classes
  121. Refer to the Javadoc for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
  122. They are quite self-explanatory once you understand the previous code.
  123. * `DelegatingSecurityContextCallable`
  124. * `DelegatingSecurityContextExecutor`
  125. * `DelegatingSecurityContextExecutorService`
  126. * `DelegatingSecurityContextRunnable`
  127. * `DelegatingSecurityContextScheduledExecutorService`
  128. * `DelegatingSecurityContextSchedulingTaskExecutor`
  129. * `DelegatingSecurityContextAsyncTaskExecutor`
  130. * `DelegatingSecurityContextTaskExecutor`
  131. * `DelegatingSecurityContextTaskScheduler`