提交 d8bbaa77 编写于 作者: 茶陵後's avatar 茶陵後 👍

#18 [skip ci] spring-security

上级 391802b7
# Spring Security Community
Welcome to the Spring Security Community!
This section discusses how you can make the most of our vast community.
## Getting Help
If you need help with Spring Security, we are here to help.
The following are some of the best ways to get help:
* Read through this documentation.
* Try one of our many [sample applications](samples.html#samples).
* Ask a question on [https://stackoverflow.com](https://stackoverflow.com/questions/tagged/spring-security) with the `spring-security` tag.
* Report bugs and enhancement requests at [https://github.com/spring-projects/spring-security/issues](https://github.com/spring-projects/spring-security/issues)
## Becoming Involved
We welcome your involvement in the Spring Security project.
There are many ways to contribute, including answering questions on Stack Overflow, writing new code, improving existing code, assisting with documentation, developing samples or tutorials, reporting bugs, or simply making suggestions.
For more information, see our [Contributing](https://github.com/spring-projects/spring-security/blob/main/CONTRIBUTING.adoc) documentation.
## Source Code
You can find Spring Security’s source code on GitHub at [https://github.com/spring-projects/spring-security/](https://github.com/spring-projects/spring-security/)
## Apache 2 License
Spring Security is Open Source software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
## Social Media
You can follow [@SpringSecurity](https://twitter.com/SpringSecurity) and the [Spring Security team](https://twitter.com/SpringSecurity/lists/team) on Twitter to stay up to date with the latest news.
You can also follow [@SpringCentral](https://twitter.com/SpringCentral) to keep up to date with the entire Spring portfolio.
\ No newline at end of file
# Authentication
Spring Security provides comprehensive support for [authentication](https://en.wikipedia.org/wiki/Authentication).
Authentication is how we verify the identity of who is trying to access a particular resource.
A common way to authenticate users is by requiring the user to enter a username and password.
Once authentication is performed we know the identity and can perform authorization.
Spring Security provides built in support for authenticating users.
This section is dedicated to generic authentication support that applies in both Servlet and WebFlux environments.
Refer to the sections on authentication for [Servlet](../../servlet/authentication/index.html#servlet-authentication) and WebFlux for details on what is supported for each stack.
\ No newline at end of file
此差异已折叠。
此差异已折叠。
# HTTP
All HTTP based communication, including [static resources](https://www.troyhunt.com/heres-why-your-static-website-needs-https/), should be protected [using TLS](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html).
As a framework, Spring Security does not handle HTTP connections and thus does not provide support for HTTPS directly.
However, it does provide a number of features that help with HTTPS usage.
## Redirect to HTTPS
When a client uses HTTP, Spring Security can be configured to redirect to HTTPS both [Servlet](../../servlet/exploits/http.html#servlet-http-redirect) and [WebFlux](../../reactive/exploits/http.html#webflux-http-redirect) environments.
## Strict Transport Security
Spring Security provides support for [Strict Transport Security](headers.html#headers-hsts) and enables it by default.
## Proxy Server Configuration
When using a proxy server it is important to ensure that you have configured your application properly.
For example, many applications will have a load balancer that responds to request for [https://example.com/](https://example.com/) by forwarding the request to an application server at [https://192.168.1:8080](https://192.168.1:8080).
Without proper configuration, the application server will not know that the load balancer exists and treat the request as though [https://192.168.1:8080](https://192.168.1:8080) was requested by the client.
To fix this you can use [RFC 7239](https://tools.ietf.org/html/rfc7239) to specify that a load balancer is being used.
To make the application aware of this, you need to either configure your application server aware of the X-Forwarded headers.
For example Tomcat uses the [RemoteIpValve](https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html) and Jetty uses [ForwardedRequestCustomizer](https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/server/ForwardedRequestCustomizer.html).
Alternatively, Spring users can leverage [ForwardedHeaderFilter](https://github.com/spring-projects/spring-framework/blob/v4.3.3.RELEASE/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java).
Spring Boot users may use the `server.use-forward-headers` property to configure the application.
See the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-use-tomcat-behind-a-proxy-server) for further details.
\ No newline at end of file
# Protection Against Exploits
Spring Security provides protection against common exploits.
Whenever possible, the protection is enabled by default.
Below you will find high level description of the various exploits that Spring Security protects against.
## Section Summary
* [CSRF](csrf.html)
* [HTTP Headers](headers.html)
* [HTTP Requests](http.html)
\ No newline at end of file
# 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 [AsyncContext.start(Runnable)](../../servlet/integrations/servlet-api.html#servletapi-start-runnable) and [Spring MVC Async Integration](../../servlet/integrations/mvc.html#mvc-async).
## 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:
Java
```
public void run() {
try {
SecurityContextHolder.setContext(securityContext);
delegate.run();
} finally {
SecurityContextHolder.clearContext();
}
}
```
Kotlin
```
fun 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 [\<global-method-security\>](../../servlet/appendix/namespace/method-security.html#nsa-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:
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();
```
Kotlin
```
val originalRunnable = Runnable {
// invoke secured service
}
val context: SecurityContext = SecurityContextHolder.getContext()
val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable, context)
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:
Java
```
Runnable originalRunnable = new Runnable() {
public void run() {
// invoke secured service
}
};
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable);
new Thread(wrappedRunnable).start();
```
Kotlin
```
val originalRunnable = Runnable {
// invoke secured service
}
val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable)
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:
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);
```
Kotlin
```
val context: SecurityContext = SecurityContextHolder.createEmptyContext()
val authentication: Authentication =
UsernamePasswordAuthenticationToken("user", "doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"))
context.authentication = authentication
val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = DelegatingSecurityContextExecutor(delegateExecutor, context)
val originalRunnable = Runnable {
// 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`.
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);
}
```
Kotlin
```
@Autowired
lateinit var executor: Executor // becomes an instance of our DelegatingSecurityContextExecutor
fun submitRunnable() {
val originalRunnable = Runnable {
// 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:
Java
```
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
new DelegatingSecurityContextExecutor(delegateExecutor);
```
Kotlin
```
val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = 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`
\ No newline at end of file
# Spring Security Crypto Module
## Introduction
The Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding.
The code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code.
## Encryptors
The Encryptors class provides factory methods for constructing symmetric encryptors.
Using this class, you can create ByteEncryptors to encrypt data in raw byte[] form.
You can also construct TextEncryptors to encrypt text strings.
Encryptors are thread-safe.
### BytesEncryptor
Use the `Encryptors.stronger` factory method to construct a BytesEncryptor:
Example 1. BytesEncryptor
Java
```
Encryptors.stronger("password", "salt");
```
Kotlin
```
Encryptors.stronger("password", "salt")
```
The "stronger" encryption method creates an encryptor using 256 bit AES encryption with
Galois Counter Mode (GCM).
It derives the secret key using PKCS #5’s PBKDF2 (Password-Based Key Derivation Function #2).
This method requires Java 6.
The password used to generate the SecretKey should be kept in a secure place and not be shared.
The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised.
A 16-byte random initialization vector is also applied so each encrypted message is unique.
The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length.
Such a salt may be generated using a KeyGenerator:
Example 2. Generating a key
Java
```
String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded
```
Kotlin
```
val salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded
```
Users may also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode.
This mode is not [authenticated](https://en.wikipedia.org/wiki/Authenticated_encryption) and does not provide any
guarantees about the authenticity of the data.
For a more secure alternative, users should prefer `Encryptors.stronger`.
### TextEncryptor
Use the Encryptors.text factory method to construct a standard TextEncryptor:
Example 3. TextEncryptor
Java
```
Encryptors.text("password", "salt");
```
Kotlin
```
Encryptors.text("password", "salt")
```
A TextEncryptor uses a standard BytesEncryptor to encrypt text data.
Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in the database.
Use the Encryptors.queryableText factory method to construct a "queryable" TextEncryptor:
Example 4. Queryable TextEncryptor
Java
```
Encryptors.queryableText("password", "salt");
```
Kotlin
```
Encryptors.queryableText("password", "salt")
```
The difference between a queryable TextEncryptor and a standard TextEncryptor has to do with initialization vector (iv) handling.
The iv used in a queryable TextEncryptor#encrypt operation is shared, or constant, and is not randomly generated.
This means the same text encrypted multiple times will always produce the same encryption result.
This is less secure, but necessary for encrypted data that needs to be queried against.
An example of queryable encrypted text would be an OAuth apiKey.
## Key Generators
The KeyGenerators class provides a number of convenience factory methods for constructing different types of key generators.
Using this class, you can create a BytesKeyGenerator to generate byte[] keys.
You can also construct a StringKeyGenerator to generate string keys.
KeyGenerators are thread-safe.
### BytesKeyGenerator
Use the KeyGenerators.secureRandom factory methods to generate a BytesKeyGenerator backed by a SecureRandom instance:
Example 5. BytesKeyGenerator
Java
```
BytesKeyGenerator generator = KeyGenerators.secureRandom();
byte[] key = generator.generateKey();
```
Kotlin
```
val generator = KeyGenerators.secureRandom()
val key = generator.generateKey()
```
The default key length is 8 bytes.
There is also a KeyGenerators.secureRandom variant that provides control over the key length:
Example 6. KeyGenerators.secureRandom
Java
```
KeyGenerators.secureRandom(16);
```
Kotlin
```
KeyGenerators.secureRandom(16)
```
Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator that always returns the same key on every invocation:
Example 7. KeyGenerators.shared
Java
```
KeyGenerators.shared(16);
```
Kotlin
```
KeyGenerators.shared(16)
```
### StringKeyGenerator
Use the KeyGenerators.string factory method to construct a 8-byte, SecureRandom KeyGenerator that hex-encodes each key as a String:
Example 8. StringKeyGenerator
Java
```
KeyGenerators.string();
```
Kotlin
```
KeyGenerators.string()
```
## Password Encoding
The password package of the spring-security-crypto module provides support for encoding passwords.`PasswordEncoder` is the central service interface and has the following signature:
```
public interface PasswordEncoder {
String encode(String rawPassword);
boolean matches(String rawPassword, String encodedPassword);
}
```
The matches method returns true if the rawPassword, once encoded, equals the encodedPassword.
This method is designed to support password-based authentication schemes.
The `BCryptPasswordEncoder` implementation uses the widely supported "bcrypt" algorithm to hash the passwords.
Bcrypt uses a random 16 byte salt value and is a deliberately slow algorithm, in order to hinder password crackers.
The amount of work it does can be tuned using the "strength" parameter which takes values from 4 to 31.
The higher the value, the more work has to be done to calculate the hash.
The default value is 10.
You can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash.
Example 9. BCryptPasswordEncoder
Java
```
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
```
Kotlin
```
// Create an encoder with strength 16
val encoder = BCryptPasswordEncoder(16)
val result: String = encoder.encode("myPassword")
assertTrue(encoder.matches("myPassword", result))
```
The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords.
In order to defeat password cracking PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system.
Example 10. Pbkdf2PasswordEncoder
Java
```
// Create an encoder with all the defaults
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
```
Kotlin
```
// Create an encoder with all the defaults
val encoder = Pbkdf2PasswordEncoder()
val result: String = encoder.encode("myPassword")
assertTrue(encoder.matches("myPassword", result))
```
\ No newline at end of file
# 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.
## 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:
Java
```
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
```
Kotlin
```
@Bean
fun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {
return SecurityEvaluationContextExtension()
}
```
In XML Configuration, this would look like:
```
<bean class="org.springframework.security.data.repository.query.SecurityEvaluationContextExtension"/>
```
## Security Expressions within @Query
Now Spring Security can be used within your queries.
For example:
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);
}
```
Kotlin
```
@Repository
interface MessageRepository : PagingAndSortingRepository<Message?, Long?> {
@Query("select m from Message m where m.to.id = ?#{ principal?.id }")
fun findInbox(pageable: Pageable?): Page<Message?>?
}
```
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 [Common Security Expressions](../../servlet/authorization/expression-based.html#common-expressions) are available within the Query.
\ No newline at end of file
# 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` ([jackson-databind](https://github.com/FasterXML/jackson-databind)):
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);
```
Kotlin
```
val mapper = ObjectMapper()
val loader = javaClass.classLoader
val modules: MutableList<Module> = SecurityJackson2Modules.getModules(loader)
mapper.registerModules(modules)
// ... use ObjectMapper as normally ...
val context: SecurityContext = SecurityContextImpl()
// ...
val json: String = mapper.writeValueAsString(context)
```
| |The following Spring Security modules provide Jackson support:<br/><br/>* spring-security-core (`CoreJackson2Module`)<br/><br/>* spring-security-web (`WebJackson2Module`, `WebServletJackson2Module`, `WebServerJackson2Module`)<br/><br/>* [ spring-security-oauth2-client](../../servlet/oauth2/client/index.html#oauth2client) (`OAuth2ClientJackson2Module`)<br/><br/>* spring-security-cas (`CasJackson2Module`)|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
\ No newline at end of file
# Localization
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:
```
<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.
\ No newline at end of file
# Integrations
Spring Security provides integrations with numerous frameworks and APIs.
In this section, we discuss generic integrations that are not specific to Servlet or Reactive environments.
To see specific integrations, refer to the [Servlet](../../servlet/integrations/index.html) and [Reactive](../../servlet/integrations/index.html) Integrations sections.
## Section Summary
* [Cryptography](cryptography.html)
* [Spring Data](data.html)
* [Java’s Concurrency APIs](concurrency.html)
* [Jackson](jackson.html)
* [Localization](localization.html)
\ No newline at end of file
# Features
Spring Security provides comprehensive support for [authentication](authentication/index.html), [authorization](authorization/index.html), and protection against [common exploits](exploits/index.html#exploits).
It also provides integration with other libraries to simplify its usage.
## Section Summary
* [Authentication](authentication/index.html)
* [Protection Against Exploits](exploits/index.html)
* [Integrations](integrations/index.html)
[Getting Spring Security](../getting-spring-security.html)[Authentication](authentication/index.html)
\ No newline at end of file
# Getting Spring Security
This section discusses all you need to know about getting the Spring Security binaries.
See [Source Code](community.html#community-source) for how to obtain the source code.
## Release Numbering
Spring Security versions are formatted as MAJOR.MINOR.PATCH such that:
* MAJOR versions may contain breaking changes.
Typically, these are done to provide improved security to match modern security practices.
* MINOR versions contain enhancements but are considered passive updates
* PATCH level should be perfectly compatible, forwards and backwards, with the possible exception of changes that fix bugs.
## Usage with Maven
As most open source projects, Spring Security deploys its dependencies as Maven artifacts.
The topics in this section provide detail on how to consume Spring Security when using Maven.
### Spring Boot with Maven
Spring Boot provides a `spring-boot-starter-security` starter that aggregates Spring Security-related dependencies together.
The simplest and preferred way to use the starter is to use [Spring Initializr](https://docs.spring.io/initializr/docs/current/reference/html/) by using an IDE integration ([Eclipse](https://joshlong.com/jl/blogPost/tech_tip_geting_started_with_spring_boot.html), [IntelliJ](https://www.jetbrains.com/help/idea/spring-boot.html#d1489567e2), [NetBeans](https://github.com/AlexFalappa/nb-springboot/wiki/Quick-Tour)) or through [https://start.spring.io](https://start.spring.io).
Alternatively, you can manually add the starter, as the following example shows:
Example 1. pom.xml
```
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
```
Since Spring Boot provides a Maven BOM to manage dependency versions, you do not need to specify a version.
If you wish to override the Spring Security version, you may do so by providing a Maven property, as the following example shows:
Example 2. pom.xml
```
<properties>
<!-- ... -->
<spring-security.version>5.6.2</spring-security.version>
</properties>
```
Since Spring Security makes breaking changes only in major releases, it is safe to use a newer version of Spring Security with Spring Boot.
However, at times, you may need to update the version of Spring Framework as well.
You can do so by adding a Maven property, as the following example shows:
Example 3. pom.xml
```
<properties>
<!-- ... -->
<spring.version>5.3.16</spring.version>
</properties>
```
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate [Project Modules and Dependencies](modules.html#modules).
### Maven Without Spring Boot
When you use Spring Security without Spring Boot, the preferred way is to use Spring Security’s BOM to ensure a consistent version of Spring Security is used throughout the entire project. The following example shows how to do so:
Example 4. pom.xml
```
<dependencyManagement>
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>{spring-security-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
A minimal Spring Security Maven set of dependencies typically looks like the following:
Example 5. pom.xml
```
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
</dependencies>
```
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate [Project Modules and Dependencies](modules.html#modules).
Spring Security builds against Spring Framework 5.3.16 but should generally work with any newer version of Spring Framework 5.x.
Many users are likely to run afoul of the fact that Spring Security’s transitive dependencies resolve Spring Framework 5.3.16, which can cause strange classpath problems.
The easiest way to resolve this is to use the `spring-framework-bom` within the `<dependencyManagement>` section of your `pom.xml` as the following example shows:
Example 6. pom.xml
```
<dependencyManagement>
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>5.3.16</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
The preceding example ensures that all the transitive dependencies of Spring Security use the Spring 5.3.16 modules.
| |This approach uses Maven’s “bill of materials” (BOM) concept and is only available in Maven 2.0.9+.<br/>For additional details about how dependencies are resolved, see [Maven’s Introduction to the Dependency Mechanism documentation](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html).|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Maven Repositories
All GA releases (that is, versions ending in .RELEASE) are deployed to Maven Central, so no additional Maven repositories need to be declared in your pom.
If you use a SNAPSHOT version, you need to ensure that you have the Spring Snapshot repository defined, as the following example shows:
Example 7. pom.xml
```
<repositories>
<!-- ... possibly other repository elements ... -->
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
```
If you use a milestone or release candidate version, you need to ensure that you have the Spring Milestone repository defined, as the following example shows:
Example 8. pom.xml
```
<repositories>
<!-- ... possibly other repository elements ... -->
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
```
## Gradle
As most open source projects, Spring Security deploys its dependencies as Maven artifacts, which allows for first-class Gradle support.
The following topics provide detail on how to consume Spring Security when using Gradle.
### Spring Boot with Gradle
Spring Boot provides a `spring-boot-starter-security` starter that aggregates Spring Security related dependencies together.
The simplest and preferred method to use the starter is to use [Spring Initializr](https://docs.spring.io/initializr/docs/current/reference/html/) by using an IDE integration ([Eclipse](https://joshlong.com/jl/blogPost/tech_tip_geting_started_with_spring_boot.html), [IntelliJ](https://www.jetbrains.com/help/idea/spring-boot.html#d1489567e2), [NetBeans](https://github.com/AlexFalappa/nb-springboot/wiki/Quick-Tour)) or through [https://start.spring.io](https://start.spring.io).
Alternatively, you can manually add the starter, as the following example shows:
Example 9. build.gradle
```
dependencies {
compile "org.springframework.boot:spring-boot-starter-security"
}
```
Since Spring Boot provides a Maven BOM to manage dependency versions, you need not specify a version.
If you wish to override the Spring Security version, you may do so by providing a Gradle property, as the following example shows:
Example 10. build.gradle
```
ext['spring-security.version']='5.6.2'
```
Since Spring Security makes breaking changes only in major releases, it is safe to use a newer version of Spring Security with Spring Boot.
However, at times, you may need to update the version of Spring Framework as well.
You can do so by adding a Gradle property, as the following example shows:
Example 11. build.gradle
```
ext['spring.version']='5.3.16'
```
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate [Project Modules and Dependencies](modules.html#modules).
### Gradle Without Spring Boot
When you use Spring Security without Spring Boot, the preferred way is to use Spring Security’s BOM to ensure a consistent version of Spring Security is used throughout the entire project.
You can do so by using the [Dependency Management Plugin](https://github.com/spring-gradle-plugins/dependency-management-plugin), as the following example shows:
Example 12. build.gradle
```
plugins {
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
dependencyManagement {
imports {
mavenBom 'org.springframework.security:spring-security-bom:5.6.2'
}
}
```
A minimal Spring Security Maven set of dependencies typically looks like the following:
Example 13. build.gradle
```
dependencies {
compile "org.springframework.security:spring-security-web"
compile "org.springframework.security:spring-security-config"
}
```
If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate [Project Modules and Dependencies](modules.html#modules).
Spring Security builds against Spring Framework 5.3.16 but should generally work with any newer version of Spring Framework 5.x.
Many users are likely to run afoul of the fact that Spring Security’s transitive dependencies resolve Spring Framework 5.3.16, which can cause strange classpath problems.
The easiest way to resolve this is to use the `spring-framework-bom` within your `<dependencyManagement>` section of your `pom.xml`.
You can do so by using the [Dependency Management Plugin](https://github.com/spring-gradle-plugins/dependency-management-plugin), as the following example shows:
Example 14. build.gradle
```
plugins {
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
dependencyManagement {
imports {
mavenBom 'org.springframework:spring-framework-bom:5.3.16'
}
}
```
The preceding example ensures that all the transitive dependencies of Spring Security use the Spring 5.3.16 modules.
### Gradle Repositories
All GA releases (that is, versions ending in .RELEASE) are deployed to Maven Central, so using the mavenCentral() repository is sufficient for GA releases. The following example shows how to do so:
Example 15. build.gradle
```
repositories {
mavenCentral()
}
```
If you use a SNAPSHOT version, you need to ensure you have the Spring Snapshot repository defined, as the following example shows:
Example 16. build.gradle
```
repositories {
maven { url 'https://repo.spring.io/snapshot' }
}
```
If you use a milestone or release candidate version, you need to ensure that you have the Spring Milestone repository defined, as the following example shows:
Example 17. build.gradle
```
repositories {
maven { url 'https://repo.spring.io/milestone' }
}
```
\ No newline at end of file
# Project Modules and Dependencies
Even if you do not use Maven, we recommend that you consult the `pom.xml` files to get an idea of third-party dependencies and versions.
Another good idea is to examine the libraries that are included in the sample applications.
This section provides a reference of the modules in Spring Security and the additional dependencies that they require in order to function in a running application.
We don’t include dependencies that are only used when building or testing Spring Security itself.
Nor do we include transitive dependencies which are required by external dependencies.
The version of Spring required is listed on the project website, so the specific versions are omitted for Spring dependencies below.
Note that some of the dependencies listed as "optional" below may still be required for other non-security functionality in a Spring application.
Also dependencies listed as "optional" may not actually be marked as such in the project’s Maven POM files if they are used in most applications.
They are "optional" only in the sense that you don’t need them unless you are using the specified functionality.
Where a module depends on another Spring Security module, the non-optional dependencies of the module it depends on are also assumed to be required and are not listed separately.
## Core — `spring-security-core.jar`
This module contains core authentication and access-contol classes and interfaces, remoting support, and basic provisioning APIs.
It is required by any application that uses Spring Security.
It supports standalone applications, remote clients, method (service layer) security, and JDBC user provisioning.
It contains the following top-level packages:
* `org.springframework.security.core`
* `org.springframework.security.access`
* `org.springframework.security.authentication`
* `org.springframework.security.provisioning`
| Dependency |Version| Description |
|-----------------|-------|---------------------------------------------------------------------------|
| ehcache | 1.6.2 |Required if the Ehcache-based user cache implementation is used (optional).|
| spring-aop | | Method security is based on Spring AOP |
| spring-beans | | Required for Spring configuration |
|spring-expression| | Required for expression-based method security (optional) |
| spring-jdbc | | Required if using a database to store user data (optional). |
| spring-tx | | Required if using a database to store user data (optional). |
| aspectjrt |1.6.10 | Required if using AspectJ support (optional). |
| jsr250-api | 1.0 | Required if you are using JSR-250 method-security annotations (optional). |
## Remoting — `spring-security-remoting.jar`
This module provides integration with Spring Remoting.
You do not need this unless you are writing a remote client that uses Spring Remoting.
The main package is `org.springframework.security.remoting`.
| Dependency |Version| Description |
|--------------------|-------|-----------------------------------------------------|
|spring-security-core| | |
| spring-web | |Required for clients which use HTTP remoting support.|
## Web — `spring-security-web.jar`
This module contains filters and related web-security infrastructure code.
It contains anything with a servlet API dependency.
You need it if you require Spring Security web authentication services and URL-based access-control.
The main package is `org.springframework.security.web`.
| Dependency |Version| Description |
|--------------------|-------|-------------------------------------------------------------------------------|
|spring-security-core| | |
| spring-web | | Spring web support classes are used extensively. |
| spring-jdbc | | Required for JDBC-based persistent remember-me token repository (optional). |
| spring-tx | |Required by remember-me persistent token repository implementations (optional).|
## Config — `spring-security-config.jar`
This module contains the security namespace parsing code and Java configuration code.
You need it if you use the Spring Security XML namespace for configuration or Spring Security’s Java Configuration support.
The main package is `org.springframework.security.config`.
None of the classes are intended for direct use in an application.
| Dependency |Version| Description |
|----------------------|-------|-----------------------------------------------------------------------------|
| spring-security-core | | |
| spring-security-web | |Required if you are using any web-related namespace configuration (optional).|
| spring-security-ldap | | Required if you are using the LDAP namespace options (optional). |
|spring-security-openid| | Required if you are using OpenID authentication (optional). |
| aspectjweaver |1.6.10 | Required if using the protect-pointcut namespace syntax (optional). |
## LDAP — `spring-security-ldap.jar`
This module provides LDAP authentication and provisioning code.
It is required if you need to use LDAP authentication or manage LDAP user entries.
The top-level package is `org.springframework.security.ldap`.
| Dependency |Version| Description |
|-----------------------------------------------------------------------------------------------------------------------------------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| spring-security-core | | |
| spring-ldap-core | 1.3.0 | LDAP support is based on Spring LDAP. |
| spring-tx | | Data exception classes are required. |
|apache-ds <sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>| 1.5.5 | Required if you are using an embedded LDAP server (optional). |
| shared-ldap |0.9.15 | Required if you are using an embedded LDAP server (optional). |
| ldapsdk | 4.1 |Mozilla LdapSDK.<br/>Used for decoding LDAP password policy controls if you are using password-policy functionality with OpenLDAP, for example.|
## OAuth 2.0 Core — `spring-security-oauth2-core.jar`
`spring-security-oauth2-core.jar` contains core classes and interfaces that provide support for the OAuth 2.0 Authorization Framework and for OpenID Connect Core 1.0.
It is required by applications that use OAuth 2.0 or OpenID Connect Core 1.0, such as client, resource server, and authorization server.
The top-level package is `org.springframework.security.oauth2.core`.
## OAuth 2.0 Client — `spring-security-oauth2-client.jar`
`spring-security-oauth2-client.jar` contains Spring Security’s client support for OAuth 2.0 Authorization Framework and OpenID Connect Core 1.0.
It is required by applications that use OAuth 2.0 Login or OAuth Client support.
The top-level package is `org.springframework.security.oauth2.client`.
## OAuth 2.0 JOSE — `spring-security-oauth2-jose.jar`
`spring-security-oauth2-jose.jar` contains Spring Security’s support for the JOSE (Javascript Object Signing and Encryption) framework.
The JOSE framework is intended to provide a method to securely transfer claims between parties.
It is built from a collection of specifications:
* JSON Web Token (JWT)
* JSON Web Signature (JWS)
* JSON Web Encryption (JWE)
* JSON Web Key (JWK)
It contains the following top-level packages:
* `org.springframework.security.oauth2.jwt`
* `org.springframework.security.oauth2.jose`
## OAuth 2.0 Resource Server — `spring-security-oauth2-resource-server.jar`
`spring-security-oauth2-resource-server.jar` contains Spring Security’s support for OAuth 2.0 Resource Servers.
It is used to protect APIs via OAuth 2.0 Bearer Tokens.
The top-level package is `org.springframework.security.oauth2.server.resource`.
## ACL — `spring-security-acl.jar`
This module contains a specialized domain object ACL implementation.
It is used to apply security to specific domain object instances within your application.
The top-level package is `org.springframework.security.acls`.
| Dependency |Version| Description |
|--------------------|-------|-------------------------------------------------------------------------------------------------------------------|
|spring-security-core| | |
| ehcache | 1.6.2 |Required if the Ehcache-based ACL cache implementation is used (optional if you are using your own implementation).|
| spring-jdbc | | Required if you are using the default JDBC-based AclService (optional if you implement your own). |
| spring-tx | | Required if you are using the default JDBC-based AclService (optional if you implement your own). |
## CAS — `spring-security-cas.jar`
This module contains Spring Security’s CAS client integration.
You should use it if you want to use Spring Security web authentication with a CAS single sign-on server.
The top-level package is `org.springframework.security.cas`.
| Dependency |Version| Description |
|--------------------|-------|--------------------------------------------------------------------------------|
|spring-security-core| | |
|spring-security-web | | |
| cas-client-core |3.1.12 |The JA-SIG CAS Client.<br/>This is the basis of the Spring Security integration.|
| ehcache | 1.6.2 | Required if you are using the Ehcache-based ticket cache (optional). |
## OpenID — `spring-security-openid.jar`
| |The OpenID 1.0 and 2.0 protocols have been deprecated and users are encouraged to migrate to OpenID Connect, which is supported by spring-security-oauth2.|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------|
This module contains OpenID web authentication support.
It is used to authenticate users against an external OpenID server.
The top-level package is `org.springframework.security.openid`.
It requires OpenID4Java.
| Dependency |Version| Description |
|--------------------|-------|------------------------------------------------------|
|spring-security-core| | |
|spring-security-web | | |
| openid4java-nodeps | 0.9.6 |Spring Security’s OpenID integration uses OpenID4Java.|
| httpclient | 4.1.1 | openid4java-nodeps depends on HttpClient 4. |
| guice | 2.0 | openid4java-nodeps depends on Guice 2. |
## Test — `spring-security-test.jar`
This module contains support for testing with Spring Security.
## Taglibs — `spring-secuity-taglibs.jar`
Provides Spring Security’s JSP tag implementations.
| Dependency |Version| Description |
|--------------------|-------|------------------------------------------------------------------------------------------------------------|
|spring-security-core| | |
|spring-security-web | | |
|spring-security-acl | |Required if you are using the `accesscontrollist` tag or `hasPermission()` expressions with ACLs (optional).|
| spring-expression | | Required if you are using SPEL expressions in your tag access constraints. |
---
[1](#_footnoteref_1). The modules `apacheds-core`, `apacheds-core-entry`, `apacheds-protocol-shared`, `apacheds-protocol-ldap` and `apacheds-server-jndi` are required.
\ No newline at end of file
# Spring Security
Spring Security is a framework that provides [authentication](features/authentication/index.html), [authorization](features/authorization/index.html), and [protection against common attacks](features/exploits/index.html).
With first class support for securing both [imperative](servlet/index.html) and [reactive](reactive/index.html) applications, it is the de-facto standard for securing Spring-based applications.
For a complete list of features, see the [Features](features/index.html) section of the reference.
## Getting Started
If you are ready to start securing an application see the Getting Started sections for [servlet](servlet/getting-started.html) and [reactive](reactive/getting-started.html). These sections will walk you through creating your first Spring Security applications.
If you want to understand how Spring Security works, you can refer to the [Architecture](servlet/architecture.html) section.
If you have any questions, there is a wonderful [community](community.html) that would love to help you!
\ No newline at end of file
# Prerequisites
Spring Security requires a Java 8 or higher Runtime Environment.
As Spring Security aims to operate in a self-contained manner, you do not need to place any special configuration files in your Java Runtime Environment.
In particular, you need not configure a special Java Authentication and Authorization Service (JAAS) policy file or place Spring Security into common classpath locations.
Similarly, if you use an EJB Container or Servlet Container, you need not put any special configuration files anywhere nor include Spring Security in a server classloader.
All the required files are contained within your application.
This design offers maximum deployment time flexibility, as you can copy your target artifact (be it a JAR, WAR, or EAR) from one system to another and it immediately works.
\ No newline at end of file
# Logout
Spring Security provides a logout endpoint by default.
Once logged in, you can `GET /logout` to see a default logout confirmation page, or you can `POST /logout` to initiate logout.
This will:
* clear the `ServerCsrfTokenRepository`, `ServerSecurityContextRepository`, and
* redirect back to the login page
Often, you will want to also invalidate the session on logout.
To achieve this, you can add the `WebSessionServerLogoutHandler` to your logout configuration, like so:
```
@Bean
SecurityWebFilterChain http(ServerHttpSecurity http) throws Exception {
DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(
new WebSessionServerLogoutHandler(), new SecurityContextServerLogoutHandler()
);
http
.authorizeExchange((exchange) -> exchange.anyExchange().authenticated())
.logout((logout) -> logout.logoutHandler(logoutHandler));
return http.build();
}
```
\ No newline at end of file
# Reactive X.509 Authentication
Similar to [Servlet X.509 authentication](../../servlet/authentication/x509.html#servlet-x509), reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.
Below is an example of a reactive x509 security configuration:
Java
```
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.x509(withDefaults())
.authorizeExchange(exchanges -> exchanges
.anyExchange().permitAll()
);
return http.build();
}
```
Kotlin
```
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
x509 { }
authorizeExchange {
authorize(anyExchange, authenticated)
}
}
}
```
In the configuration above, when neither `principalExtractor` nor `authenticationManager` is provided defaults will be used. The default principal extractor is `SubjectDnX509PrincipalExtractor` which extracts the CN (common name) field from a certificate provided by a client. The default authentication manager is `ReactivePreAuthenticatedAuthenticationManager` which performs user account validation, checking that user account with a name extracted by `principalExtractor` exists and it is not locked, disabled, or expired.
The next example demonstrates how these defaults can be overridden.
Java
```
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
SubjectDnX509PrincipalExtractor principalExtractor =
new SubjectDnX509PrincipalExtractor();
principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");
ReactiveAuthenticationManager authenticationManager = authentication -> {
authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
return Mono.just(authentication);
};
http
.x509(x509 -> x509
.principalExtractor(principalExtractor)
.authenticationManager(authenticationManager)
)
.authorizeExchange(exchanges -> exchanges
.anyExchange().authenticated()
);
return http.build();
}
```
Kotlin
```
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
val customPrincipalExtractor = SubjectDnX509PrincipalExtractor()
customPrincipalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)")
val customAuthenticationManager = ReactiveAuthenticationManager { authentication: Authentication ->
authentication.isAuthenticated = "Trusted Org Unit" == authentication.name
Mono.just(authentication)
}
return http {
x509 {
principalExtractor = customPrincipalExtractor
authenticationManager = customAuthenticationManager
}
authorizeExchange {
authorize(anyExchange, authenticated)
}
}
}
```
In this example, a username is extracted from the OU field of a client certificate instead of CN, and account lookup using `ReactiveUserDetailsService` is not performed at all. Instead, if the provided certificate issued to an OU named "Trusted Org Unit", a request will be authenticated.
For an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, please refer to [https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509](https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509).
\ No newline at end of file
# Authorize ServerHttpRequest
Spring Security provides support for authorizing the incoming HTTP requests.
By default, Spring Security’s authorization will require all requests to be authenticated.
The explicit configuration looks like:
Example 1. All Requests Require Authenticated User
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges -> exchanges
.anyExchange().authenticated()
)
.httpBasic(withDefaults())
.formLogin(withDefaults());
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
formLogin { }
httpBasic { }
}
}
```
We can configure Spring Security to have different rules by adding more rules in order of precedence.
Example 2. Multiple Authorize Requests Rules
Java
```
import static org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.hasRole;
// ...
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
// @formatter:off
http
// ...
.authorizeExchange((authorize) -> authorize (1)
.pathMatchers("/resources/**", "/signup", "/about").permitAll() (2)
.pathMatchers("/admin/**").hasRole("ADMIN") (3)
.pathMatchers("/db/**").access((authentication, context) -> (4)
hasRole("ADMIN").check(authentication, context)
.filter(decision -> !decision.isGranted())
.switchIfEmpty(hasRole("DBA").check(authentication, context))
)
.anyExchange().denyAll() (5)
);
// @formatter:on
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange { (1)
authorize(pathMatchers("/resources/**", "/signup", "/about"), permitAll) (2)
authorize("/admin/**", hasRole("ADMIN")) (3)
authorize("/db/**", { authentication, context -> (4)
hasRole("ADMIN").check(authentication, context)
.filter({ decision -> !decision.isGranted() })
.switchIfEmpty(hasRole("DBA").check(authentication, context))
})
authorize(anyExchange, denyAll) (5)
}
// ...
}
}
```
|**1**| There are multiple authorization rules specified.<br/>Each rule is considered in the order they were declared. |
|-----|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**| We specified multiple URL patterns that any user can access.<br/>Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about". |
|**3**| Any URL that starts with "/admin/" will be restricted to users who have the authority "ROLE\_ADMIN".<br/>You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE\_" prefix. |
|**4**|Any URL that starts with "/db/" requires the user to have both "ROLE\_ADMIN" and "ROLE\_DBA".<br/>This demonstrates the flexibility of providing a custom `ReactiveAuthorizationManager` allowing us to implement arbitrary authorization logic.<br/>For simplicity, the sample uses a lambda and delegate to the existing `AuthorityReactiveAuthorizationManager.hasRole` implementation.<br/>However, in a real world situation applications would likely implement the logic in a proper class implementing `ReactiveAuthorizationManager`.|
|**5**| Any URL that has not already been matched on is denied access.<br/>This is a good strategy if you do not want to accidentally forget to update your authorization rules. |
\ No newline at end of file
# EnableReactiveMethodSecurity
Spring Security supports method security using [Reactor’s Context](https://projectreactor.io/docs/core/release/reference/#context) which is setup using `ReactiveSecurityContextHolder`.
For example, this demonstrates how to retrieve the currently logged in user’s message.
| |For this to work the return type of the method must be a `org.reactivestreams.Publisher` (i.e. `Mono`/`Flux`) or the function must be a Kotlin coroutine function.<br/>This is necessary to integrate with Reactor’s `Context`.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
Java
```
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
Mono<String> messageByUsername = ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getName)
.flatMap(this::findMessageByUsername)
// In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`
.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication));
StepVerifier.create(messageByUsername)
.expectNext("Hi user")
.verifyComplete();
```
Kotlin
```
val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
val messageByUsername: Mono<String> = ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getName)
.flatMap(this::findMessageByUsername) // In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`
.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(authentication))
StepVerifier.create(messageByUsername)
.expectNext("Hi user")
.verifyComplete()
```
with `this::findMessageByUsername` defined as:
Java
```
Mono<String> findMessageByUsername(String username) {
return Mono.just("Hi " + username);
}
```
Kotlin
```
fun findMessageByUsername(username: String): Mono<String> {
return Mono.just("Hi $username")
}
```
Below is a minimal method security configuration when using method security in reactive applications.
Java
```
@EnableReactiveMethodSecurity
public class SecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
UserDetails rob = userBuilder.username("rob")
.password("rob")
.roles("USER")
.build();
UserDetails admin = userBuilder.username("admin")
.password("admin")
.roles("USER","ADMIN")
.build();
return new MapReactiveUserDetailsService(rob, admin);
}
}
```
Kotlin
```
@EnableReactiveMethodSecurity
class SecurityConfig {
@Bean
fun userDetailsService(): MapReactiveUserDetailsService {
val userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()
val rob = userBuilder.username("rob")
.password("rob")
.roles("USER")
.build()
val admin = userBuilder.username("admin")
.password("admin")
.roles("USER", "ADMIN")
.build()
return MapReactiveUserDetailsService(rob, admin)
}
}
```
Consider the following class:
Java
```
@Component
public class HelloWorldMessageService {
@PreAuthorize("hasRole('ADMIN')")
public Mono<String> findMessage() {
return Mono.just("Hello World!");
}
}
```
Kotlin
```
@Component
class HelloWorldMessageService {
@PreAuthorize("hasRole('ADMIN')")
fun findMessage(): Mono<String> {
return Mono.just("Hello World!")
}
}
```
Or, the following class using Kotlin coroutines:
Kotlin
```
@Component
class HelloWorldMessageService {
@PreAuthorize("hasRole('ADMIN')")
suspend fun findMessage(): String {
delay(10)
return "Hello World!"
}
}
```
Combined with our configuration above, `@PreAuthorize("hasRole('ADMIN')")` will ensure that `findByMessage` is only invoked by a user with the role `ADMIN`.
It is important to note that any of the expressions in standard method security work for `@EnableReactiveMethodSecurity`.
However, at this time we only support return type of `Boolean` or `boolean` of the expression.
This means that the expression must not block.
When integrating with [WebFlux Security](../configuration/webflux.html#jc-webflux), the Reactor Context is automatically established by Spring Security according to the authenticated user.
Java
```
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
return http
// Demonstrate that method security works
// Best practice to use both for defense in depth
.authorizeExchange(exchanges -> exchanges
.anyExchange().permitAll()
)
.httpBasic(withDefaults())
.build();
}
@Bean
MapReactiveUserDetailsService userDetailsService() {
User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
UserDetails rob = userBuilder.username("rob")
.password("rob")
.roles("USER")
.build();
UserDetails admin = userBuilder.username("admin")
.password("admin")
.roles("USER","ADMIN")
.build();
return new MapReactiveUserDetailsService(rob, admin);
}
}
```
Kotlin
```
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig {
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, permitAll)
}
httpBasic { }
}
}
@Bean
fun userDetailsService(): MapReactiveUserDetailsService {
val userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()
val rob = userBuilder.username("rob")
.password("rob")
.roles("USER")
.build()
val admin = userBuilder.username("admin")
.password("admin")
.roles("USER", "ADMIN")
.build()
return MapReactiveUserDetailsService(rob, admin)
}
}
```
\ No newline at end of file
# WebFlux Security
Spring Security’s WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.
You can find a few sample applications that demonstrate the code below:
* Hello WebFlux [hellowebflux](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/webflux/java/hello-security)
* Hello WebFlux.Fn [hellowebfluxfn](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/webflux-fn/hello-security)
* Hello WebFlux Method [hellowebflux-method](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/webflux/java/method)
## Minimal WebFlux Security Configuration
You can find a minimal WebFlux Security configuration below:
Example 1. Minimal WebFlux Security Configuration
Java
```
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
```
Kotlin
```
@EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
}
```
This configuration provides form and http basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default log in page and a default log out page, sets up security related HTTP headers, CSRF protection, and more.
## Explicit WebFlux Security Configuration
You can find an explicit version of the minimal WebFlux Security configuration below:
Example 2. Explicit WebFlux Security Configuration
Java
```
@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges -> exchanges
.anyExchange().authenticated()
)
.httpBasic(withDefaults())
.formLogin(withDefaults());
return http.build();
}
}
```
Kotlin
```
@Configuration
@EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
@Bean
fun userDetailsService(): ReactiveUserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(userDetails)
}
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
formLogin { }
httpBasic { }
}
}
}
```
This configuration explicitly sets up all the same things as our minimal configuration.
From here you can easily make the changes to the defaults.
You can find more examples of explicit configuration in unit tests, by searching [EnableWebFluxSecurity in the `config/src/test/` directory](https://github.com/spring-projects/spring-security/search?q=path%3Aconfig%2Fsrc%2Ftest%2F+EnableWebFluxSecurity).
### Multiple Chains Support
You can configure multiple `SecurityWebFilterChain` instances to separate configuration by `RequestMatcher`s.
For example, you can isolate configuration for URLs that start with `/api`, like so:
Java
```
@Configuration
@EnableWebFluxSecurity
static class MultiSecurityHttpConfig {
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
@Bean
SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
http
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) (2)
.authorizeExchange((exchanges) -> exchanges
.anyExchange().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); (3)
return http.build();
}
@Bean
SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) { (4)
http
.authorizeExchange((exchanges) -> exchanges
.anyExchange().authenticated()
)
.httpBasic(withDefaults()); (5)
return http.build();
}
@Bean
ReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
PasswordEncodedUser.user(), PasswordEncodedUser.admin());
}
}
```
Kotlin
```
@Configuration
@EnableWebFluxSecurity
open class MultiSecurityHttpConfig {
@Order(Ordered.HIGHEST_PRECEDENCE) (1)
@Bean
open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
securityMatcher(PathPatternParserServerWebExchangeMatcher("/api/**")) (2)
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
jwt { } (3)
}
}
}
@Bean
open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain { (4)
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
httpBasic { } (5)
}
}
@Bean
open fun userDetailsService(): ReactiveUserDetailsService {
return MapReactiveUserDetailsService(
PasswordEncodedUser.user(), PasswordEncodedUser.admin()
)
}
}
```
|**1**| Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first |
|-----|------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`|
|**3**| Specify the authentication mechanisms that will be used for `/api/**` endpoints |
|**4**| Create another instance of `SecurityWebFilterChain` with lower precedence to match all other URLs |
|**5**| Specify the authentication mechanisms that will be used for the rest of the application |
Spring Security will select one `SecurityWebFilterChain` `@Bean` for each request.
It will match the requests in order by the `securityMatcher` definition.
In this case, that means that if the URL path starts with `/api`, then Spring Security will use `apiHttpSecurity`.
If the URL does not start with `/api` then Spring Security will default to `webHttpSecurity`, which has an implied `securityMatcher` that matches any request.
\ No newline at end of file
# Cross Site Request Forgery (CSRF) for WebFlux Environments
This section discusses Spring Security’s [Cross Site Request Forgery (CSRF)](../../features/exploits/csrf.html#csrf) support for WebFlux environments.
## Using Spring Security CSRF Protection
The steps to using Spring Security’s CSRF protection are outlined below:
* [Use proper HTTP verbs](#webflux-csrf-idempotent)
* [Configure CSRF Protection](#webflux-csrf-configure)
* [Include the CSRF Token](#webflux-csrf-include)
### Use proper HTTP verbs
The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs.
This is covered in detail in [Safe Methods Must be Idempotent](../../features/exploits/csrf.html#csrf-protection-idempotent).
### Configure CSRF Protection
The next step is to configure Spring Security’s CSRF protection within your application.
Spring Security’s CSRF protection is enabled by default, but you may need to customize the configuration.
Below are a few common customizations.
#### Custom CsrfTokenRepository
By default Spring Security stores the expected CSRF token in the `WebSession` using `WebSessionServerCsrfTokenRepository`.
There can be cases where users will want to configure a custom `ServerCsrfTokenRepository`.
For example, it might be desirable to persist the `CsrfToken` in a cookie to [support a JavaScript based application](#webflux-csrf-include-ajax-auto).
By default the `CookieServerCsrfTokenRepository` will write to a cookie named `XSRF-TOKEN` and read it from a header named `X-XSRF-TOKEN` or the HTTP parameter `_csrf`.
These defaults come from [AngularJS](https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection)
You can configure `CookieServerCsrfTokenRepository` in Java Configuration using:
Example 1. Store CSRF Token in a Cookie
Java
```
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
csrfTokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
}
}
}
```
| |The sample explicitly sets `cookieHttpOnly=false`.<br/>This is necessary to allow JavaScript (i.e. AngularJS) to read it.<br/>If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` (by using `new CookieServerCsrfTokenRepository()` instead) to improve security.|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Disable CSRF Protection
CSRF protection is enabled by default.
However, it is simple to disable CSRF protection if it [makes sense for your application](../../features/exploits/csrf.html#csrf-when).
The Java configuration below will disable CSRF protection.
Example 2. Disable CSRF Configuration
Java
```
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.disable()))
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
disable()
}
}
}
```
### Include the CSRF Token
In order for the [synchronizer token pattern](../../features/exploits/csrf.html#csrf-protection-stp) to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request.
This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser.
Spring Security’s [CsrfWebFilter](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/CsrfWebFilter.html) exposes a [Mono\<CsrfToken\>](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html) as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`.
This means that any view technology can access the `Mono<CsrfToken>` to expose the expected token as either a [form](#webflux-csrf-include-form-attr) or [meta tag](#webflux-csrf-include-ajax-meta).
If your view technology does not provide a simple way to subscribe to the `Mono<CsrfToken>`, a common pattern is to use Spring’s `@ControllerAdvice` to expose the `CsrfToken` directly.
For example, the following code will place the `CsrfToken` on the default attribute name (`_csrf`) used by Spring Security’s [CsrfRequestDataValueProcessor](#webflux-csrf-include-form-auto) to automatically include the CSRF token as a hidden input.
Example 3. `CsrfToken` as `@ModelAttribute`
Java
```
@ControllerAdvice
public class SecurityControllerAdvice {
@ModelAttribute
Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());
return csrfToken.doOnSuccess(token -> exchange.getAttributes()
.put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));
}
}
```
Kotlin
```
@ControllerAdvice
class SecurityControllerAdvice {
@ModelAttribute
fun csrfToken(exchange: ServerWebExchange): Mono<CsrfToken> {
val csrfToken: Mono<CsrfToken>? = exchange.getAttribute(CsrfToken::class.java.name)
return csrfToken!!.doOnSuccess { token ->
exchange.attributes[CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME] = token
}
}
}
```
Fortunately, Thymeleaf provides [integration](#webflux-csrf-include-form-auto) that works without any additional work.
#### Form URL Encoded
In order to post an HTML form the CSRF token must be included in the form as a hidden input.
For example, the rendered HTML might look like:
Example 4. CSRF Token HTML
```
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
```
Next we will discuss various ways of including the CSRF token in a form as a hidden input.
##### Automatic CSRF Token Inclusion
Spring Security’s CSRF support provides integration with Spring’s [RequestDataValueProcessor](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/result/view/RequestDataValueProcessor.html) via its [CsrfRequestDataValueProcessor](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.html).
In order for `CsrfRequestDataValueProcessor` to work, the `Mono<CsrfToken>` must be subscribed to and the `CsrfToken` must be [exposed as an attribute](#webflux-csrf-include-subscribe) that matches [DEFAULT\_CSRF\_ATTR\_NAME](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.html#DEFAULT_CSRF_ATTR_NAME).
Fortunately, Thymeleaf [provides support](https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor) to take care of all the boilerplate for you by integrating with `RequestDataValueProcessor` to ensure that forms that have an unsafe HTTP method (i.e. post) will automatically include the actual CSRF token.
##### CsrfToken Request Attribute
If the [other options](#webflux-csrf-include) for including the actual CSRF token in the request do not work, you can take advantage of the fact that the `Mono<CsrfToken>` [is exposed](#webflux-csrf-include) as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`.
The Thymeleaf sample below assumes that you [expose](#webflux-csrf-include-subscribe) the `CsrfToken` on an attribute named `_csrf`.
Example 5. CSRF Token in Form with Request Attribute
```
<form th:action="@{/logout}"
method="post">
<input type="submit"
value="Log out" />
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
</form>
```
#### Ajax and JSON Requests
If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter.
Instead you can submit the token within a HTTP header.
In the following sections we will discuss various ways of including the CSRF token as an HTTP request header in JavaScript based applications.
##### Automatic Inclusion
Spring Security can easily be [configured](#webflux-csrf-configure-custom-repository) to store the expected CSRF token in a cookie.
By storing the expected CSRF in a cookie, JavaScript frameworks like [AngularJS](https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection) will automatically include the actual CSRF token in the HTTP request headers.
##### Meta tags
An alternative pattern to [exposing the CSRF in a cookie](#webflux-csrf-include-form-auto) is to include the CSRF token within your `meta` tags.
The HTML might look something like this:
Example 6. CSRF meta tag HTML
```
<html>
<head>
<meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
<!-- ... -->
</head>
<!-- ... -->
```
Once the meta tags contained the CSRF token, the JavaScript code would read the meta tags and include the CSRF token as a header.
If you were using jQuery, this could be done with the following:
Example 7. AJAX send CSRF Token
```
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
```
The sample below assumes that you [expose](#webflux-csrf-include-subscribe) the `CsrfToken` on an attribute named `_csrf`.
An example of doing this with Thymeleaf is shown below:
Example 8. CSRF meta tag JSP
```
<html>
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->
```
## CSRF Considerations
There are a few special considerations to consider when implementing protection against CSRF attacks.
This section discusses those considerations as it pertains to WebFlux environments.
Refer to [CSRF Considerations](../../features/exploits/csrf.html#csrf-considerations) for a more general discussion.
### Logging In
It is important to [require CSRF for log in](../../features/exploits/csrf.html#csrf-considerations-login) requests to protect against forging log in attempts.
Spring Security’s WebFlux support does this out of the box.
### Logging Out
It is important to [require CSRF for log out](../../features/exploits/csrf.html#csrf-considerations-logout) requests to protect against forging log out attempts.
By default Spring Security’s `LogoutWebFilter` only processes HTTP post requests.
This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users.
The easiest approach is to use a form to log out.
If you really want a link, you can use JavaScript to have the link perform a POST (i.e. maybe on a hidden form).
For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST.
If you really want to use HTTP GET with logout you can do so, but remember this is generally not recommended.
For example, the following Java Configuration will perform logout with the URL `/logout` is requested with any HTTP method:
Example 9. Log out with HTTP GET
Java
```
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.logout(logout -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout")))
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
logout {
requiresLogout = PathPatternParserServerWebExchangeMatcher("/logout")
}
}
}
```
### CSRF and Session Timeouts
By default Spring Security stores the CSRF token in the `WebSession`.
This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against.
We’ve already discussed [general solutions](../../features/exploits/csrf.html#csrf-considerations-login) to session timeouts.
This section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support.
It is simple to change storage of the expected CSRF token to be in a cookie.
For details, refer to the [Custom CsrfTokenRepository](#webflux-csrf-configure-custom-repository) section.
###
We have [already discussed](../../features/exploits/csrf.html#csrf-considerations-multipart) how protecting multipart requests (file uploads) from CSRF attacks causes a [chicken and the egg](https://en.wikipedia.org/wiki/Chicken_or_the_egg) problem.
This section discusses how to implement placing the CSRF token in the [body](#webflux-csrf-considerations-multipart-body) and [url](#webflux-csrf-considerations-multipart-url) within a WebFlux application.
| |More information about using multipart forms with Spring can be found within the [Multipart Data](https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web-reactive.html#webflux-multipart) section of the Spring reference.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Place CSRF Token in the Body
We have [already discussed](../../features/exploits/csrf.html#csrf-considerations-multipart) the trade-offs of placing the CSRF token in the body.
In a WebFlux application, this can be configured with the following configuration:
Example 10. Enable obtaining CSRF token from multipart/form-data
Java
```
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true))
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
tokenFromMultipartDataEnabled = true
}
}
}
```
#### Include CSRF Token in URL
We have [already discussed](../../features/exploits/csrf.html#csrf-considerations-multipart) the trade-offs of placing the CSRF token in the URL.
Since the `CsrfToken` is exposed as an `ServerHttpRequest` [request attribute](#webflux-csrf-include), we can use that to create an `action` with the CSRF token in it.
An example with Thymeleaf is shown below:
Example 11. CSRF Token in Action
```
<form method="post"
th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"
enctype="multipart/form-data">
```
### HiddenHttpMethodFilter
We have [already discussed](../../features/exploits/csrf.html#csrf-considerations-override-method) overriding the HTTP method.
In a Spring WebFlux application, overriding the HTTP method is done using [HiddenHttpMethodFilter](https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html).
\ No newline at end of file
# Security HTTP Response Headers
[Security HTTP Response Headers](../../features/exploits/headers.html#headers) can be used to increase the security of web applications.
This section is dedicated to WebFlux based support for Security HTTP Response Headers.
## Default Security Headers
Spring Security provides a [default set of Security HTTP Response Headers](../../features/exploits/headers.html#headers-default) to provide secure defaults.
While each of these headers are considered best practice, it should be noted that not all clients utilize the headers, so additional testing is encouraged.
You can customize specific headers.
For example, assume that you want the defaults except you wish to specify `SAMEORIGIN` for [X-Frame-Options](../../servlet/exploits/headers.html#servlet-headers-frame-options).
You can easily do this with the following Configuration:
Example 1. Customize Default Security Headers
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions
.mode(Mode.SAMEORIGIN)
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
frameOptions {
mode = Mode.SAMEORIGIN
}
}
}
}
```
If you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults.
An example is provided below:
Example 2. Disable HTTP Security Response Headers
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers.disable());
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
disable()
}
}
}
```
## Cache Control
Spring Security includes [Cache Control](../../features/exploits/headers.html#headers-cache-control) headers by default.
However, if you actually want to cache specific responses, your application can selectively add them to the [ServerHttpResponse](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/server/reactive/ServerHttpResponse.html) to override the header set by Spring Security.
This is useful to ensure things like CSS, JavaScript, and images are properly cached.
When using Spring WebFlux, this is typically done within your configuration.
Details on how to do this can be found in the [Static Resources](https://docs.spring.io/spring/docs/5.0.0.RELEASE/spring-framework-reference/web-reactive.html#webflux-config-static-resources) portion of the Spring Reference documentation
If necessary, you can also disable Spring Security’s cache control HTTP response headers.
Example 3. Cache Control Disabled
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.cache(cache -> cache.disable())
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
cache {
disable()
}
}
}
}
```
## Content Type Options
Spring Security includes [Content-Type](../../features/exploits/headers.html#headers-content-type-options) headers by default.
However, you can disable it with:
Example 4. Content Type Options Disabled
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
contentTypeOptions {
disable()
}
}
}
}
```
##
Spring Security provides the [Strict Transport Security](../../features/exploits/headers.html#headers-hsts) header by default.
However, you can customize the results explicitly.
For example, the following is an example of explicitly providing HSTS:
Example 5. Strict Transport Security
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.hsts(hsts -> hsts
.includeSubdomains(true)
.preload(true)
.maxAge(Duration.ofDays(365))
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
hsts {
includeSubdomains = true
preload = true
maxAge = Duration.ofDays(365)
}
}
}
}
```
## X-Frame-Options
By default, Spring Security disables rendering within an iframe using [X-Frame-Options](../../features/exploits/headers.html#headers-frame-options).
You can customize frame options to use the same origin using the following:
Example 6. X-Frame-Options: SAMEORIGIN
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions
.mode(SAMEORIGIN)
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
frameOptions {
mode = SAMEORIGIN
}
}
}
}
```
## X-XSS-Protection
By default, Spring Security instructs browsers to block reflected XSS attacks using the \<\<headers-xss-protection,X-XSS-Protection header\>.
You can disable `X-XSS-Protection` with the following Configuration:
Example 7. X-XSS-Protection Customization
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.xssProtection(xssProtection -> xssProtection.disable())
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
xssProtection {
disable()
}
}
}
}
```
##
Spring Security does not add [Content Security Policy](../../features/exploits/headers.html#headers-csp) by default, because a reasonable default is impossible to know without context of the application.
The web application author must declare the security policy(s) to enforce and/or monitor for the protected resources.
For example, given the following security policy:
Example 8. Content Security Policy Example
```
Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
```
You can enable the CSP header as shown below:
Example 9. Content Security Policy
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.contentSecurityPolicy(policy -> policy
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
contentSecurityPolicy {
policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
}
}
}
}
```
To enable the CSP `report-only` header, provide the following configuration:
Example 10. Content Security Policy Report Only
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.contentSecurityPolicy(policy -> policy
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
.reportOnly()
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
contentSecurityPolicy {
policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
reportOnly = true
}
}
}
}
```
## Referrer Policy
Spring Security does not add [Referrer Policy](../../features/exploits/headers.html#headers-referrer) headers by default.
You can enable the Referrer Policy header using configuration as shown below:
Example 11. Referrer Policy Configuration
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.referrerPolicy(referrer -> referrer
.policy(ReferrerPolicy.SAME_ORIGIN)
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
referrerPolicy {
policy = ReferrerPolicy.SAME_ORIGIN
}
}
}
}
```
## Feature Policy
Spring Security does not add [Feature Policy](../../features/exploits/headers.html#headers-feature) headers by default.
The following `Feature-Policy` header:
Example 12. Feature-Policy Example
```
Feature-Policy: geolocation 'self'
```
You can enable the Feature Policy header as shown below:
Example 13. Feature-Policy Configuration
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.featurePolicy("geolocation 'self'")
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
featurePolicy("geolocation 'self'")
}
}
}
```
## Permissions Policy
Spring Security does not add [Permissions Policy](../../features/exploits/headers.html#headers-permissions) headers by default.
The following `Permissions-Policy` header:
Example 14. Permissions-Policy Example
```
Permissions-Policy: geolocation=(self)
```
You can enable the Permissions Policy header as shown below:
Example 15. Permissions-Policy Configuration
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.permissionsPolicy(permissions -> permissions
.policy("geolocation=(self)")
)
);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
permissionsPolicy {
policy = "geolocation=(self)"
}
}
}
}
```
## Clear Site Data
Spring Security does not add [Clear-Site-Data](../../features/exploits/headers.html#headers-clear-site-data) headers by default.
The following Clear-Site-Data header:
Example 16. Clear-Site-Data Example
```
Clear-Site-Data: "cache", "cookies"
```
can be sent on log out with the following configuration:
Example 17. Clear-Site-Data Configuration
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
ServerLogoutHandler securityContext = new SecurityContextServerLogoutHandler();
ClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(CACHE, COOKIES);
ServerLogoutHandler clearSiteData = new HeaderWriterServerLogoutHandler(writer);
DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(securityContext, clearSiteData);
http
// ...
.logout()
.logoutHandler(logoutHandler);
return http.build();
}
```
Kotlin
```
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val securityContext: ServerLogoutHandler = SecurityContextServerLogoutHandler()
val writer = ClearSiteDataServerHttpHeadersWriter(CACHE, COOKIES)
val clearSiteData: ServerLogoutHandler = HeaderWriterServerLogoutHandler(writer)
val customLogoutHandler = DelegatingServerLogoutHandler(securityContext, clearSiteData)
return http {
// ...
logout {
logoutHandler = customLogoutHandler
}
}
}
```
\ No newline at end of file
# HTTP
All HTTP based communication should be protected [using TLS](../../features/exploits/http.html#http).
Below you can find details around WebFlux specific features that assist with HTTPS usage.
## Redirect to HTTPS
If a client makes a request using HTTP rather than HTTPS, Spring Security can be configured to redirect to HTTPS.
For example, the following Java configuration will redirect any HTTP requests to HTTPS:
Example 1. Redirect to HTTPS
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.redirectToHttps(withDefaults());
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
redirectToHttps { }
}
}
```
The configuration can easily be wrapped around an if statement to only be turned on in production.
Alternatively, it can be enabled by looking for a property about the request that only happens in production.
For example, if the production environment adds a header named `X-Forwarded-Proto` the following Java Configuration could be used:
Example 2. Redirect to HTTPS when X-Forwarded
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.redirectToHttps(redirect -> redirect
.httpsRedirectWhen(e -> e.getRequest().getHeaders().containsKey("X-Forwarded-Proto"))
);
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
redirectToHttps {
httpsRedirectWhen {
it.request.headers.containsKey("X-Forwarded-Proto")
}
}
}
}
```
## Strict Transport Security
Spring Security provides support for [Strict Transport Security](../../servlet/exploits/headers.html#servlet-headers-hsts) and enables it by default.
## Proxy Server Configuration
Spring Security [integrates with proxy servers](../../features/exploits/http.html#http-proxy-server).
\ No newline at end of file
# Protection Against Exploits
Spring Security provides protection against numerous exploits.
This section discusses WebFlux specific support for:
* [CSRF](csrf.html)
* [Headers](headers.html)
* [HTTP Requests](http.html)
\ No newline at end of file
# Getting Started with WebFlux Applications
This section covers the minimum setup for how to use Spring Security with Spring Boot in a reactive application.
| |The completed application can be found [in our samples repository](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/webflux/java/hello-security).<br/>For your convenience, you can download a minimal Reactive Spring Boot + Spring Security application by [clicking here](https://start.spring.io/starter.zip?type=maven-project&language=java&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=webflux,security).|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## Updating Dependencies
You can add Spring Security to your Spring Boot project by adding `spring-boot-starter-security`.
Maven
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
```
Gradle
```
implementation 'org.springframework.boot:spring-boot-starter-security'
```
## Starting Hello Spring Security Boot
You can now [run the Spring Boot application](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-running-with-the-maven-plugin) by using the Maven Plugin’s `run` goal.
The following example shows how to do so (and the beginning of the output from doing so):
Example 1. Running Spring Boot Application
Maven
```
$ ./mvnw spring-boot:run
...
INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336
...
```
Gradle
```
$ ./gradlew bootRun
...
INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336
...
```
## Authenticating
You can access the application at [http://localhost:8080/](http://localhost:8080/) which will redirect the browser to the default log in page. You can provide the default username of `user` with the randomly generated password that is logged to the console. The browser is then taken to the orginally requested page.
To log out you can visit [http://localhost:8080/logout](http://localhost:8080/logout) and then confirming you wish to log out.
## Spring Boot Auto Configuration
Spring Boot automatically adds Spring Security which requires all requests be authenticated. It also generates a user with a randomly generated password that is logged to the console which can be used to authenticate using form or basic authentication.
[Reactive Applications](index.html)[X.509 Authentication](authentication/x509.html)
\ No newline at end of file
# CORS
Spring Framework provides [first class support for CORS](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-cors-intro).
CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the `JSESSIONID`).
If the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it.
The easiest way to ensure that CORS is handled first is to use the `CorsWebFilter`.
Users can integrate the `CorsWebFilter` with Spring Security by providing a `CorsConfigurationSource`.
For example, the following will integrate CORS support within Spring Security:
Java
```
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
```
Kotlin
```
@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = listOf("https://example.com")
configuration.allowedMethods = listOf("GET", "POST")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", configuration)
return source
}
```
The following will disable the CORS integration within Spring Security:
Java
```
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.cors(cors -> cors.disable());
return http.build();
}
```
Kotlin
```
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
cors {
disable()
}
}
}
```
[HTTP Requests](../exploits/http.html)[RSocket](rsocket.html)
\ No newline at end of file
# RSocket Security
Spring Security’s RSocket support relies on a `SocketAcceptorInterceptor`.
The main entry point into security is found in the `PayloadSocketAcceptorInterceptor` which adapts the RSocket APIs to allow intercepting a `PayloadExchange` with `PayloadInterceptor` implementations.
You can find a few sample applications that demonstrate the code below:
* Hello RSocket [hellorsocket](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/rsocket/hello-security)
* [Spring Flights](https://github.com/rwinch/spring-flights/tree/security)
## Minimal RSocket Security Configuration
You can find a minimal RSocket Security configuration below:
Java
```
@Configuration
@EnableRSocketSecurity
public class HelloRSocketSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
```
Kotlin
```
@Configuration
@EnableRSocketSecurity
open class HelloRSocketSecurityConfig {
@Bean
open fun userDetailsService(): MapReactiveUserDetailsService {
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}
}
```
This configuration enables [simple authentication](#rsocket-authentication-simple) and sets up [rsocket-authorization](#rsocket-authorization) to require an authenticated user for any request.
## Adding SecuritySocketAcceptorInterceptor
For Spring Security to work we need to apply `SecuritySocketAcceptorInterceptor` to the `ServerRSocketFactory`.
This is what connects our `PayloadSocketAcceptorInterceptor` we created with the RSocket infrastructure.
In a Spring Boot application this is done automatically using `RSocketSecurityAutoConfiguration` with the following code.
```
@Bean
RSocketServerCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {
return (server) -> server.interceptors((registry) -> registry.forSocketAcceptor(interceptor));
}
```
## RSocket Authentication
RSocket authentication is performed with `AuthenticationPayloadInterceptor` which acts as a controller to invoke a `ReactiveAuthenticationManager` instance.
### Authentication at Setup vs Request Time
Generally, authentication can occur at setup time and/or request time.
Authentication at setup time makes sense in a few scenarios.
A common scenarios is when a single user (i.e. mobile connection) is leveraging an RSocket connection.
In this case only a single user is leveraging the connection, so authentication can be done once at connection time.
In a scenario where the RSocket connection is shared it makes sense to send credentials on each request.
For example, a web application that connects to an RSocket server as a downstream service would make a single connection that all users leverage.
In this case, if the RSocket server needs to perform authorization based on the web application’s users credentials per request makes sense.
In some scenarios authentication at setup and per request makes sense.
Consider a web application as described previously.
If we need to restrict the connection to the web application itself, we can provide a credential with a `SETUP` authority at connection time.
Then each user would have different authorities but not the `SETUP` authority.
This means that individual users can make requests but not make additional connections.
### Simple Authentication
Spring Security has support for [Simple Authentication Metadata Extension](https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md).
| |Basic Authentication drafts evolved into Simple Authentication and is only supported for backward compatibility.<br/>See `RSocketSecurity.basicAuthentication(Customizer)` for setting it up.|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
The RSocket receiver can decode the credentials using `AuthenticationPayloadExchangeConverter` which is automatically setup using the `simpleAuthentication` portion of the DSL.
An explicit configuration can be found below.
Java
```
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.simpleAuthentication(Customizer.withDefaults());
return rsocket.build();
}
```
Kotlin
```
@Bean
open fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
rsocket
.authorizePayload { authorize -> authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
}
.simpleAuthentication(withDefaults())
return rsocket.build()
}
```
The RSocket sender can send credentials using `SimpleAuthenticationEncoder` which can be added to Spring’s `RSocketStrategies`.
Java
```
RSocketStrategies.Builder strategies = ...;
strategies.encoder(new SimpleAuthenticationEncoder());
```
Kotlin
```
var strategies: RSocketStrategies.Builder = ...
strategies.encoder(SimpleAuthenticationEncoder())
```
It can then be used to send a username and password to the receiver in the setup:
Java
```
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
Mono<RSocketRequester> requester = RSocketRequester.builder()
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(strategies.build())
.connectTcp(host, port);
```
Kotlin
```
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
val credentials = UsernamePasswordMetadata("user", "password")
val requester: Mono<RSocketRequester> = RSocketRequester.builder()
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(strategies.build())
.connectTcp(host, port)
```
Alternatively or additionally, a username and password can be sent in a request.
Java
```
Mono<RSocketRequester> requester;
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
public Mono<AirportLocation> findRadar(String code) {
return this.requester.flatMap(req ->
req.route("find.radar.{code}", code)
.metadata(credentials, authenticationMimeType)
.retrieveMono(AirportLocation.class)
);
}
```
Kotlin
```
import org.springframework.messaging.rsocket.retrieveMono
// ...
var requester: Mono<RSocketRequester>? = null
var credentials = UsernamePasswordMetadata("user", "password")
open fun findRadar(code: String): Mono<AirportLocation> {
return requester!!.flatMap { req ->
req.route("find.radar.{code}", code)
.metadata(credentials, authenticationMimeType)
.retrieveMono<AirportLocation>()
}
}
```
### JWT
Spring Security has support for [Bearer Token Authentication Metadata Extension](https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Bearer.md).
The support comes in the form of authenticating a JWT (determining the JWT is valid) and then using the JWT to make authorization decisions.
The RSocket receiver can decode the credentials using `BearerPayloadExchangeConverter` which is automatically setup using the `jwt` portion of the DSL.
An example configuration can be found below:
Java
```
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.jwt(Customizer.withDefaults());
return rsocket.build();
}
```
Kotlin
```
@Bean
fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {
rsocket
.authorizePayload { authorize -> authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
}
.jwt(withDefaults())
return rsocket.build()
}
```
The configuration above relies on the existence of a `ReactiveJwtDecoder` `@Bean` being present.
An example of creating one from the issuer can be found below:
Java
```
@Bean
ReactiveJwtDecoder jwtDecoder() {
return ReactiveJwtDecoders
.fromIssuerLocation("https://example.com/auth/realms/demo");
}
```
Kotlin
```
@Bean
fun jwtDecoder(): ReactiveJwtDecoder {
return ReactiveJwtDecoders
.fromIssuerLocation("https://example.com/auth/realms/demo")
}
```
The RSocket sender does not need to do anything special to send the token because the value is just a simple String.
For example, the token can be sent at setup time:
Java
```
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
BearerTokenMetadata token = ...;
Mono<RSocketRequester> requester = RSocketRequester.builder()
.setupMetadata(token, authenticationMimeType)
.connectTcp(host, port);
```
Kotlin
```
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
val token: BearerTokenMetadata = ...
val requester = RSocketRequester.builder()
.setupMetadata(token, authenticationMimeType)
.connectTcp(host, port)
```
Alternatively or additionally, the token can be sent in a request.
Java
```
MimeType authenticationMimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
Mono<RSocketRequester> requester;
BearerTokenMetadata token = ...;
public Mono<AirportLocation> findRadar(String code) {
return this.requester.flatMap(req ->
req.route("find.radar.{code}", code)
.metadata(token, authenticationMimeType)
.retrieveMono(AirportLocation.class)
);
}
```
Kotlin
```
val authenticationMimeType: MimeType =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)
var requester: Mono<RSocketRequester>? = null
val token: BearerTokenMetadata = ...
open fun findRadar(code: String): Mono<AirportLocation> {
return this.requester!!.flatMap { req ->
req.route("find.radar.{code}", code)
.metadata(token, authenticationMimeType)
.retrieveMono<AirportLocation>()
}
}
```
## RSocket Authorization
RSocket authorization is performed with `AuthorizationPayloadInterceptor` which acts as a controller to invoke a `ReactiveAuthorizationManager` instance.
The DSL can be used to setup authorization rules based upon the `PayloadExchange`.
An example configuration can be found below:
Java
```
rsocket
.authorizePayload(authz ->
authz
.setup().hasRole("SETUP") (1)
.route("fetch.profile.me").authenticated() (2)
.matcher(payloadExchange -> isMatch(payloadExchange)) (3)
.hasRole("CUSTOM")
.route("fetch.profile.{username}") (4)
.access((authentication, context) -> checkFriends(authentication, context))
.anyRequest().authenticated() (5)
.anyExchange().permitAll() (6)
);
```
Kotlin
```
rsocket
.authorizePayload { authz ->
authz
.setup().hasRole("SETUP") (1)
.route("fetch.profile.me").authenticated() (2)
.matcher { payloadExchange -> isMatch(payloadExchange) } (3)
.hasRole("CUSTOM")
.route("fetch.profile.{username}") (4)
.access { authentication, context -> checkFriends(authentication, context) }
.anyRequest().authenticated() (5)
.anyExchange().permitAll()
} (6)
```
|**1**| Setting up a connection requires the authority `ROLE_SETUP` |
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**| If the route is `fetch.profile.me` authorization only requires the user be authenticated |
|**3**| In this rule we setup a custom matcher where authorization requires the user to have the authority `ROLE_CUSTOM` |
|**4**|This rule leverages custom authorization.<br/>The matcher expresses a variable with the name `username` that is made available in the `context`.<br/>A custom authorization rule is exposed in the `checkFriends` method.|
|**5**| This rule ensures that request that does not already have a rule will require the user to be authenticated.<br/>A request is where the metadata is included.<br/>It would not include additional payloads. |
|**6**| This rule ensures that any exchange that does not already have a rule is allowed for anyone.<br/>In this example, it means that payloads that have no metadata have no authorization rules. |
It is important to understand that authorization rules are performed in order.
Only the first authorization rule that matches will be invoked.
[CORS](cors.html)[Testing](../test/index.html)
\ No newline at end of file
# Authorized Clients
## Resolving an Authorized Client
The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.
Java
```
@Controller
public class OAuth2ClientController {
@GetMapping("/")
public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
return Mono.just(authorizedClient.getAccessToken())
...
.thenReturn("index");
}
}
```
Kotlin
```
@Controller
class OAuth2ClientController {
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
return Mono.just(authorizedClient.accessToken)
...
.thenReturn("index")
}
}
```
The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses a [ReactiveOAuth2AuthorizedClientManager](#oauth2Client-authorized-manager-provider) and therefore inherits it’s capabilities.
## WebClient integration for Reactive Environments
The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
The `ServerOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
It directly uses an [ReactiveOAuth2AuthorizedClientManager](#oauth2Client-authorized-manager-provider) and therefore inherits the following capabilities:
* An `OAuth2AccessToken` will be requested if the client has not yet been authorized.
* `authorization_code` - triggers the Authorization Request redirect to initiate the flow
* `client_credentials` - the access token is obtained directly from the Token Endpoint
* `password` - the access token is obtained directly from the Token Endpoint
* If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
Java
```
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(oauth2Client)
.build();
}
```
Kotlin
```
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
return WebClient.builder()
.filter(oauth2Client)
.build()
}
```
### Providing the Authorized Client
The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
Java
```
@GetMapping("/")
public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
```
Kotlin
```
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
val resourceUri: String = ...
return webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono<String>()
...
.thenReturn("index")
}
```
|**1**|`oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.|
|-----|--------------------------------------------------------------------------------------------------------|
The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
Java
```
@GetMapping("/")
public Mono<String> index() {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
```
Kotlin
```
@GetMapping("/")
fun index(): Mono<String> {
val resourceUri: String = ...
return webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono<String>()
...
.thenReturn("index")
}
```
|**1**|`clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.|
|-----|------------------------------------------------------------------------------------------------------|
### Defaulting the Authorized Client
If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServerOAuth2AuthorizedClientExchangeFilterFunction` can determine the *default* client to use depending on it’s configuration.
If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
The following code shows the specific configuration:
Java
```
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.filter(oauth2Client)
.build();
}
```
Kotlin
```
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultOAuth2AuthorizedClient(true)
return WebClient.builder()
.filter(oauth2Client)
.build()
}
```
| |It is recommended to be cautious with this feature since all HTTP requests will receive the access token.|
|---|---------------------------------------------------------------------------------------------------------|
Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
The following code shows the specific configuration:
Java
```
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("okta");
return WebClient.builder()
.filter(oauth2Client)
.build();
}
```
Kotlin
```
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultClientRegistrationId("okta")
return WebClient.builder()
.filter(oauth2Client)
.build()
}
```
| |It is recommended to be cautious with this feature since all HTTP requests will receive the access token.|
|---|---------------------------------------------------------------------------------------------------------|
[OAuth2 Client Authentication](client-authentication.html)[OAuth2 Resource Server](../resource-server/index.html)
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
# OAuth 2.0 Login
The OAuth 2.0 Login feature provides an application with the capability to have users log in to the application by using their existing account at an OAuth 2.0 Provider (e.g. GitHub) or OpenID Connect 1.0 Provider (such as Google).
OAuth 2.0 Login implements the use cases: "Login with Google" or "Login with GitHub".
| |OAuth 2.0 Login is implemented by using the **Authorization Code Grant**, as specified in the [OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749#section-4.1) and [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth).|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## Section Summary
* [Core Configuration](core.html)
* [Advanced Configuration](advanced.html)
[OAuth2](../index.html)[Core Configuration](core.html)
# OAuth 2.0 Resource Server
Spring Security supports protecting endpoints using two forms of OAuth 2.0 [Bearer Tokens](https://tools.ietf.org/html/rfc6750.html):
* [JWT](https://tools.ietf.org/html/rfc7519)
* Opaque Tokens
This is handy in circumstances where an application has delegated its authority management to an [authorization server](https://tools.ietf.org/html/rfc6749) (for example, Okta or Ping Identity).
This authorization server can be consulted by resource servers to authorize requests.
| |A complete working example for [**JWTs**](https://github.com/spring-projects/spring-security-samples/tree/5.6.x/reactive/webflux/java/oauth2/resource-server) is available in the [Spring Security repository](https://github.com/spring-projects/spring-security-samples/tree/5.6.x).|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
[OAuth2 Authorized Clients](../client/authorized-clients.html)[JWT](jwt.html)
# OAuth2 WebFlux
Spring Security provides OAuth2 and WebFlux integration for reactive applications.
* [OAuth2 Log In](login/index.html) - Authenticating with an OAuth2 or OpenID Connect 1.0 Provider
* [OAuth2 Client](client/index.html) - Making requests to an OAuth2 Resource Server
* [OAuth2 Resource Server](resource-server/index.html) - Protecting a REST endpoint using OAuth2
[EnableReactiveMethodSecurity](../authorization/method.html)[OAuth2 Log In](login/index.html)
此差异已折叠。
# Testing with CSRF
Spring Security also provides support for CSRF testing with `WebTestClient`.
For example:
Java
```
this.rest
// provide a valid CSRF token
.mutateWith(csrf())
.post()
.uri("/login")
...
```
Kotlin
```
this.rest
// provide a valid CSRF token
.mutateWith(csrf())
.post()
.uri("/login")
...
```
[Testing Authentication](authentication.html)[Testing OAuth 2.0](oauth2.html)
此差异已折叠。
此差异已折叠。
# Testing Web Security
In this section, we’ll talk about testing web application endpoints.
## Section Summary
* [WebTestClient Setup](setup.html)
* [Testing Authentication](authentication.html)
* [Testing CSRF](csrf.html)
* [Testing OAuth 2.0](oauth2.html)
[Testing Method Security](../method.html)[WebTestClient Setup](setup.html)
此差异已折叠。
# Reactive Applications
Reactive applications work very differently than [Servlet Applications](../servlet/index.html#servlet-applications).
This section discusses how Spring Security works with reactive applications which are typically written using Spring’s WebFlux.
[FAQ](../servlet/appendix/faq.html)[Getting Started](getting-started.html)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册