19 changed files with 1475 additions and 51 deletions
@ -0,0 +1,248 @@
@@ -0,0 +1,248 @@
|
||||
[[servlet-architecture]] |
||||
= Servlet Security: The Big Picture |
||||
:figures: servlet/architecture |
||||
|
||||
This section discusses Spring Security's high level architecture within Servlet based applications. |
||||
We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference. |
||||
// FIXME: Add links to other sections of architecture |
||||
|
||||
[[servlet-filters-review]] |
||||
== A Review of ``Filter``s |
||||
|
||||
Spring Security's Servlet support is based on Servlet ``Filter``s, so it is helpful to look at the role of ``Filter``s generally first. |
||||
The picture below shows the typical layering of the handlers for a single HTTP request. |
||||
|
||||
.FilterChain |
||||
[[servlet-filterchain-figure]] |
||||
image::{figures}/filterchain.png[] |
||||
|
||||
The client sends a request to the application, and the container creates a `FilterChain` which contains the ``Filter``s and `Servlet` that should process the `HttpServletRequest` based on the path of the request URI. |
||||
In a Spring MVC application the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`]. |
||||
At most one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`. |
||||
However, more than one `Filter` can be used to: |
||||
|
||||
* Prevent downstream ``Filter``s or the `Servlet` from being invoked. |
||||
In this instance the `Filter` will typically write the `HttpServletResponse`. |
||||
* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet` |
||||
|
||||
The power of the `Filter` comes from the `FilterChain` that is passed into it. |
||||
|
||||
.`FilterChain` Usage Example |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { |
||||
// do something before the rest of the application |
||||
chain.doFilter(request, response); // invoke the rest of the application |
||||
// do something after the rest of the application |
||||
} |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { |
||||
// do something before the rest of the application |
||||
chain.doFilter(request, response) // invoke the rest of the application |
||||
// do something after the rest of the application |
||||
} |
||||
---- |
||||
==== |
||||
|
||||
Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important. |
||||
|
||||
|
||||
[[servlet-delegatingfilterproxy]] |
||||
== DelegatingFilterProxy |
||||
|
||||
Spring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`. |
||||
The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans. |
||||
`DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`. |
||||
|
||||
Here is a picture of how `DelegatingFilterProxy` fits into the <<servlet-filters-review,``Filter``s and the `FilterChain`>>. |
||||
|
||||
.DelegatingFilterProxy |
||||
[[servlet-delegatingfilterproxy-figure]] |
||||
image::{figures}/delegatingfilterproxy.png[] |
||||
|
||||
`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__. |
||||
The pseudo code of `DelegatingFilterProxy` can be seen below. |
||||
|
||||
.`DelegatingFilterProxy` Pseudo Code |
||||
==== |
||||
.Java |
||||
[source,java,role="primary",subs="+quotes,+macros"] |
||||
---- |
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { |
||||
// Lazily get Filter that was registered as a Spring Bean |
||||
// For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__ |
||||
Filter delegate = getFilterBean(someBeanName); |
||||
// delegate work to the Spring Bean |
||||
delegate.doFilter(request, response); |
||||
} |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary",subs="+quotes,+macros"] |
||||
---- |
||||
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { |
||||
// Lazily get Filter that was registered as a Spring Bean |
||||
// For the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__ |
||||
val delegate: Filter = getFilterBean(someBeanName) |
||||
// delegate work to the Spring Bean |
||||
delegate.doFilter(request, response) |
||||
} |
||||
---- |
||||
==== |
||||
|
||||
Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances. |
||||
This is important because the container needs to register the `Filter` instances before the container can startup. |
||||
However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans which will not be done until after the `Filter` instances need to be registered. |
||||
|
||||
[[servlet-filterchainproxy]] |
||||
== FilterChainProxy |
||||
|
||||
Spring Security's Servlet support is contained within `FilterChainProxy`. |
||||
`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <<servlet-securityfilterchain,`SecurityFilterChain`>>. |
||||
Since `FilterChainProxy` is a Bean, it is typically wrapped in a <<servlet-delegatingfilterproxy>>. |
||||
|
||||
.FilterChainProxy |
||||
[[servlet-filterchainproxy-figure]] |
||||
image::{figures}/filterchainproxy.png[] |
||||
|
||||
[[servlet-securityfilterchain]] |
||||
== SecurityFilterChain |
||||
|
||||
{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <<servlet-filterchainproxy>> to determine which Spring Security ``Filter``s should be invoked for this request. |
||||
|
||||
.SecurityFilterChain |
||||
[[servlet-securityfilterchain-figure]] |
||||
image::{figures}/securityfilterchain.png[] |
||||
|
||||
The <<servlet-security-filters,Security Filters>> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <<servlet-delegatingfilterproxy>>. |
||||
`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <<servlet-delegatingfilterproxy>>. |
||||
First, it provides a starting point for all of Spring Security's Servlet support. |
||||
For that reason, if you are attempting to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start. |
||||
|
||||
Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. |
||||
// FIXME: Add a link to SecurityContext |
||||
For example, it clears out the `SecurityContext` to avoid memory leaks. |
||||
It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks. |
||||
|
||||
In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. |
||||
In a Servlet container, ``Filter``s are invoked based upon the URL alone. |
||||
// FIXME: Link to RequestMatcher |
||||
However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface. |
||||
|
||||
In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used. |
||||
This allows providing a totally separate configuration for different _slices_ of your application. |
||||
|
||||
.Multiple SecurityFilterChain |
||||
[[servlet-multi-securityfilterchain-figure]] |
||||
image::{figures}/multi-securityfilterchain.png[] |
||||
|
||||
In the <<servlet-multi-securityfilterchain-figure>> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used. |
||||
Only the first `SecurityFilterChain` that matches will be invoked. |
||||
If a URL of `/api/messages/` is requested, it will first match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so only `SecurityFilterChain~0~` will be invoked even though it also matches on ``SecurityFilterChain~n~``. |
||||
If a URL of `/messages/` is requested, it will not match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so `FilterChainProxy` will continue trying each `SecurityFilterChain`. |
||||
Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked. |
||||
// FIXME add link to pattern matching |
||||
|
||||
Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured. |
||||
However, `SecurityFilterChain~n~` has four security ``Filter``s configured. |
||||
It is important to note that each `SecurityFilterChain` can be unique and configured in isolation. |
||||
In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests. |
||||
// FIXME: add link to configuring multiple `SecurityFilterChain` instances |
||||
|
||||
[[servlet-security-filters]] |
||||
== Security Filters |
||||
|
||||
The Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API. |
||||
The <<servlet-filters-review,order of ``Filter``>>s matters. |
||||
It is typically not necessary to know the ordering of Spring Security's ``Filter``s. |
||||
However, there are times that it is beneficial to know the ordering |
||||
|
||||
Below is a comprehensive list of Spring Security Filter ordering: |
||||
|
||||
* ChannelProcessingFilter |
||||
* WebAsyncManagerIntegrationFilter |
||||
* SecurityContextPersistenceFilter |
||||
* HeaderWriterFilter |
||||
* CorsFilter |
||||
* CsrfFilter |
||||
* LogoutFilter |
||||
* OAuth2AuthorizationRequestRedirectFilter |
||||
* Saml2WebSsoAuthenticationRequestFilter |
||||
* X509AuthenticationFilter |
||||
* AbstractPreAuthenticatedProcessingFilter |
||||
* CasAuthenticationFilter |
||||
* OAuth2LoginAuthenticationFilter |
||||
* Saml2WebSsoAuthenticationFilter |
||||
* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] |
||||
* OpenIDAuthenticationFilter |
||||
* DefaultLoginPageGeneratingFilter |
||||
* DefaultLogoutPageGeneratingFilter |
||||
* ConcurrentSessionFilter |
||||
* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] |
||||
* BearerTokenAuthenticationFilter |
||||
* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] |
||||
* RequestCacheAwareFilter |
||||
* SecurityContextHolderAwareRequestFilter |
||||
* JaasApiIntegrationFilter |
||||
* RememberMeAuthenticationFilter |
||||
* AnonymousAuthenticationFilter |
||||
* OAuth2AuthorizationCodeGrantFilter |
||||
* SessionManagementFilter |
||||
* <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> |
||||
* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] |
||||
* SwitchUserFilter |
||||
|
||||
[[servlet-exceptiontranslationfilter]] |
||||
== Handling Security Exceptions |
||||
|
||||
|
||||
The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses. |
||||
|
||||
`ExceptionTranslationFilter` is inserted into the <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>. |
||||
|
||||
image::{figures}/exceptiontranslationfilter.png[] |
||||
|
||||
|
||||
* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. |
||||
* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. |
||||
** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out |
||||
** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. |
||||
When the user successfully authenticates, the `RequestCache` is used to replay the original request. |
||||
// FIXME: add link to authentication success |
||||
** The `AuthenticationEntryPoint` is used to request credentials from the client. |
||||
For example, it might redirect to a log in page or send a `WWW-Authenticate` header. |
||||
// FIXME: link to AuthenticationEntryPoint |
||||
* image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__. |
||||
The `AccessDeniedHandler` is invoked to handle access denied. |
||||
// FIXME: link to AccessDeniedHandler |
||||
|
||||
[NOTE] |
||||
==== |
||||
If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything. |
||||
==== |
||||
|
||||
The pseudocode for `ExceptionTranslationFilter` looks something like this: |
||||
|
||||
.ExceptionTranslationFilter pseudocode |
||||
[source,java] |
||||
---- |
||||
try { |
||||
filterChain.doFilter(request, response); // <1> |
||||
} catch (AccessDeniedException | AuthenticationException ex) { |
||||
if (!authenticated || ex instanceof AuthenticationException) { |
||||
startAuthentication(); // <2> |
||||
} else { |
||||
accessDenied(); // <3> |
||||
} |
||||
} |
||||
---- |
||||
<1> You will recall from <<servlet-filters-review>> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. |
||||
This means that if another part of the application, (i.e. xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. |
||||
<2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. |
||||
<3> Otherwise, __Access Denied__ |
||||
@ -0,0 +1,264 @@
@@ -0,0 +1,264 @@
|
||||
[[crypto]] |
||||
= Spring Security Crypto Module |
||||
|
||||
|
||||
[[spring-security-crypto-introduction]] |
||||
== 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. |
||||
|
||||
|
||||
[[spring-security-crypto-encryption]] |
||||
== 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. |
||||
|
||||
[[spring-security-crypto-encryption-bytes]] |
||||
=== BytesEncryptor |
||||
Use the `Encryptors.stronger` factory method to construct a BytesEncryptor: |
||||
|
||||
.BytesEncryptor |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
Encryptors.stronger("password", "salt"); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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: |
||||
|
||||
.Generating a key |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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 https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any |
||||
guarantees about the authenticity of the data. |
||||
For a more secure alternative, users should prefer `Encryptors.stronger`. |
||||
|
||||
[[spring-security-crypto-encryption-text]] |
||||
=== TextEncryptor |
||||
Use the Encryptors.text factory method to construct a standard TextEncryptor: |
||||
|
||||
.TextEncryptor |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
Encryptors.text("password", "salt"); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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: |
||||
|
||||
.Queryable TextEncryptor |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
Encryptors.queryableText("password", "salt"); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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. |
||||
|
||||
[[spring-security-crypto-keygenerators]] |
||||
== 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: |
||||
|
||||
.BytesKeyGenerator |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
BytesKeyGenerator generator = KeyGenerators.secureRandom(); |
||||
byte[] key = generator.generateKey(); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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: |
||||
|
||||
.KeyGenerators.secureRandom |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
KeyGenerators.secureRandom(16); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
KeyGenerators.secureRandom(16) |
||||
---- |
||||
==== |
||||
|
||||
Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator that always returns the same key on every invocation: |
||||
|
||||
.KeyGenerators.shared |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
KeyGenerators.shared(16); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
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: |
||||
|
||||
.StringKeyGenerator |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
KeyGenerators.string(); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
KeyGenerators.string() |
||||
---- |
||||
==== |
||||
|
||||
[[spring-security-crypto-passwordencoders]] |
||||
== 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: |
||||
|
||||
[source,java] |
||||
---- |
||||
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. |
||||
|
||||
.BCryptPasswordEncoder |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
|
||||
// Create an encoder with strength 16 |
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16); |
||||
String result = encoder.encode("myPassword"); |
||||
assertTrue(encoder.matches("myPassword", result)); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
|
||||
// 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. |
||||
|
||||
|
||||
.Pbkdf2PasswordEncoder |
||||
==== |
||||
.Java |
||||
[source,java,role="primary"] |
||||
---- |
||||
// Create an encoder with all the defaults |
||||
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(); |
||||
String result = encoder.encode("myPassword"); |
||||
assertTrue(encoder.matches("myPassword", result)); |
||||
---- |
||||
|
||||
.Kotlin |
||||
[source,kotlin,role="secondary"] |
||||
---- |
||||
// Create an encoder with all the defaults |
||||
val encoder = Pbkdf2PasswordEncoder() |
||||
val result: String = encoder.encode("myPassword") |
||||
assertTrue(encoder.matches("myPassword", result)) |
||||
---- |
||||
==== |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
[[servlet-hello]] |
||||
= Hello Spring Security |
||||
|
||||
This section covers the minimum setup for how to use Spring Security with Spring Boot. |
||||
|
||||
[NOTE] |
||||
==== |
||||
The completed application can be found {gh-samples-url}/servlet/spring-boot/java/hello-security[in our samples repository]. |
||||
For your convenience, you can download a minimal Spring Boot + Spring Security application by 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=web,security[clicking here]. |
||||
==== |
||||
|
||||
[[servlet-hello-dependencies]] |
||||
== Updating Dependencies |
||||
|
||||
The only step you need to do is update the dependencies by using xref:getting-spring-security.adoc#getting-maven-boot[Maven] or xref:getting-spring-security.adoc#getting-gradle-boot[Gradle]. |
||||
|
||||
[[servlet-hello-starting]] |
||||
== Starting Hello Spring Security Boot |
||||
|
||||
You can now https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-running-with-the-maven-plugin[run the Spring Boot application] 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): |
||||
|
||||
.Running Spring Boot Application |
||||
==== |
||||
[source,bash] |
||||
---- |
||||
$ ./mvn spring-boot:run |
||||
... |
||||
INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration : |
||||
|
||||
Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336 |
||||
|
||||
... |
||||
---- |
||||
==== |
||||
|
||||
|
||||
[[servlet-hello-auto-configuration]] |
||||
== Spring Boot Auto Configuration |
||||
|
||||
// FIXME: Link to relevant portions of documentation |
||||
// FIXME: Link to Spring Boot's Security Auto configuration classes |
||||
// FIXME: Add a links for what user's should do next |
||||
|
||||
Spring Boot automatically: |
||||
|
||||
* Enables Spring Security's default configuration, which creates a servlet `Filter` as a bean named `springSecurityFilterChain`. |
||||
This bean is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application. |
||||
* Creates a `UserDetailsService` bean with a username of `user` and a randomly generated password that is logged to the console. |
||||
* Registers the `Filter` with a bean named `springSecurityFilterChain` with the Servlet container for every request. |
||||
|
||||
Spring Boot is not configuring much, but it does a lot. |
||||
A summary of the features follows: |
||||
|
||||
* Require an authenticated user for any interaction with the application |
||||
* Generate a default login form for you |
||||
* Let the user with a username of `user` and a password that is logged to the console to authenticate with form-based authentication (in the preceding example, the password is `8e557245-73e2-4286-969a-ff57fe326336`) |
||||
* Protects the password storage with BCrypt |
||||
* Lets the user log out |
||||
* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention |
||||
* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection |
||||
* Security Header integration |
||||
** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests |
||||
** https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration |
||||
** Cache Control (can be overridden later by your application to allow caching of your static resources) |
||||
** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration |
||||
** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] |
||||
* Integrate with the following Servlet API methods: |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.html#getUserPrincipal()`] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.html#isUserInRole(java.lang.String)`] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest.html#login(java.lang.String, java.lang.String)`] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest.html#logout()`] |
||||
|
||||
@ -0,0 +1,337 @@
@@ -0,0 +1,337 @@
|
||||
|
||||
[[jc]] |
||||
= Java Configuration |
||||
|
||||
General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. |
||||
Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. |
||||
|
||||
If you are familiar with the xref:servlet/xml-namespace.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. |
||||
|
||||
NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] which demonstrate the use of Spring Security Java Configuration. |
||||
|
||||
== Hello Web Security Java Configuration |
||||
|
||||
The first step is to create our Spring Security Java Configuration. |
||||
The configuration creates a Servlet Filter known as the `springSecurityFilterChain` which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application. |
||||
You can find the most basic example of a Spring Security Java Configuration below: |
||||
|
||||
[[jc-hello-wsca]] |
||||
[source,java] |
||||
---- |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
import org.springframework.context.annotation.*; |
||||
import org.springframework.security.config.annotation.authentication.builders.*; |
||||
import org.springframework.security.config.annotation.web.configuration.*; |
||||
|
||||
@EnableWebSecurity |
||||
public class WebSecurityConfig { |
||||
|
||||
@Bean |
||||
public UserDetailsService userDetailsService() { |
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); |
||||
manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build()); |
||||
return manager; |
||||
} |
||||
} |
||||
---- |
||||
|
||||
There really isn't much to this configuration, but it does a lot. |
||||
You can find a summary of the features below: |
||||
|
||||
* Require authentication to every URL in your application |
||||
* Generate a login form for you |
||||
* Allow the user with the *Username* _user_ and the *Password* _password_ to authenticate with form based authentication |
||||
* Allow the user to logout |
||||
* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention |
||||
* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection |
||||
* Security Header integration |
||||
** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests |
||||
** https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration |
||||
** Cache Control (can be overridden later by your application to allow caching of your static resources) |
||||
** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration |
||||
** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] |
||||
* Integrate with the following Servlet API methods |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest#getUserPrincipal()] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest#isUserInRole(java.lang.String)] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[HttpServletRequest#login(java.lang.String, java.lang.String)] |
||||
** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest#logout()] |
||||
|
||||
=== AbstractSecurityWebApplicationInitializer |
||||
|
||||
The next step is to register the `springSecurityFilterChain` with the war. |
||||
This can be done in Java Configuration with https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-container-config[Spring's WebApplicationInitializer support] in a Servlet 3.0+ environment. |
||||
Not suprisingly, Spring Security provides a base class `AbstractSecurityWebApplicationInitializer` that will ensure the `springSecurityFilterChain` gets registered for you. |
||||
The way in which we use `AbstractSecurityWebApplicationInitializer` differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application. |
||||
|
||||
* <<abstractsecuritywebapplicationinitializer-without-existing-spring>> - Use these instructions if you are not using Spring already |
||||
* <<abstractsecuritywebapplicationinitializer-with-spring-mvc>> - Use these instructions if you are already using Spring |
||||
|
||||
=== AbstractSecurityWebApplicationInitializer without Existing Spring |
||||
|
||||
If you are not using Spring or Spring MVC, you will need to pass in the `WebSecurityConfig` into the superclass to ensure the configuration is picked up. |
||||
You can find an example below: |
||||
|
||||
[source,java] |
||||
---- |
||||
import org.springframework.security.web.context.*; |
||||
|
||||
public class SecurityWebApplicationInitializer |
||||
extends AbstractSecurityWebApplicationInitializer { |
||||
|
||||
public SecurityWebApplicationInitializer() { |
||||
super(WebSecurityConfig.class); |
||||
} |
||||
} |
||||
---- |
||||
|
||||
The `SecurityWebApplicationInitializer` will do the following things: |
||||
|
||||
* Automatically register the springSecurityFilterChain Filter for every URL in your application |
||||
* Add a ContextLoaderListener that loads the <<jc-hello-wsca,WebSecurityConfig>>. |
||||
|
||||
=== AbstractSecurityWebApplicationInitializer with Spring MVC |
||||
|
||||
If we were using Spring elsewhere in our application we probably already had a `WebApplicationInitializer` that is loading our Spring Configuration. |
||||
If we use the previous configuration we would get an error. |
||||
Instead, we should register Spring Security with the existing `ApplicationContext`. |
||||
For example, if we were using Spring MVC our `SecurityWebApplicationInitializer` would look something like the following: |
||||
|
||||
[source,java] |
||||
---- |
||||
import org.springframework.security.web.context.*; |
||||
|
||||
public class SecurityWebApplicationInitializer |
||||
extends AbstractSecurityWebApplicationInitializer { |
||||
|
||||
} |
||||
---- |
||||
|
||||
This would simply only register the springSecurityFilterChain Filter for every URL in your application. |
||||
After that we would ensure that `WebSecurityConfig` was loaded in our existing ApplicationInitializer. |
||||
For example, if we were using Spring MVC it would be added in the `getRootConfigClasses()` |
||||
|
||||
[[message-web-application-inititializer-java]] |
||||
[source,java] |
||||
---- |
||||
public class MvcWebApplicationInitializer extends |
||||
AbstractAnnotationConfigDispatcherServletInitializer { |
||||
|
||||
@Override |
||||
protected Class<?>[] getRootConfigClasses() { |
||||
return new Class[] { WebSecurityConfig.class }; |
||||
} |
||||
|
||||
// ... other overrides ... |
||||
} |
||||
---- |
||||
|
||||
[[jc-httpsecurity]] |
||||
== HttpSecurity |
||||
|
||||
Thus far our <<jc-hello-wsca,WebSecurityConfig>> only contains information about how to authenticate our users. |
||||
How does Spring Security know that we want to require all users to be authenticated? |
||||
How does Spring Security know we want to support form based authentication? |
||||
Actually, there is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. |
||||
It has a method called `configure` with the following default implementation: |
||||
|
||||
[source,java] |
||||
---- |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.authorizeRequests(authorize -> authorize |
||||
.anyRequest().authenticated() |
||||
) |
||||
.formLogin(withDefaults()) |
||||
.httpBasic(withDefaults()); |
||||
} |
||||
---- |
||||
|
||||
The default configuration above: |
||||
|
||||
* Ensures that any request to our application requires the user to be authenticated |
||||
* Allows users to authenticate with form based login |
||||
* Allows users to authenticate with HTTP Basic authentication |
||||
|
||||
You will notice that this configuration is quite similar the XML Namespace configuration: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http> |
||||
<intercept-url pattern="/**" access="authenticated"/> |
||||
<form-login /> |
||||
<http-basic /> |
||||
</http> |
||||
---- |
||||
|
||||
== Multiple HttpSecurity |
||||
|
||||
We can configure multiple HttpSecurity instances just as we can have multiple `<http>` blocks. |
||||
The key is to extend the `WebSecurityConfigurerAdapter` multiple times. |
||||
For example, the following is an example of having a different configuration for URL's that start with `/api/`. |
||||
|
||||
[source,java] |
||||
---- |
||||
@EnableWebSecurity |
||||
public class MultiHttpSecurityConfig { |
||||
@Bean <1> |
||||
public UserDetailsService userDetailsService() throws Exception { |
||||
// ensure the passwords are encoded properly |
||||
UserBuilder users = User.withDefaultPasswordEncoder(); |
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); |
||||
manager.createUser(users.username("user").password("password").roles("USER").build()); |
||||
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()); |
||||
return manager; |
||||
} |
||||
|
||||
@Configuration |
||||
@Order(1) <2> |
||||
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.antMatcher("/api/**") <3> |
||||
.authorizeRequests(authorize -> authorize |
||||
.anyRequest().hasRole("ADMIN") |
||||
) |
||||
.httpBasic(withDefaults()); |
||||
} |
||||
} |
||||
|
||||
@Configuration <4> |
||||
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { |
||||
|
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.authorizeRequests(authorize -> authorize |
||||
.anyRequest().authenticated() |
||||
) |
||||
.formLogin(withDefaults()); |
||||
} |
||||
} |
||||
} |
||||
---- |
||||
|
||||
<1> Configure Authentication as normal |
||||
<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. |
||||
<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` |
||||
<4> Create another instance of `WebSecurityConfigurerAdapter`. |
||||
If the URL does not start with `/api/` this configuration will be used. |
||||
This configuration is considered after `ApiWebSecurityConfigurationAdapter` since it has an `@Order` value after `1` (no `@Order` defaults to last). |
||||
|
||||
[[jc-custom-dsls]] |
||||
== Custom DSLs |
||||
|
||||
You can provide your own custom DSLs in Spring Security. |
||||
For example, you might have something that looks like this: |
||||
|
||||
[source,java] |
||||
---- |
||||
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> { |
||||
private boolean flag; |
||||
|
||||
@Override |
||||
public void init(HttpSecurity http) throws Exception { |
||||
// any method that adds another configurer |
||||
// must be done in the init method |
||||
http.csrf().disable(); |
||||
} |
||||
|
||||
@Override |
||||
public void configure(HttpSecurity http) throws Exception { |
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class); |
||||
|
||||
// here we lookup from the ApplicationContext. You can also just create a new instance. |
||||
MyFilter myFilter = context.getBean(MyFilter.class); |
||||
myFilter.setFlag(flag); |
||||
http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class); |
||||
} |
||||
|
||||
public MyCustomDsl flag(boolean value) { |
||||
this.flag = value; |
||||
return this; |
||||
} |
||||
|
||||
public static MyCustomDsl customDsl() { |
||||
return new MyCustomDsl(); |
||||
} |
||||
} |
||||
---- |
||||
|
||||
NOTE: This is actually how methods like `HttpSecurity.authorizeRequests()` are implemented. |
||||
|
||||
The custom DSL can then be used like this: |
||||
|
||||
[source,java] |
||||
---- |
||||
@EnableWebSecurity |
||||
public class Config extends WebSecurityConfigurerAdapter { |
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.apply(customDsl()) |
||||
.flag(true) |
||||
.and() |
||||
...; |
||||
} |
||||
} |
||||
---- |
||||
|
||||
The code is invoked in the following order: |
||||
|
||||
* Code in `Config`s configure method is invoked |
||||
* Code in `MyCustomDsl`s init method is invoked |
||||
* Code in `MyCustomDsl`s configure method is invoked |
||||
|
||||
If you want, you can have `WebSecurityConfigurerAdapter` add `MyCustomDsl` by default by using `SpringFactories`. |
||||
For example, you would create a resource on the classpath named `META-INF/spring.factories` with the following contents: |
||||
|
||||
.META-INF/spring.factories |
||||
---- |
||||
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl |
||||
---- |
||||
|
||||
Users wishing to disable the default can do so explicitly. |
||||
|
||||
[source,java] |
||||
---- |
||||
@EnableWebSecurity |
||||
public class Config extends WebSecurityConfigurerAdapter { |
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.apply(customDsl()).disable() |
||||
...; |
||||
} |
||||
} |
||||
---- |
||||
|
||||
[[post-processing-configured-objects]] |
||||
== Post Processing Configured Objects |
||||
|
||||
Spring Security's Java Configuration does not expose every property of every object that it configures. |
||||
This simplifies the configuration for a majority of users. |
||||
Afterall, if every property was exposed, users could use standard bean configuration. |
||||
|
||||
While there are good reasons to not directly expose every property, users may still need more advanced configuration options. |
||||
To address this Spring Security introduces the concept of an `ObjectPostProcessor` which can be used to modify or replace many of the Object instances created by the Java Configuration. |
||||
For example, if you wanted to configure the `filterSecurityPublishAuthorizationSuccess` property on `FilterSecurityInterceptor` you could use the following: |
||||
|
||||
[source,java] |
||||
---- |
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http |
||||
.authorizeRequests(authorize -> authorize |
||||
.anyRequest().authenticated() |
||||
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { |
||||
public <O extends FilterSecurityInterceptor> O postProcess( |
||||
O fsi) { |
||||
fsi.setPublishAuthorizationSuccess(true); |
||||
return fsi; |
||||
} |
||||
}) |
||||
); |
||||
} |
||||
---- |
||||
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
|
||||
[[kotlin-config]] |
||||
= Kotlin Configuration |
||||
Spring Security Kotlin Configuration support has been available since Spring Security 5.3. |
||||
It enables users to easily configure Spring Security using a native Kotlin DSL. |
||||
|
||||
NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/kotlin/hello-security[a sample application] which demonstrates the use of Spring Security Kotlin Configuration. |
||||
|
||||
[[kotlin-config-httpsecurity]] |
||||
== HttpSecurity |
||||
|
||||
How does Spring Security know that we want to require all users to be authenticated? |
||||
How does Spring Security know we want to support form based authentication? |
||||
There is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. |
||||
It has a method called `configure` with the following default implementation: |
||||
|
||||
[source,kotlin] |
||||
---- |
||||
fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
formLogin { } |
||||
httpBasic { } |
||||
} |
||||
} |
||||
---- |
||||
|
||||
The default configuration above: |
||||
|
||||
* Ensures that any request to our application requires the user to be authenticated |
||||
* Allows users to authenticate with form based login |
||||
* Allows users to authenticate with HTTP Basic authentication |
||||
|
||||
You will notice that this configuration is quite similar the XML Namespace configuration: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http> |
||||
<intercept-url pattern="/**" access="authenticated"/> |
||||
<form-login /> |
||||
<http-basic /> |
||||
</http> |
||||
---- |
||||
|
||||
== Multiple HttpSecurity |
||||
|
||||
We can configure multiple HttpSecurity instances just as we can have multiple `<http>` blocks. |
||||
The key is to extend the `WebSecurityConfigurerAdapter` multiple times. |
||||
For example, the following is an example of having a different configuration for URL's that start with `/api/`. |
||||
|
||||
[source,kotlin] |
||||
---- |
||||
@EnableWebSecurity |
||||
class MultiHttpSecurityConfig { |
||||
@Bean <1> |
||||
public fun userDetailsService(): UserDetailsService { |
||||
val users: User.UserBuilder = User.withDefaultPasswordEncoder() |
||||
val manager = InMemoryUserDetailsManager() |
||||
manager.createUser(users.username("user").password("password").roles("USER").build()) |
||||
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()) |
||||
return manager |
||||
} |
||||
|
||||
@Configuration |
||||
@Order(1) <2> |
||||
class ApiWebSecurityConfigurationAdapter: WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
securityMatcher("/api/**") <3> |
||||
authorizeRequests { |
||||
authorize(anyRequest, hasRole("ADMIN")) |
||||
} |
||||
httpBasic { } |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Configuration <4> |
||||
class FormLoginWebSecurityConfigurerAdapter: WebSecurityConfigurerAdapter() { |
||||
override fun configure(http: HttpSecurity) { |
||||
http { |
||||
authorizeRequests { |
||||
authorize(anyRequest, authenticated) |
||||
} |
||||
formLogin { } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
---- |
||||
|
||||
<1> Configure Authentication as normal |
||||
<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. |
||||
<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` |
||||
<4> Create another instance of `WebSecurityConfigurerAdapter`. |
||||
If the URL does not start with `/api/` this configuration will be used. |
||||
This configuration is considered after `ApiWebSecurityConfigurationAdapter` since it has an `@Order` value after `1` (no `@Order` defaults to last). |
||||
@ -0,0 +1,402 @@
@@ -0,0 +1,402 @@
|
||||
|
||||
[[ns-config]] |
||||
= Security Namespace Configuration |
||||
|
||||
|
||||
== Introduction |
||||
Namespace configuration has been available since version 2.0 of the Spring Framework. |
||||
It allows you to supplement the traditional Spring beans application context syntax with elements from additional XML schema. |
||||
You can find more information in the Spring https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/[Reference Documentation]. |
||||
A namespace element can be used simply to allow a more concise way of configuring an individual bean or, more powerfully, to define an alternative configuration syntax which more closely matches the problem domain and hides the underlying complexity from the user. |
||||
A simple element may conceal the fact that multiple beans and processing steps are being added to the application context. |
||||
For example, adding the following element from the security namespace to an application context will start up an embedded LDAP server for testing use within the application: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<security:ldap-server /> |
||||
---- |
||||
|
||||
This is much simpler than wiring up the equivalent Apache Directory Server beans. |
||||
The most common alternative configuration requirements are supported by attributes on the `ldap-server` element and the user is isolated from worrying about which beans they need to create and what the bean property names are. |
||||
footnote:[You can find out more about the use of the `ldap-server` element in the chapter on pass:specialcharacters,macros[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP Authentication]].]. |
||||
Use of a good XML editor while editing the application context file should provide information on the attributes and elements that are available. |
||||
We would recommend that you try out the https://spring.io/tools[Eclipse IDE with Spring Tools] as it has special features for working with standard Spring namespaces. |
||||
|
||||
|
||||
To start using the security namespace in your application context, you need to have the `spring-security-config` jar on your classpath. |
||||
Then all you need to do is add the schema declaration to your application context file: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:security="http://www.springframework.org/schema/security" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans |
||||
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
||||
http://www.springframework.org/schema/security |
||||
https://www.springframework.org/schema/security/spring-security.xsd"> |
||||
... |
||||
</beans> |
||||
---- |
||||
|
||||
In many of the examples you will see (and in the sample applications), we will often use "security" as the default namespace rather than "beans", which means we can omit the prefix on all the security namespace elements, making the content easier to read. |
||||
You may also want to do this if you have your application context divided up into separate files and have most of your security configuration in one of them. |
||||
Your security application context file would then start like this |
||||
|
||||
[source,xml] |
||||
---- |
||||
<beans:beans xmlns="http://www.springframework.org/schema/security" |
||||
xmlns:beans="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans |
||||
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
||||
http://www.springframework.org/schema/security |
||||
https://www.springframework.org/schema/security/spring-security.xsd"> |
||||
... |
||||
</beans:beans> |
||||
---- |
||||
|
||||
We'll assume this syntax is being used from now on in this chapter. |
||||
|
||||
|
||||
=== Design of the Namespace |
||||
The namespace is designed to capture the most common uses of the framework and provide a simplified and concise syntax for enabling them within an application. |
||||
The design is based around the large-scale dependencies within the framework, and can be divided up into the following areas: |
||||
|
||||
* __Web/HTTP Security__ - the most complex part. |
||||
Sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, render login and error pages and much more. |
||||
|
||||
* __Business Object (Method) Security__ - options for securing the service layer. |
||||
|
||||
* __AuthenticationManager__ - handles authentication requests from other parts of the framework. |
||||
|
||||
* __AccessDecisionManager__ - provides access decisions for web and method security. |
||||
A default one will be registered, but you can also choose to use a custom one, declared using normal Spring bean syntax. |
||||
|
||||
* __AuthenticationProvider__s - mechanisms against which the authentication manager authenticates users. |
||||
The namespace provides supports for several standard options and also a means of adding custom beans declared using a traditional syntax. |
||||
|
||||
* __UserDetailsService__ - closely related to authentication providers, but often also required by other beans. |
||||
|
||||
We'll see how to configure these in the following sections. |
||||
|
||||
[[ns-getting-started]] |
||||
== Getting Started with Security Namespace Configuration |
||||
In this section, we'll look at how you can build up a namespace configuration to use some of the main features of the framework. |
||||
Let's assume you initially want to get up and running as quickly as possible and add authentication support and access control to an existing web application, with a few test logins. |
||||
Then we'll look at how to change over to authenticating against a database or other security repository. |
||||
In later sections we'll introduce more advanced namespace configuration options. |
||||
|
||||
[[ns-web-xml]] |
||||
=== web.xml Configuration |
||||
The first thing you need to do is add the following filter declaration to your `web.xml` file: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<filter> |
||||
<filter-name>springSecurityFilterChain</filter-name> |
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> |
||||
</filter> |
||||
|
||||
<filter-mapping> |
||||
<filter-name>springSecurityFilterChain</filter-name> |
||||
<url-pattern>/*</url-pattern> |
||||
</filter-mapping> |
||||
---- |
||||
|
||||
This provides a hook into the Spring Security web infrastructure. |
||||
`DelegatingFilterProxy` is a Spring Framework class which delegates to a filter implementation which is defined as a Spring bean in your application context. |
||||
In this case, the bean is named "springSecurityFilterChain", which is an internal infrastructure bean created by the namespace to handle web security. |
||||
Note that you should not use this bean name yourself. |
||||
Once you've added this to your `web.xml`, you're ready to start editing your application context file. |
||||
Web security services are configured using the `<http>` element. |
||||
|
||||
[[ns-minimal]] |
||||
=== A Minimal <http> Configuration |
||||
All you need to enable web security to begin with is |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http> |
||||
<intercept-url pattern="/**" access="hasRole('USER')" /> |
||||
<form-login /> |
||||
<logout /> |
||||
</http> |
||||
---- |
||||
|
||||
Which says that we want all URLs within our application to be secured, requiring the role `ROLE_USER` to access them, we want to log in to the application using a form with username and password, and that we want a logout URL registered which will allow us to log out of the application. |
||||
`<http>` element is the parent for all web-related namespace functionality. |
||||
The `<intercept-url>` element defines a `pattern` which is matched against the URLs of incoming requests using an ant path style syntax footnote:[See the section on pass:specialcharacters,macros[xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`]] for more details on how matches are actually performed.]. |
||||
You can also use regular-expression matching as an alternative (see the namespace appendix for more details). |
||||
The `access` attribute defines the access requirements for requests matching the given pattern. |
||||
With the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request. |
||||
The prefix "ROLE_" is a marker which indicates that a simple comparison with the user's authorities should be made. |
||||
In other words, a normal role-based check should be used. |
||||
Access-control in Spring Security is not limited to the use of simple roles (hence the use of the prefix to differentiate between different types of security attributes). |
||||
We'll see later how the interpretation can vary footnote:[The interpretation of the comma-separated values in the `access` attribute depends on the implementation of the <<ns-access-manager,AccessDecisionManager>> which is used.]. |
||||
In Spring Security 3.0, the attribute can also be populated with an xref:servlet/authorization/expression-based.adoc#el-access[EL expression]. |
||||
|
||||
|
||||
[NOTE] |
||||
==== |
||||
|
||||
You can use multiple `<intercept-url>` elements to define different access requirements for different sets of URLs, but they will be evaluated in the order listed and the first match will be used. |
||||
So you must put the most specific matches at the top. |
||||
You can also add a `method` attribute to limit the match to a particular HTTP method (`GET`, `POST`, `PUT` etc.). |
||||
|
||||
==== |
||||
|
||||
To add some users, you can define a set of test data directly in the namespace: |
||||
|
||||
[source,xml,attrs="-attributes"] |
||||
---- |
||||
<authentication-manager> |
||||
<authentication-provider> |
||||
<user-service> |
||||
<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that |
||||
NoOpPasswordEncoder should be used. This is not safe for production, but makes reading |
||||
in samples easier. Normally passwords should be hashed using BCrypt --> |
||||
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" /> |
||||
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" /> |
||||
</user-service> |
||||
</authentication-provider> |
||||
</authentication-manager> |
||||
---- |
||||
|
||||
This is an example of a secure way of storing the same passwords. |
||||
The password is prefixed with `+{bcrypt}+` to instruct `DelegatingPasswordEncoder`, which supports any configured `PasswordEncoder` for matching, that the passwords are hashed using BCrypt: |
||||
|
||||
[source,xml,attrs="-attributes"] |
||||
---- |
||||
<authentication-manager> |
||||
<authentication-provider> |
||||
<user-service> |
||||
<user name="jimi" password="{bcrypt}$2a$10$ddEWZUl8aU0GdZPPpy7wbu82dvEw/pBpbRvDQRqA41y6mK1CoH00m" |
||||
authorities="ROLE_USER, ROLE_ADMIN" /> |
||||
<user name="bob" password="{bcrypt}$2a$10$/elFpMBnAYYig6KRR5bvOOYeZr1ie1hSogJryg9qDlhza4oCw1Qka" |
||||
authorities="ROLE_USER" /> |
||||
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" /> |
||||
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" /> |
||||
</user-service> |
||||
</authentication-provider> |
||||
</authentication-manager> |
||||
---- |
||||
|
||||
|
||||
|
||||
[subs="quotes"] |
||||
**** |
||||
If you are familiar with pre-namespace versions of the framework, you can probably already guess roughly what's going on here. |
||||
The `<http>` element is responsible for creating a `FilterChainProxy` and the filter beans which it uses. |
||||
Common problems like incorrect filter ordering are no longer an issue as the filter positions are predefined. |
||||
|
||||
The `<authentication-provider>` element creates a `DaoAuthenticationProvider` bean and the `<user-service>` element creates an `InMemoryDaoImpl`. |
||||
All `authentication-provider` elements must be children of the `<authentication-manager>` element, which creates a `ProviderManager` and registers the authentication providers with it. |
||||
You can find more detailed information on the beans that are created in the xref:servlet/appendix/namespace.adoc#appendix-namespace[namespace appendix]. |
||||
It's worth cross-checking this if you want to start understanding what the important classes in the framework are and how they are used, particularly if you want to customise things later. |
||||
**** |
||||
|
||||
The configuration above defines two users, their passwords and their roles within the application (which will be used for access control). |
||||
It is also possible to load user information from a standard properties file using the `properties` attribute on `user-service`. |
||||
See the section on xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] for more details on the file format. |
||||
Using the `<authentication-provider>` element means that the user information will be used by the authentication manager to process authentication requests. |
||||
You can have multiple `<authentication-provider>` elements to define different authentication sources and each will be consulted in turn. |
||||
|
||||
At this point you should be able to start up your application and you will be required to log in to proceed. |
||||
Try it out, or try experimenting with the "tutorial" sample application that comes with the project. |
||||
|
||||
[[ns-form-target]] |
||||
==== Setting a Default Post-Login Destination |
||||
If a form login isn't prompted by an attempt to access a protected resource, the `default-target-url` option comes into play. |
||||
This is the URL the user will be taken to after successfully logging in, and defaults to "/". |
||||
You can also configure things so that the user __always__ ends up at this page (regardless of whether the login was "on-demand" or they explicitly chose to log in) by setting the `always-use-default-target` attribute to "true". |
||||
This is useful if your application always requires that the user starts at a "home" page, for example: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http pattern="/login.htm*" security="none"/> |
||||
<http use-expressions="false"> |
||||
<intercept-url pattern='/**' access='ROLE_USER' /> |
||||
<form-login login-page='/login.htm' default-target-url='/home.htm' |
||||
always-use-default-target='true' /> |
||||
</http> |
||||
---- |
||||
|
||||
For even more control over the destination, you can use the `authentication-success-handler-ref` attribute as an alternative to `default-target-url`. |
||||
The referenced bean should be an instance of `AuthenticationSuccessHandler`. |
||||
|
||||
[[ns-web-advanced]] |
||||
== Advanced Web Features |
||||
|
||||
[[ns-custom-filters]] |
||||
=== Adding in Your Own Filters |
||||
If you've used Spring Security before, you'll know that the framework maintains a chain of filters in order to apply its services. |
||||
You may want to add your own filters to the stack at particular locations or use a Spring Security filter for which there isn't currently a namespace configuration option (CAS, for example). |
||||
Or you might want to use a customized version of a standard namespace filter, such as the `UsernamePasswordAuthenticationFilter` which is created by the `<form-login>` element, taking advantage of some of the extra configuration options which are available by using the bean explicitly. |
||||
How can you do this with namespace configuration, since the filter chain is not directly exposed? |
||||
|
||||
The order of the filters is always strictly enforced when using the namespace. |
||||
When the application context is being created, the filter beans are sorted by the namespace handling code and the standard Spring Security filters each have an alias in the namespace and a well-known position. |
||||
|
||||
[NOTE] |
||||
==== |
||||
In previous versions, the sorting took place after the filter instances had been created, during post-processing of the application context. |
||||
In version 3.0+ the sorting is now done at the bean metadata level, before the classes have been instantiated. |
||||
This has implications for how you add your own filters to the stack as the entire filter list must be known during the parsing of the `<http>` element, so the syntax has changed slightly in 3.0. |
||||
==== |
||||
|
||||
The filters, aliases and namespace elements/attributes which create the filters are shown in <<filter-stack>>. |
||||
The filters are listed in the order in which they occur in the filter chain. |
||||
|
||||
[[filter-stack]] |
||||
.Standard Filter Aliases and Ordering |
||||
|=== |
||||
| Alias | Filter Class | Namespace Element or Attribute |
||||
|
||||
| CHANNEL_FILTER |
||||
| `ChannelProcessingFilter` |
||||
| `http/intercept-url@requires-channel` |
||||
|
||||
| SECURITY_CONTEXT_FILTER |
||||
| `SecurityContextPersistenceFilter` |
||||
| `http` |
||||
|
||||
| CONCURRENT_SESSION_FILTER |
||||
| `ConcurrentSessionFilter` |
||||
| `session-management/concurrency-control` |
||||
|
||||
| HEADERS_FILTER |
||||
| `HeaderWriterFilter` |
||||
| `http/headers` |
||||
|
||||
| CSRF_FILTER |
||||
| `CsrfFilter` |
||||
| `http/csrf` |
||||
|
||||
| LOGOUT_FILTER |
||||
| `LogoutFilter` |
||||
| `http/logout` |
||||
|
||||
| X509_FILTER |
||||
| `X509AuthenticationFilter` |
||||
| `http/x509` |
||||
|
||||
| PRE_AUTH_FILTER |
||||
| `AbstractPreAuthenticatedProcessingFilter` Subclasses |
||||
| N/A |
||||
|
||||
| CAS_FILTER |
||||
| `CasAuthenticationFilter` |
||||
| N/A |
||||
|
||||
| FORM_LOGIN_FILTER |
||||
| `UsernamePasswordAuthenticationFilter` |
||||
| `http/form-login` |
||||
|
||||
| BASIC_AUTH_FILTER |
||||
| `BasicAuthenticationFilter` |
||||
| `http/http-basic` |
||||
|
||||
| SERVLET_API_SUPPORT_FILTER |
||||
| `SecurityContextHolderAwareRequestFilter` |
||||
| `http/@servlet-api-provision` |
||||
|
||||
| JAAS_API_SUPPORT_FILTER |
||||
| `JaasApiIntegrationFilter` |
||||
| `http/@jaas-api-provision` |
||||
|
||||
| REMEMBER_ME_FILTER |
||||
| `RememberMeAuthenticationFilter` |
||||
| `http/remember-me` |
||||
|
||||
| ANONYMOUS_FILTER |
||||
| `AnonymousAuthenticationFilter` |
||||
| `http/anonymous` |
||||
|
||||
| SESSION_MANAGEMENT_FILTER |
||||
| `SessionManagementFilter` |
||||
| `session-management` |
||||
|
||||
| EXCEPTION_TRANSLATION_FILTER |
||||
| `ExceptionTranslationFilter` |
||||
| `http` |
||||
|
||||
| FILTER_SECURITY_INTERCEPTOR |
||||
| `FilterSecurityInterceptor` |
||||
| `http` |
||||
|
||||
| SWITCH_USER_FILTER |
||||
| `SwitchUserFilter` |
||||
| N/A |
||||
|=== |
||||
|
||||
You can add your own filter to the stack, using the `custom-filter` element and one of these names to specify the position your filter should appear at: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http> |
||||
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" /> |
||||
</http> |
||||
|
||||
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/> |
||||
---- |
||||
|
||||
You can also use the `after` or `before` attributes if you want your filter to be inserted before or after another filter in the stack. |
||||
The names "FIRST" and "LAST" can be used with the `position` attribute to indicate that you want your filter to appear before or after the entire stack, respectively. |
||||
|
||||
.Avoiding filter position conflicts |
||||
[TIP] |
||||
==== |
||||
|
||||
If you are inserting a custom filter which may occupy the same position as one of the standard filters created by the namespace then it's important that you don't include the namespace versions by mistake. |
||||
Remove any elements which create filters whose functionality you want to replace. |
||||
|
||||
Note that you can't replace filters which are created by the use of the `<http>` element itself - `SecurityContextPersistenceFilter`, `ExceptionTranslationFilter` or `FilterSecurityInterceptor`. |
||||
Some other filters are added by default, but you can disable them. |
||||
An `AnonymousAuthenticationFilter` is added by default and unless you have xref:servlet/authentication/session-management.adoc#ns-session-fixation[session-fixation protection] disabled, a `SessionManagementFilter` will also be added to the filter chain. |
||||
|
||||
==== |
||||
|
||||
If you're replacing a namespace filter which requires an authentication entry point (i.e. where the authentication process is triggered by an attempt by an unauthenticated user to access to a secured resource), you will need to add a custom entry point bean too. |
||||
|
||||
|
||||
|
||||
[[ns-method-security]] |
||||
== Method Security |
||||
From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. |
||||
It provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation. |
||||
From 3.0 you can also make use of new xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations]. |
||||
You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts. |
||||
|
||||
[[ns-access-manager]] |
||||
== The Default AccessDecisionManager |
||||
This section assumes you have some knowledge of the underlying architecture for access-control within Spring Security. |
||||
If you don't you can skip it and come back to it later, as this section is only really relevant for people who need to do some customization in order to use more than simple role-based security. |
||||
|
||||
When you use a namespace configuration, a default instance of `AccessDecisionManager` is automatically registered for you and will be used for making access decisions for method invocations and web URL access, based on the access attributes you specify in your `intercept-url` and `protect-pointcut` declarations (and in annotations if you are using annotation secured methods). |
||||
|
||||
The default strategy is to use an `AffirmativeBased` `AccessDecisionManager` with a `RoleVoter` and an `AuthenticatedVoter`. |
||||
You can find out more about these in the chapter on xref:servlet/authorization/architecture.adoc#authz-arch[authorization]. |
||||
|
||||
|
||||
[[ns-custom-access-mgr]] |
||||
=== Customizing the AccessDecisionManager |
||||
If you need to use a more complicated access control strategy then it is easy to set an alternative for both method and web security. |
||||
|
||||
For method security, you do this by setting the `access-decision-manager-ref` attribute on `global-method-security` to the `id` of the appropriate `AccessDecisionManager` bean in the application context: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean"> |
||||
... |
||||
</global-method-security> |
||||
---- |
||||
|
||||
The syntax for web security is the same, but on the `http` element: |
||||
|
||||
[source,xml] |
||||
---- |
||||
<http access-decision-manager-ref="myAccessDecisionManagerBean"> |
||||
... |
||||
</http> |
||||
---- |
||||
Loading…
Reference in new issue