Port Missing Integration Docs

Closes gh-10465
This commit is contained in:
Josh Cummings 2021-11-10 15:38:29 -07:00
parent b60020a40c
commit 310a50587c
5 changed files with 281 additions and 0 deletions

View File

@ -81,7 +81,11 @@
*** xref:servlet/exploits/http.adoc[]
*** xref:servlet/exploits/firewall.adoc[]
** xref:servlet/integrations/index.adoc[Integrations]
*** xref:servlet/integrations/concurrency.adoc[Concurrency]
*** xref:servlet/integrations/jackson.adoc[Jackson]
*** xref:servlet/integrations/localization.adoc[Localization]
*** xref:servlet/integrations/servlet-api.adoc[Servlet APIs]
*** xref:servlet/integrations/data.adoc[Spring Data]
*** xref:servlet/integrations/mvc.adoc[Spring MVC]
*** xref:servlet/integrations/websocket.adoc[WebSocket]
*** xref:servlet/integrations/cors.adoc[Spring's CORS Support]

View File

@ -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`

View File

@ -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.

View File

@ -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`)
====

View File

@ -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.