Skip to content

Commit 09a14bf

Browse files
committed
Port Missing Integration Docs
Closes gh-10465
1 parent 812d6f7 commit 09a14bf

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

docs/modules/ROOT/nav.adoc

+4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@
8080
*** xref:servlet/exploits/http.adoc[]
8181
*** xref:servlet/exploits/firewall.adoc[]
8282
** xref:servlet/integrations/index.adoc[Integrations]
83+
*** xref:servlet/integrations/concurrency.adoc[Concurrency]
84+
*** xref:servlet/integrations/jackson.adoc[Jackson]
85+
*** xref:servlet/integrations/localization.adoc[Localization]
8386
*** xref:servlet/integrations/servlet-api.adoc[Servlet APIs]
87+
*** xref:servlet/integrations/data.adoc[Spring Data]
8488
*** xref:servlet/integrations/mvc.adoc[Spring MVC]
8589
*** xref:servlet/integrations/websocket.adoc[WebSocket]
8690
*** xref:servlet/integrations/cors.adoc[Spring's CORS Support]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
[[concurrency]]
2+
= Concurrency Support
3+
4+
In most environments, Security is stored on a per `Thread` basis.
5+
This means that when work is done on a new `Thread`, the `SecurityContext` is lost.
6+
Spring Security provides some infrastructure to help make this much easier for users.
7+
Spring Security provides low level abstractions for working with Spring Security in multi-threaded environments.
8+
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].
9+
10+
== DelegatingSecurityContextRunnable
11+
12+
One of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`.
13+
It wraps a delegate `Runnable` in order to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate.
14+
It then invokes the delegate Runnable ensuring to clear the `SecurityContextHolder` afterwards.
15+
The `DelegatingSecurityContextRunnable` looks something like this:
16+
17+
[source,java]
18+
----
19+
public void run() {
20+
try {
21+
SecurityContextHolder.setContext(securityContext);
22+
delegate.run();
23+
} finally {
24+
SecurityContextHolder.clearContext();
25+
}
26+
}
27+
----
28+
29+
While very simple, it makes it seamless to transfer the SecurityContext from one Thread to another.
30+
This is important since, in most cases, the SecurityContextHolder acts on a per Thread basis.
31+
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.
32+
You can now easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.
33+
An example of how you might do this can be found below:
34+
35+
[source,java]
36+
----
37+
Runnable originalRunnable = new Runnable() {
38+
public void run() {
39+
// invoke secured service
40+
}
41+
};
42+
43+
SecurityContext context = SecurityContextHolder.getContext();
44+
DelegatingSecurityContextRunnable wrappedRunnable =
45+
new DelegatingSecurityContextRunnable(originalRunnable, context);
46+
47+
new Thread(wrappedRunnable).start();
48+
----
49+
50+
The code above performs the following steps:
51+
52+
* Creates a `Runnable` that will be invoking our secured service.
53+
Notice that it is not aware of Spring Security
54+
* Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`
55+
* Use the `DelegatingSecurityContextRunnable` to create a Thread
56+
* Start the Thread we created
57+
58+
Since it is quite common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder` there is a shortcut constructor for it.
59+
The following code is the same as the code above:
60+
61+
62+
[source,java]
63+
----
64+
Runnable originalRunnable = new Runnable() {
65+
public void run() {
66+
// invoke secured service
67+
}
68+
};
69+
70+
DelegatingSecurityContextRunnable wrappedRunnable =
71+
new DelegatingSecurityContextRunnable(originalRunnable);
72+
73+
new Thread(wrappedRunnable).start();
74+
----
75+
76+
The code we have is simple to use, but it still requires knowledge that we are using Spring Security.
77+
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.
78+
79+
== DelegatingSecurityContextExecutor
80+
81+
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.
82+
Let's take a look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.
83+
84+
The design of `DelegatingSecurityContextExecutor` is very similar to that of `DelegatingSecurityContextRunnable` except it accepts a delegate `Executor` instead of a delegate `Runnable`.
85+
You can see an example of how it might be used below:
86+
87+
88+
[source,java]
89+
----
90+
SecurityContext context = SecurityContextHolder.createEmptyContext();
91+
Authentication authentication =
92+
new UsernamePasswordAuthenticationToken("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
93+
context.setAuthentication(authentication);
94+
95+
SimpleAsyncTaskExecutor delegateExecutor =
96+
new SimpleAsyncTaskExecutor();
97+
DelegatingSecurityContextExecutor executor =
98+
new DelegatingSecurityContextExecutor(delegateExecutor, context);
99+
100+
Runnable originalRunnable = new Runnable() {
101+
public void run() {
102+
// invoke secured service
103+
}
104+
};
105+
106+
executor.execute(originalRunnable);
107+
----
108+
109+
The code performs the following steps:
110+
111+
* Creates the `SecurityContext` to be used for our `DelegatingSecurityContextExecutor`.
112+
Note that in this example we simply create the `SecurityContext` by hand.
113+
However, it does not matter where or how we get the `SecurityContext` (i.e. we could obtain it from the `SecurityContextHolder` if we wanted).
114+
* Creates a delegateExecutor that is in charge of executing submitted ``Runnable``s
115+
* Finally we create a `DelegatingSecurityContextExecutor` which is in charge of wrapping any Runnable that is passed into the execute method with a `DelegatingSecurityContextRunnable`.
116+
It then passes the wrapped Runnable to the delegateExecutor.
117+
In this instance, the same `SecurityContext` will be used for every Runnable submitted to our `DelegatingSecurityContextExecutor`.
118+
This is nice if we are running background tasks that need to be run by a user with elevated privileges.
119+
* 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`.
120+
121+
[source,java]
122+
----
123+
@Autowired
124+
private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor
125+
126+
public void submitRunnable() {
127+
Runnable originalRunnable = new Runnable() {
128+
public void run() {
129+
// invoke secured service
130+
}
131+
};
132+
executor.execute(originalRunnable);
133+
}
134+
----
135+
136+
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.
137+
In this example, the same user is being used to run each thread.
138+
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``?
139+
This can be done by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor.
140+
For example:
141+
142+
143+
[source,java]
144+
----
145+
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
146+
DelegatingSecurityContextExecutor executor =
147+
new DelegatingSecurityContextExecutor(delegateExecutor);
148+
----
149+
150+
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`.
151+
This means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.
152+
153+
== Spring Security Concurrency Classes
154+
155+
Refer to the Javadoc for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
156+
They are quite self-explanatory once you understand the previous code.
157+
158+
* `DelegatingSecurityContextCallable`
159+
* `DelegatingSecurityContextExecutor`
160+
* `DelegatingSecurityContextExecutorService`
161+
* `DelegatingSecurityContextRunnable`
162+
* `DelegatingSecurityContextScheduledExecutorService`
163+
* `DelegatingSecurityContextSchedulingTaskExecutor`
164+
* `DelegatingSecurityContextAsyncTaskExecutor`
165+
* `DelegatingSecurityContextTaskExecutor`
166+
* `DelegatingSecurityContextTaskScheduler`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[[data]]
2+
= Spring Data Integration
3+
4+
Spring Security provides Spring Data integration that allows referring to the current user within your queries.
5+
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.
6+
7+
[[data-configuration]]
8+
== Spring Data & Spring Security Configuration
9+
10+
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`.
11+
In Java Configuration, this would look like:
12+
13+
[source,java]
14+
----
15+
@Bean
16+
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
17+
return new SecurityEvaluationContextExtension();
18+
}
19+
----
20+
21+
In XML Configuration, this would look like:
22+
23+
[source,xml]
24+
----
25+
<bean class="org.springframework.security.data.repository.query.SecurityEvaluationContextExtension"/>
26+
----
27+
28+
[[data-query]]
29+
== Security Expressions within @Query
30+
31+
Now Spring Security can be used within your queries.
32+
For example:
33+
34+
[source,java]
35+
----
36+
@Repository
37+
public interface MessageRepository extends PagingAndSortingRepository<Message,Long> {
38+
@Query("select m from Message m where m.to.id = ?#{ principal?.id }")
39+
Page<Message> findInbox(Pageable pageable);
40+
}
41+
----
42+
43+
This checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`.
44+
Note that this example assumes you have customized the principal to be an Object that has an id property.
45+
By exposing the `SecurityEvaluationContextExtension` bean, all of the xref:servlet/authorization/expression-based.adoc#common-expressions[Common Security Expressions] are available within the Query.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[[jackson]]
2+
= Jackson Support
3+
4+
Spring Security provides Jackson support for persisting Spring Security related classes.
5+
This can improve the performance of serializing Spring Security related classes when working with distributed sessions (i.e. session replication, Spring Session, etc).
6+
7+
To use it, register the `SecurityJackson2Modules.getModules(ClassLoader)` with `ObjectMapper` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
8+
9+
[source,java]
10+
----
11+
ObjectMapper mapper = new ObjectMapper();
12+
ClassLoader loader = getClass().getClassLoader();
13+
List<Module> modules = SecurityJackson2Modules.getModules(loader);
14+
mapper.registerModules(modules);
15+
16+
// ... use ObjectMapper as normally ...
17+
SecurityContext context = new SecurityContextImpl();
18+
// ...
19+
String json = mapper.writeValueAsString(context);
20+
----
21+
22+
[NOTE]
23+
====
24+
The following Spring Security modules provide Jackson support:
25+
26+
- spring-security-core (`CoreJackson2Module`)
27+
- spring-security-web (`WebJackson2Module`, `WebServletJackson2Module`, `WebServerJackson2Module`)
28+
- <<oauth2client, spring-security-oauth2-client>> (`OAuth2ClientJackson2Module`)
29+
- spring-security-cas (`CasJackson2Module`)
30+
====
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[[localization]]
2+
= Localization
3+
Spring Security supports localization of exception messages that end users are likely to see.
4+
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.
5+
If you need to support other locales, everything you need to know is contained in this section.
6+
7+
All exception messages can be localized, including messages related to authentication failures and access being denied (authorization failures).
8+
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.
9+
10+
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.
11+
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.
12+
Usually all you need to do is register a bean inside your application context to refer to the messages.
13+
An example is shown below:
14+
15+
[source,xml]
16+
----
17+
<bean id="messageSource"
18+
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
19+
<property name="basename" value="classpath:org/springframework/security/messages"/>
20+
</bean>
21+
----
22+
23+
The `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages.
24+
This default file is in English.
25+
26+
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.
27+
There are not a large number of message keys inside this file, so localization should not be considered a major initiative.
28+
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`.
29+
30+
Spring Security relies on Spring's localization support in order to actually lookup the appropriate message.
31+
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`.
32+
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.
33+
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`.
34+
Please refer to the Spring Framework documentation for further details on using localization with Spring.
35+
36+
The "contacts" sample application is set up to use localized messages.

0 commit comments

Comments
 (0)