5 changed files with 281 additions and 0 deletions
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
[[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 for users. |
||||
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 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]. |
||||
|
||||
== DelegatingSecurityContextRunnable |
||||
|
||||
One of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`. |
||||
It wraps a delegate `Runnable` in order 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 easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service. |
||||
An example of how you might do this can be found below: |
||||
|
||||
[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 code above performs the following steps: |
||||
|
||||
* Creates a `Runnable` that will be invoking our secured service. |
||||
Notice that it is not aware of Spring Security |
||||
* Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable` |
||||
* Use the `DelegatingSecurityContextRunnable` to create a Thread |
||||
* Start the Thread we created |
||||
|
||||
Since it is quite common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder` there is a shortcut constructor for it. |
||||
The following code is the same as the code above: |
||||
|
||||
|
||||
[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 in order to use it. |
||||
Let's take a look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security. |
||||
|
||||
The design of `DelegatingSecurityContextExecutor` is very similar to that of `DelegatingSecurityContextRunnable` except it accepts a delegate `Executor` instead of a delegate `Runnable`. |
||||
You can see an example of how it might be used below: |
||||
|
||||
|
||||
[source,java] |
||||
---- |
||||
SecurityContext context = SecurityContextHolder.createEmptyContext(); |
||||
Authentication authentication = |
||||
new UsernamePasswordAuthenticationToken("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); |
||||
---- |
||||
|
||||
The code performs the following steps: |
||||
|
||||
* Creates the `SecurityContext` to be used for our `DelegatingSecurityContextExecutor`. |
||||
Note that in this example we simply create the `SecurityContext` by hand. |
||||
However, it does not matter where or how we get the `SecurityContext` (i.e. we could obtain it from the `SecurityContextHolder` if we wanted). |
||||
* Creates a delegateExecutor that is in charge of executing submitted ``Runnable``s |
||||
* 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 instance, the same `SecurityContext` will be used for every Runnable submitted to our `DelegatingSecurityContextExecutor`. |
||||
This is nice if we are running background tasks that need to be run by a user with elevated privileges. |
||||
* 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`. |
||||
|
||||
[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`, then the `originalRunnable` is run, and then 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` at the time we invoked `executor.execute(Runnable)` (i.e. the currently logged in user) to process ``originalRunnable``? |
||||
This can be done by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor. |
||||
For example: |
||||
|
||||
|
||||
[source,java] |
||||
---- |
||||
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor(); |
||||
DelegatingSecurityContextExecutor executor = |
||||
new DelegatingSecurityContextExecutor(delegateExecutor); |
||||
---- |
||||
|
||||
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`. |
||||
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 |
||||
|
||||
Refer to the Javadoc for additional integrations with both the Java concurrent APIs and the Spring Task abstractions. |
||||
They are quite self-explanatory once you understand the previous code. |
||||
|
||||
* `DelegatingSecurityContextCallable` |
||||
* `DelegatingSecurityContextExecutor` |
||||
* `DelegatingSecurityContextExecutorService` |
||||
* `DelegatingSecurityContextRunnable` |
||||
* `DelegatingSecurityContextScheduledExecutorService` |
||||
* `DelegatingSecurityContextSchedulingTaskExecutor` |
||||
* `DelegatingSecurityContextAsyncTaskExecutor` |
||||
* `DelegatingSecurityContextTaskExecutor` |
||||
* `DelegatingSecurityContextTaskScheduler` |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
[[data]] |
||||
= Spring Data Integration |
||||
|
||||
Spring Security provides Spring Data integration that allows referring to the current user within your queries. |
||||
It is not only useful but necessary to include the user in the queries to support paged results since filtering the results afterwards would not scale. |
||||
|
||||
[[data-configuration]] |
||||
== Spring Data & Spring Security Configuration |
||||
|
||||
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`. |
||||
In Java Configuration, this would look like: |
||||
|
||||
[source,java] |
||||
---- |
||||
@Bean |
||||
public SecurityEvaluationContextExtension securityEvaluationContextExtension() { |
||||
return new SecurityEvaluationContextExtension(); |
||||
} |
||||
---- |
||||
|
||||
In XML Configuration, this would look like: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<bean class="org.springframework.security.data.repository.query.SecurityEvaluationContextExtension"/> |
||||
---- |
||||
|
||||
[[data-query]] |
||||
== Security Expressions within @Query |
||||
|
||||
Now Spring Security can be used within your queries. |
||||
For example: |
||||
|
||||
[source,java] |
||||
---- |
||||
@Repository |
||||
public interface MessageRepository extends PagingAndSortingRepository<Message,Long> { |
||||
@Query("select m from Message m where m.to.id = ?#{ principal?.id }") |
||||
Page<Message> findInbox(Pageable pageable); |
||||
} |
||||
---- |
||||
|
||||
This checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`. |
||||
Note that this example assumes you have customized the principal to be an Object that has an id property. |
||||
By exposing the `SecurityEvaluationContextExtension` bean, all of the xref:servlet/authorization/expression-based.adoc#common-expressions[Common Security Expressions] are available within the Query. |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
[[jackson]] |
||||
= Jackson Support |
||||
|
||||
Spring Security provides Jackson support for persisting Spring Security related classes. |
||||
This can improve the performance of serializing Spring Security related classes when working with distributed sessions (i.e. session replication, Spring Session, etc). |
||||
|
||||
To use it, register the `SecurityJackson2Modules.getModules(ClassLoader)` with `ObjectMapper` (https://github.com/FasterXML/jackson-databind[jackson-databind]): |
||||
|
||||
[source,java] |
||||
---- |
||||
ObjectMapper mapper = new ObjectMapper(); |
||||
ClassLoader loader = getClass().getClassLoader(); |
||||
List<Module> modules = SecurityJackson2Modules.getModules(loader); |
||||
mapper.registerModules(modules); |
||||
|
||||
// ... use ObjectMapper as normally ... |
||||
SecurityContext context = new SecurityContextImpl(); |
||||
// ... |
||||
String json = mapper.writeValueAsString(context); |
||||
---- |
||||
|
||||
[NOTE] |
||||
==== |
||||
The following Spring Security modules provide Jackson support: |
||||
|
||||
- spring-security-core (`CoreJackson2Module`) |
||||
- spring-security-web (`WebJackson2Module`, `WebServletJackson2Module`, `WebServerJackson2Module`) |
||||
- <<oauth2client, spring-security-oauth2-client>> (`OAuth2ClientJackson2Module`) |
||||
- spring-security-cas (`CasJackson2Module`) |
||||
==== |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
[[localization]] |
||||
= Localization |
||||
Spring Security supports localization of exception messages that end users are likely to see. |
||||
If your application is designed for English-speaking users, you don't need to do anything as by default all Security messages are in English. |
||||
If you need to support other locales, everything you need to know is contained in this section. |
||||
|
||||
All exception messages can be localized, including messages related to authentication failures and access being denied (authorization failures). |
||||
Exceptions and logging messages that are focused on developers or system deplopers (including incorrect attributes, interface contract violations, using incorrect constructors, startup time validation, debug-level logging) are not localized and instead are hard-coded in English within Spring Security's code. |
||||
|
||||
Shipping in the `spring-security-core-xx.jar` you will find an `org.springframework.security` package that in turn contains a `messages.properties` file, as well as localized versions for some common languages. |
||||
This should be referred to by your `ApplicationContext`, as Spring Security classes implement Spring's `MessageSourceAware` interface and expect the message resolver to be dependency injected at application context startup time. |
||||
Usually all you need to do is register a bean inside your application context to refer to the messages. |
||||
An example is shown below: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<bean id="messageSource" |
||||
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> |
||||
<property name="basename" value="classpath:org/springframework/security/messages"/> |
||||
</bean> |
||||
---- |
||||
|
||||
The `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages. |
||||
This default file is in English. |
||||
|
||||
If you wish to customize the `messages.properties` file, or support other languages, you should copy the file, rename it accordingly, and register it inside the above bean definition. |
||||
There are not a large number of message keys inside this file, so localization should not be considered a major initiative. |
||||
If you do perform localization of this file, please consider sharing your work with the community by logging a JIRA task and attaching your appropriately-named localized version of `messages.properties`. |
||||
|
||||
Spring Security relies on Spring's localization support in order to actually lookup the appropriate message. |
||||
In order for this to work, you have to make sure that the locale from the incoming request is stored in Spring's `org.springframework.context.i18n.LocaleContextHolder`. |
||||
Spring MVC's `DispatcherServlet` does this for your application automatically, but since Spring Security's filters are invoked before this, the `LocaleContextHolder` needs to be set up to contain the correct `Locale` before the filters are called. |
||||
You can either do this in a filter yourself (which must come before the Spring Security filters in `web.xml`) or you can use Spring's `RequestContextFilter`. |
||||
Please refer to the Spring Framework documentation for further details on using localization with Spring. |
||||
|
||||
The "contacts" sample application is set up to use localized messages. |
||||
Loading…
Reference in new issue