Merge Update servlet/architecture.adoc to use include-code
Update servlet/architecture.adoc to use include-code
This commit is contained in:
@@ -51,28 +51,9 @@ image::{figures}/delegatingfilterproxy.png[]
|
||||
The following listing shows pseudo code of `DelegatingFilterProxy`:
|
||||
|
||||
.`DelegatingFilterProxy` Pseudo Code
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
Filter delegate = getFilterBean(someBeanName); // <1>
|
||||
delegate.doFilter(request, response); // <2>
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
|
||||
val delegate: Filter = getFilterBean(someBeanName) // <1>
|
||||
delegate.doFilter(request, response) // <2>
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./SampleDelegatingFilterProxy[tag=dofilter,indent=0]
|
||||
|
||||
<1> 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~__.
|
||||
<2> Delegate work to the Spring Bean.
|
||||
@@ -155,58 +136,7 @@ However, there are times that it is beneficial to know the ordering, if you want
|
||||
These security filters are most often declared using an javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[`HttpSecurity`] instance.
|
||||
To exemplify the above paragraph, let's consider the following security configuration:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(Customizer.withDefaults())
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
import org.springframework.security.config.web.servlet.invoke
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
csrf { }
|
||||
httpBasic { }
|
||||
formLogin { }
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./SecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
The above configuration will result in the following `Filter` ordering:
|
||||
|
||||
@@ -233,7 +163,7 @@ If you want to see the list of filters invoked for a particular request, you can
|
||||
=== Printing the Security Filters
|
||||
|
||||
Often times, it is useful to see the list of security ``Filter``s that are invoked for a particular request.
|
||||
For example, you want to make sure that the <<adding-custom-filter,filter you have added>> is in the list of the security filters.
|
||||
For example, you want to make sure that the <<adding-filters-to-chain,filter you have added>> is in the list of the security filters.
|
||||
|
||||
The list of filters is printed at DEBUG level on the application startup, so you can see something like the following on the console output for example:
|
||||
|
||||
@@ -248,7 +178,7 @@ But that is not all, you can also configure your application to print the invoca
|
||||
That is helpful to see if the filter you have added is invoked for a particular request or to check where an exception is coming from.
|
||||
To do that, you can configure your application to <<servlet-logging,log the security events>>.
|
||||
|
||||
[[adding-custom-filter]]
|
||||
[[adding-filters-to-chain]]
|
||||
=== Adding Filters to the Filter Chain
|
||||
|
||||
Most of the time, the default <<servlet-security-filters>> are enough to provide security to your application.
|
||||
@@ -260,6 +190,7 @@ javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
* `#addFilterAfter(Filter, Class<?>)` adds your filter after another filter
|
||||
* `#addFilterAt(Filter, Class<?>)` replaces another filter with your filter
|
||||
|
||||
[[adding-custom-filter]]
|
||||
==== Adding a Custom Filter
|
||||
|
||||
If you are creating a filter of your own, you will need to determine its location in the filter chain.
|
||||
@@ -298,39 +229,7 @@ For example, let's say that you want to add a `Filter` that gets a tenant id hea
|
||||
|
||||
First, let's create the `Filter`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
||||
public class TenantFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
|
||||
String tenantId = request.getHeader("X-Tenant-Id"); <1>
|
||||
boolean hasAccess = isUserAllowed(tenantId); <2>
|
||||
if (hasAccess) {
|
||||
filterChain.doFilter(request, response); <3>
|
||||
return;
|
||||
}
|
||||
throw new AccessDeniedException("Access denied"); <4>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
----
|
||||
include-code::./TenantFilter[tag=snippet,indent=0]
|
||||
|
||||
The sample code above does the following:
|
||||
|
||||
@@ -349,34 +248,7 @@ The previous description already gives us a clue on where to add the filter, sin
|
||||
|
||||
Based on the rule of thumb, add it after xref:servlet/authentication/anonymous.adoc[ `AnonymousAuthenticationFilter`], the last authentication filter in the chain, like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.addFilterAfter(new TenantFilter(), AnonymousAuthenticationFilter.class); <1>
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http
|
||||
// ...
|
||||
.addFilterAfter(TenantFilter(), AnonymousAuthenticationFilter::class.java) <1>
|
||||
return http.build()
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./SecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
<1> Use `HttpSecurity#addFilterAfter` to add the `TenantFilter` after the `AnonymousAuthenticationFilter`.
|
||||
|
||||
@@ -405,127 +277,23 @@ public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilte
|
||||
|
||||
This makes so that `HttpSecurity` is the only one adding it.
|
||||
|
||||
[[customizing-filter]]
|
||||
==== Customizing a Spring Security Filter
|
||||
|
||||
Generally, you can use a filter's DSL method to configure Spring Security's filters.
|
||||
For example, the simplest way to add `BasicAuthenticationFilter` is by asking the DSL to do it:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
// ...
|
||||
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
httpBasic { }
|
||||
// ...
|
||||
}
|
||||
|
||||
return http.build()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
include-code::./CustomizingFilterTests[tag=basic-default,indent=0]
|
||||
|
||||
However, in the event that you want to construct a Spring Security filter yourself, you specify it in the DSL using `addFilterAt` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
BasicAuthenticationFilter basic = new BasicAuthenticationFilter();
|
||||
// ... configure
|
||||
|
||||
http
|
||||
// ...
|
||||
.addFilterAt(basic, BasicAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
val basic = BasicAuthenticationFilter()
|
||||
// ... configure
|
||||
|
||||
http
|
||||
// ...
|
||||
.addFilterAt(basic, BasicAuthenticationFilter::class.java)
|
||||
|
||||
return http.build()
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./CustomizingFilterTests[tag=custom-filter,indent=0]
|
||||
|
||||
Note that if that filter has already been added, then Spring Security will throw an exception.
|
||||
For example, calling xref:servlet/authentication/passwords/basic.adoc[ `HttpSecurity#httpBasic`] adds a `BasicAuthenticationFilter` for you.
|
||||
So, the following arrangement fails since there are two calls that are both trying to add `BasicAuthenticationFilter`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
BasicAuthenticationFilter basic = new BasicAuthenticationFilter();
|
||||
// ... configure
|
||||
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
// ... on no! BasicAuthenticationFilter is added twice!
|
||||
.addFilterAt(basic, BasicAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
val basic = BasicAuthenticationFilter()
|
||||
// ... configure
|
||||
|
||||
http {
|
||||
httpBasic { }
|
||||
}
|
||||
|
||||
// ... on no! BasicAuthenticationFilter is added twice!
|
||||
http.addFilterAt(basic, BasicAuthenticationFilter::class.java)
|
||||
|
||||
return http.build()
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./CustomizingFilterTests[tag=incorrect,indent=0]
|
||||
|
||||
In this case, remove the call to `httpBasic` since you are constructing `BasicAuthenticationFilter` yourself.
|
||||
|
||||
@@ -533,10 +301,7 @@ In this case, remove the call to `httpBasic` since you are constructing `BasicAu
|
||||
====
|
||||
In the event that you are unable to reconfigure `HttpSecurity` to not add a certain filter, you can typically disable the Spring Security filter by calling its DSL's `disable` method like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
.httpBasic((basic) -> basic.disable())
|
||||
----
|
||||
include-code::./CustomizingFilterTests[tag=disable,indent=0]
|
||||
====
|
||||
|
||||
[[servlet-exceptiontranslationfilter]]
|
||||
@@ -618,53 +383,7 @@ Or you may want to shut off this feature since you always want to redirect the u
|
||||
To do that, you can use the javadoc:org.springframework.security.web.savedrequest.NullRequestCache[NullRequestCache] implementation.
|
||||
|
||||
.Prevent the Request From Being Saved
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||
RequestCache nullRequestCache = new NullRequestCache();
|
||||
http
|
||||
// ...
|
||||
.requestCache((cache) -> cache
|
||||
.requestCache(nullRequestCache)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
|
||||
val nullRequestCache = NullRequestCache()
|
||||
http {
|
||||
requestCache {
|
||||
requestCache = nullRequestCache
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
----
|
||||
|
||||
XML::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<http auto-config="true">
|
||||
<!-- ... -->
|
||||
<request-cache ref="nullRequestCache"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
|
||||
----
|
||||
======
|
||||
|
||||
include-code::./SecurityConfig[tag=snippet,indent=0]
|
||||
|
||||
[[requestcacheawarefilter]]
|
||||
=== RequestCacheAwareFilter
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.addingcustomfilter;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = {
|
||||
CustomFilterTests.UserDetailsConfig.class,
|
||||
CustomFilterTests.ApiController.class,
|
||||
SecurityConfig.class })
|
||||
@WebAppConfiguration
|
||||
public class CustomFilterTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.defaultRequest(get("/api").with(user("user")))
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void tenantFilterWhenHeaderMissingThenAccessDenied() {
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.mvc.perform(get("/api")).andReturn());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tenantFilterWhenHeaderPresentThenContinuesFilterChain() throws Exception {
|
||||
this.mvc.perform(get("/api").header("X-Tenant-Id", "some-tenant-id"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(authenticated().withUsername("user"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserDetailsConfig {
|
||||
@Bean
|
||||
UserDetailsService userDetailsService() {
|
||||
UserDetails user = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user);
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class ApiController {
|
||||
|
||||
@GetMapping("/api")
|
||||
String api() {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.addingcustomfilter;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@Configuration
|
||||
@ContextConfiguration(classes = { SecurityConfig.class })
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
// tag::snippet[]
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.addFilterAfter(new TenantFilter(), AnonymousAuthenticationFilter.class); // <1>
|
||||
return http.build();
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.addingcustomfilter;
|
||||
|
||||
// tag::snippet[]
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
||||
public class TenantFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
|
||||
String tenantId = request.getHeader("X-Tenant-Id"); // <1>
|
||||
boolean hasAccess = isUserAllowed(tenantId); // <2>
|
||||
if (hasAccess) {
|
||||
filterChain.doFilter(request, response); // <3>
|
||||
return;
|
||||
}
|
||||
throw new AccessDeniedException("Access denied"); // <4>
|
||||
}
|
||||
|
||||
private boolean isUserAllowed(String tenantId) {
|
||||
return "some-tenant-id".equals(tenantId);
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.customizingfilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
|
||||
/**
|
||||
* Tests for customizing security filters.
|
||||
*
|
||||
*/
|
||||
@ExtendWith(SpringTestContextExtension.class)
|
||||
public class CustomizingFilterTests {
|
||||
|
||||
public final SpringTestContext spring = new SpringTestContext(this);
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Autowired
|
||||
FilterChainProxy filterChainProxy;
|
||||
|
||||
@Test
|
||||
public void filterChainWhenBasicDefaultThenBasicAuthenticationFilterPresent() {
|
||||
this.spring.register(SecurityConfigBasicDefault.class).autowire();
|
||||
List<Filter> filters = this.filterChainProxy.getFilters("/");
|
||||
assertThat(filters).extracting("class").contains(BasicAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterChainWhenCustomFilterThenCustomFilterPresent() {
|
||||
this.spring.register(SecurityConfigCustom.class).autowire();
|
||||
List<Filter> filters = this.filterChainProxy.getFilters("/");
|
||||
assertThat(filters).extracting("class").contains(SecurityConfigCustom.MyBasicAuthenticationFilter.class);
|
||||
assertThat(filters).extracting("class").doesNotContain(BasicAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenDisableThenNoWwwAuthenticateHeader() throws Exception {
|
||||
this.spring.register(SecurityConfigDisable.class).autowire();
|
||||
this.mvc.perform(get("/")).andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterChainWhenIncorrectThenBothFiltersPresent() {
|
||||
this.spring.register(SecurityConfigIncorrect.class).autowire();
|
||||
List<Filter> filters = this.filterChainProxy.getFilters("/");
|
||||
assertThat(filters).extracting("class").contains(BasicAuthenticationFilter.class);
|
||||
assertThat(filters).extracting("class").contains(SecurityConfigIncorrect.MyBasicAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfigBasicDefault {
|
||||
|
||||
// tag::basic-default[]
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults());
|
||||
// ...
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// end::basic-default[]
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfigCustom {
|
||||
|
||||
// tag::custom-filter[]
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
MyBasicAuthenticationFilter basic = new MyBasicAuthenticationFilter();
|
||||
// ... configure
|
||||
|
||||
http
|
||||
// ...
|
||||
.addFilterAt(basic, BasicAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// end::custom-filter[]
|
||||
|
||||
static class MyBasicAuthenticationFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfigDisable {
|
||||
|
||||
// tag::disable[]
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic((basic) -> basic.disable());
|
||||
// ...
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// end::disable[]
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfigIncorrect {
|
||||
|
||||
// tag::incorrect[]
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
MyBasicAuthenticationFilter basic = new MyBasicAuthenticationFilter();
|
||||
// ... configure
|
||||
|
||||
http
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
// ... on no! BasicAuthenticationFilter is added twice!
|
||||
.addFilterAt(basic, BasicAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// end::incorrect[]
|
||||
|
||||
static class MyBasicAuthenticationFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserDetailsConfig {
|
||||
|
||||
@Bean
|
||||
InMemoryUserDetailsManager userDetailsManager() {
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.requestcachepreventsavedrequest;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
|
||||
public class SecurityConfig {
|
||||
|
||||
// tag::snippet[]
|
||||
@Bean
|
||||
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||
RequestCache nullRequestCache = new NullRequestCache();
|
||||
http
|
||||
// ...
|
||||
.requestCache((cache) -> cache
|
||||
.requestCache(nullRequestCache)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.servletdelegatingfilterproxy;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A very simple implementation of a DelegatingFilterProxy.
|
||||
*/
|
||||
public class SampleDelegatingFilterProxy extends GenericFilterBean {
|
||||
|
||||
private StaticWebApplicationContext wac;
|
||||
private final String someBeanName;
|
||||
|
||||
public SampleDelegatingFilterProxy(String someBeanName, StaticWebApplicationContext webApplicationContext) {
|
||||
this.wac = webApplicationContext;
|
||||
this.someBeanName = someBeanName;
|
||||
}
|
||||
|
||||
// tag::dofilter[]
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
Filter delegate = getFilterBean(someBeanName); // <1>
|
||||
delegate.doFilter(request, response, chain); // <2>
|
||||
}
|
||||
// end::dofilter[]
|
||||
|
||||
private Filter getFilterBean(String someBeanName) {
|
||||
return this.wac.getBean(someBeanName, Filter.class);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.servletdelegatingfilterproxy;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.web.servlet.MockServletContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class SampleDelegatingFilterProxyTests {
|
||||
|
||||
@Test
|
||||
void testFilter() throws ServletException, IOException {
|
||||
ServletContext sc = new MockServletContext();
|
||||
StaticWebApplicationContext wac = new StaticWebApplicationContext();
|
||||
wac.registerSingleton("targetFilter", MockFilter.class);
|
||||
wac.setServletContext(sc);
|
||||
wac.refresh();
|
||||
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
|
||||
|
||||
MockFilter targetFilter = (MockFilter) wac.getBean("targetFilter");
|
||||
MockFilterConfig proxyConfig = new MockFilterConfig(sc);
|
||||
proxyConfig.addInitParameter("targetBeanName", "targetFilter");
|
||||
SampleDelegatingFilterProxy filterProxy = new SampleDelegatingFilterProxy("targetFilter", wac);
|
||||
filterProxy.init(proxyConfig);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
filterProxy.doFilter(request, response, null);
|
||||
|
||||
assertThat(targetFilter.filterConfig).isNull();
|
||||
assertThat(request.getAttribute("called")).isEqualTo(Boolean.TRUE);
|
||||
|
||||
filterProxy.destroy();
|
||||
assertThat(targetFilter.filterConfig).isNull();
|
||||
}
|
||||
|
||||
private static class MockFilter implements Filter {
|
||||
|
||||
private FilterConfig filterConfig;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
this.filterConfig = filterConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws java.io.IOException, ServletException {
|
||||
request.setAttribute("called", Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.filterConfig = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockFilterConfig implements FilterConfig {
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final String filterName;
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<>();
|
||||
|
||||
public MockFilterConfig(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
this.filterName = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterName() {
|
||||
return this.filterName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.servletsecurityfilters;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = { SampleSecurityConfigTests.UserDetailsConfig.class, SecurityConfig.class })
|
||||
@WebAppConfiguration
|
||||
public class SampleSecurityConfigTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.defaultRequest(get("/api").with(user("user")))
|
||||
.defaultRequest(post("/api").with(csrf()))
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGet() throws Exception {
|
||||
this.mvc.perform(get("/api")
|
||||
.with(httpBasic("user", "password")))
|
||||
// Security check was successful
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(authenticated().withUsername("user"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnauthenticated() throws Exception {
|
||||
this.mvc.perform(get("/api"))
|
||||
// Security check was successful
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCsrf() throws Exception {
|
||||
this.mvc.perform(post("/api")
|
||||
.with(csrf())
|
||||
.with(httpBasic("user", "password"))
|
||||
).andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserDetailsConfig {
|
||||
@Bean
|
||||
UserDetailsService userDetailsService() {
|
||||
UserDetails user = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.docs.servlet.servletsecurityfilters;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(Customizer.withDefaults())
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.addingcustomfilter
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
|
||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@ExtendWith(SpringExtension::class)
|
||||
@ContextConfiguration(
|
||||
classes = [
|
||||
CustomFilterTests.UserDetailsConfig::class,
|
||||
CustomFilterTests.ApiController::class,
|
||||
SecurityConfig::class
|
||||
]
|
||||
)
|
||||
@WebAppConfiguration
|
||||
class CustomFilterTests {
|
||||
|
||||
@Autowired
|
||||
private lateinit var context: WebApplicationContext
|
||||
|
||||
private lateinit var mvc: MockMvc
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply<DefaultMockMvcBuilder>(springSecurity())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tenantFilterWhenHeaderMissingThenAccessDenied() {
|
||||
assertThatExceptionOfType(Exception::class.java)
|
||||
.isThrownBy { this.mvc.perform(get("/api").with(user("user"))).andReturn() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tenantFilterWhenHeaderPresentThenContinuesFilterChain() {
|
||||
this.mvc.perform(get("/api")
|
||||
.with(user("user"))
|
||||
.header("X-Tenant-Id", "some-tenant-id"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(authenticated().withUsername("user"))
|
||||
}
|
||||
|
||||
@Configuration
|
||||
open class UserDetailsConfig {
|
||||
@Bean
|
||||
open fun userDetailsService(): UserDetailsService {
|
||||
@Suppress("DEPRECATION")
|
||||
val user: UserDetails = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build()
|
||||
return InMemoryUserDetailsManager(user)
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
class ApiController {
|
||||
|
||||
@GetMapping("/api")
|
||||
fun api(): String {
|
||||
return "ok"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.addingcustomfilter
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class SecurityConfig {
|
||||
|
||||
// tag::snippet[]
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http
|
||||
// ...
|
||||
.addFilterAfter(TenantFilter(), AnonymousAuthenticationFilter::class.java) // <1>
|
||||
return http.build()
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.addingcustomfilter
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import java.io.IOException
|
||||
|
||||
// tag::snippet[]
|
||||
class TenantFilter : Filter {
|
||||
|
||||
@Throws(IOException::class, ServletException::class)
|
||||
override fun doFilter(servletRequest: ServletRequest, servletResponse: ServletResponse, filterChain: FilterChain) {
|
||||
val request = servletRequest as HttpServletRequest
|
||||
val response = servletResponse as HttpServletResponse
|
||||
|
||||
val tenantId = request.getHeader("X-Tenant-Id") // <1>
|
||||
val hasAccess = isUserAllowed(tenantId) // <2>
|
||||
if (hasAccess) {
|
||||
filterChain.doFilter(request, response) // <3>
|
||||
return
|
||||
}
|
||||
throw AccessDeniedException("Access denied") // <4>
|
||||
}
|
||||
|
||||
private fun isUserAllowed(tenantId: String?): Boolean {
|
||||
return "some-tenant-id" == tenantId
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.customizingfilter
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.invoke
|
||||
import org.springframework.security.config.test.SpringTestContext
|
||||
import org.springframework.security.config.test.SpringTestContextExtension
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
|
||||
/**
|
||||
* Tests for customizing security filters.
|
||||
*
|
||||
*/
|
||||
@ExtendWith(SpringTestContextExtension::class)
|
||||
class CustomizingFilterTests {
|
||||
|
||||
@JvmField
|
||||
val spring = SpringTestContext(this)
|
||||
|
||||
@Autowired
|
||||
lateinit var mvc: MockMvc
|
||||
|
||||
@Autowired
|
||||
lateinit var filterChainProxy: FilterChainProxy
|
||||
|
||||
@Test
|
||||
fun `filter chain when basic default then BasicAuthenticationFilter present`() {
|
||||
spring.register(SecurityConfigBasicDefault::class.java).autowire()
|
||||
val filters = filterChainProxy.getFilters("/")
|
||||
assertThat(filters).extracting("class").contains(BasicAuthenticationFilter::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `filter chain when custom filter then custom filter present`() {
|
||||
spring.register(SecurityConfigCustom::class.java).autowire()
|
||||
val filters = filterChainProxy.getFilters("/")
|
||||
assertThat(filters).extracting("class").contains(SecurityConfigCustom.MyBasicAuthenticationFilter::class.java)
|
||||
assertThat(filters).extracting("class").doesNotContain(BasicAuthenticationFilter::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `filter chain when incorrect then both filters present`() {
|
||||
spring.register(SecurityConfigIncorrect::class.java).autowire()
|
||||
val filters = filterChainProxy.getFilters("/")
|
||||
assertThat(filters).extracting("class").contains(BasicAuthenticationFilter::class.java)
|
||||
assertThat(filters).extracting("class").contains(SecurityConfigIncorrect.MyBasicAuthenticationFilter::class.java)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class SecurityConfigBasicDefault {
|
||||
|
||||
// tag::basic-default[]
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
httpBasic { }
|
||||
// ...
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
// end::basic-default[]
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class SecurityConfigCustom {
|
||||
|
||||
// tag::custom-filter[]
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
val basic = MyBasicAuthenticationFilter()
|
||||
// ... configure
|
||||
|
||||
http
|
||||
// ...
|
||||
.addFilterAt(basic, BasicAuthenticationFilter::class.java)
|
||||
|
||||
return http.build()
|
||||
}
|
||||
// end::custom-filter[]
|
||||
|
||||
class MyBasicAuthenticationFilter : Filter {
|
||||
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
|
||||
chain.doFilter(request, response)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration @EnableWebSecurity
|
||||
open class SecurityConfigDisable {
|
||||
|
||||
// tag::disable[]
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
httpBasic {
|
||||
disable()
|
||||
}
|
||||
// ...
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
// end::disable[]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class SecurityConfigIncorrect {
|
||||
|
||||
// tag::incorrect[]
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
val basic = MyBasicAuthenticationFilter()
|
||||
// ... configure
|
||||
|
||||
http {
|
||||
httpBasic { }
|
||||
}
|
||||
|
||||
// ... on no! BasicAuthenticationFilter is added twice!
|
||||
http.addFilterAt(basic, BasicAuthenticationFilter::class.java)
|
||||
|
||||
return http.build()
|
||||
}
|
||||
// end::incorrect[]
|
||||
|
||||
class MyBasicAuthenticationFilter : Filter {
|
||||
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
|
||||
chain.doFilter(request, response)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.requestcachepreventsavedrequest
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.invoke
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache
|
||||
|
||||
open class SecurityConfig {
|
||||
|
||||
// tag::snippet[]
|
||||
@Bean
|
||||
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
|
||||
val nullRequestCache = NullRequestCache()
|
||||
http {
|
||||
requestCache {
|
||||
requestCache = nullRequestCache
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.servletdelegatingfilterproxy;
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* A very simple implementation of a DelegatingFilterProxy.
|
||||
*/
|
||||
class SampleDelegatingFilterProxy(
|
||||
private val someBeanName: String,
|
||||
private var wac: StaticWebApplicationContext
|
||||
) : GenericFilterBean() {
|
||||
|
||||
// tag::dofilter[]
|
||||
@Throws(IOException::class, ServletException::class)
|
||||
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain?) {
|
||||
val delegate: Filter = getFilterBean(someBeanName) // <1>
|
||||
delegate.doFilter(request, response, chain) // <2>
|
||||
}
|
||||
// end::dofilter[]
|
||||
|
||||
private fun getFilterBean(someBeanName: String): Filter {
|
||||
return wac.getBean(someBeanName, Filter::class.java)
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.servletdelegatingfilterproxy
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.FilterConfig
|
||||
import jakarta.servlet.ServletContext
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.web.servlet.MockServletContext
|
||||
import org.springframework.util.Assert
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext
|
||||
import java.io.IOException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import java.util.Enumeration
|
||||
import java.util.Collections
|
||||
import kotlin.collections.LinkedHashMap
|
||||
|
||||
class SampleDelegatingFilterProxyTests {
|
||||
|
||||
@Test
|
||||
@Throws(ServletException::class, IOException::class)
|
||||
fun testFilter() {
|
||||
val sc: ServletContext = MockServletContext()
|
||||
val wac = StaticWebApplicationContext()
|
||||
wac.registerSingleton("targetFilter", MockFilter::class.java)
|
||||
wac.setServletContext(sc)
|
||||
wac.refresh()
|
||||
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac)
|
||||
|
||||
val targetFilter = wac.getBean("targetFilter") as MockFilter
|
||||
val proxyConfig = MockFilterConfig(sc)
|
||||
proxyConfig.addInitParameter("targetBeanName", "targetFilter")
|
||||
val filterProxy = SampleDelegatingFilterProxy("targetFilter", wac)
|
||||
filterProxy.init(proxyConfig)
|
||||
|
||||
val request = MockHttpServletRequest()
|
||||
val response = MockHttpServletResponse()
|
||||
filterProxy.doFilter(request, response, null)
|
||||
|
||||
assertThat(targetFilter.filterConfig).isNull()
|
||||
assertThat(request.getAttribute("called")).isEqualTo(true)
|
||||
|
||||
filterProxy.destroy()
|
||||
assertThat(targetFilter.filterConfig).isNull()
|
||||
}
|
||||
|
||||
private class MockFilter : Filter {
|
||||
var filterConfig: FilterConfig? = null
|
||||
|
||||
@Throws(ServletException::class)
|
||||
override fun init(filterConfig: FilterConfig) {
|
||||
this.filterConfig = filterConfig
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ServletException::class)
|
||||
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain?) {
|
||||
request.setAttribute("called", true)
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
filterConfig = null
|
||||
}
|
||||
}
|
||||
|
||||
private class MockFilterConfig(
|
||||
private val servletContext: ServletContext
|
||||
) : FilterConfig {
|
||||
private val filterName: String = ""
|
||||
private val initParameters = LinkedHashMap<String, String>()
|
||||
|
||||
override fun getFilterName(): String = filterName
|
||||
|
||||
override fun getServletContext(): ServletContext = servletContext
|
||||
|
||||
fun addInitParameter(name: String, value: String) {
|
||||
Assert.notNull(name, "Parameter name must not be null")
|
||||
initParameters[name] = value
|
||||
}
|
||||
|
||||
override fun getInitParameter(name: String): String? {
|
||||
Assert.notNull(name, "Parameter name must not be null")
|
||||
return initParameters[name]
|
||||
}
|
||||
|
||||
override fun getInitParameterNames(): Enumeration<String> =
|
||||
Collections.enumeration(initParameters.keys)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.kt.docs.servlet.servletsecurityfilters
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.invoke
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
// tag::snippet[]
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
csrf { }
|
||||
httpBasic { }
|
||||
formLogin { }
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippet[]
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2004-present the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- tag::snippet[] -->
|
||||
<http auto-config="true">
|
||||
<!-- ... -->
|
||||
<request-cache ref="nullRequestCache"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/>
|
||||
<!-- end::snippet[] -->
|
||||
|
||||
</b:beans>
|
||||
Reference in New Issue
Block a user