|
@@ -1,460 +1,5 @@
|
|
-[[test-mockmvc]]
|
|
|
|
-= Spring MVC Test Integration
|
|
|
|
-
|
|
|
|
-Spring Security provides comprehensive integration with https://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework[Spring MVC Test]
|
|
|
|
-
|
|
|
|
-[[test-mockmvc-setup]]
|
|
|
|
-== Setting Up MockMvc and Spring Security
|
|
|
|
-
|
|
|
|
-In order to use Spring Security with Spring MVC Test it is necessary to add the Spring Security `FilterChainProxy` as a `Filter`.
|
|
|
|
-It is also necessary to add Spring Security's `TestSecurityContextHolderPostProcessor` to support <<Running as a User in Spring MVC Test with Annotations,Running as a User in Spring MVC Test with Annotations>>.
|
|
|
|
-This can be done using Spring Security's `SecurityMockMvcConfigurers.springSecurity()`.
|
|
|
|
-For example:
|
|
|
|
-
|
|
|
|
-NOTE: Spring Security's testing support requires spring-test-4.1.3.RELEASE or greater.
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
|
|
|
|
-
|
|
|
|
-@RunWith(SpringJUnit4ClassRunner.class)
|
|
|
|
-@ContextConfiguration(classes = SecurityConfig.class)
|
|
|
|
-@WebAppConfiguration
|
|
|
|
-public class CsrfShowcaseTests {
|
|
|
|
-
|
|
|
|
- @Autowired
|
|
|
|
- private WebApplicationContext context;
|
|
|
|
-
|
|
|
|
- private MockMvc mvc;
|
|
|
|
-
|
|
|
|
- @Before
|
|
|
|
- public void setup() {
|
|
|
|
- mvc = MockMvcBuilders
|
|
|
|
- .webAppContextSetup(context)
|
|
|
|
- .apply(springSecurity()) // <1>
|
|
|
|
- .build();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-...
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-@RunWith(SpringJUnit4ClassRunner::class)
|
|
|
|
-@ContextConfiguration(classes = [SecurityConfig::class])
|
|
|
|
-@WebAppConfiguration
|
|
|
|
-class CsrfShowcaseTests {
|
|
|
|
-
|
|
|
|
- @Autowired
|
|
|
|
- private lateinit var context: WebApplicationContext
|
|
|
|
-
|
|
|
|
- private var mvc: MockMvc? = null
|
|
|
|
-
|
|
|
|
- @Before
|
|
|
|
- fun setup() {
|
|
|
|
- mvc = MockMvcBuilders
|
|
|
|
- .webAppContextSetup(context)
|
|
|
|
- .apply<DefaultMockMvcBuilder>(springSecurity()) // <1>
|
|
|
|
- .build()
|
|
|
|
- }
|
|
|
|
-// ...
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-<1> `SecurityMockMvcConfigurers.springSecurity()` will perform all of the initial setup we need to integrate Spring Security with Spring MVC Test
|
|
|
|
-
|
|
|
|
-[[test-mockmvc-smmrpp]]
|
|
|
|
-== SecurityMockMvcRequestPostProcessors
|
|
|
|
-
|
|
|
|
-Spring MVC Test provides a convenient interface called a `RequestPostProcessor` that can be used to modify a request.
|
|
|
|
-Spring Security provides a number of `RequestPostProcessor` implementations that make testing easier.
|
|
|
|
-In order to use Spring Security's `RequestPostProcessor` implementations ensure the following static import is used:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-[[test-mockmvc-csrf]]
|
|
|
|
-=== Testing with CSRF Protection
|
|
|
|
-
|
|
|
|
-When testing any non-safe HTTP methods and using Spring Security's CSRF protection, you must be sure to include a valid CSRF Token in the request.
|
|
|
|
-To specify a valid CSRF token as a request parameter using the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(post("/").with(csrf()))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.post("/") {
|
|
|
|
- with(csrf())
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-If you like you can include CSRF token in the header instead:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(post("/").with(csrf().asHeader()))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.post("/") {
|
|
|
|
- with(csrf().asHeader())
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-You can also test providing an invalid CSRF token using the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(post("/").with(csrf().useInvalidToken()))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.post("/") {
|
|
|
|
- with(csrf().useInvalidToken())
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-[[test-mockmvc-securitycontextholder]]
|
|
|
|
-=== Running a Test as a User in Spring MVC Test
|
|
|
|
-
|
|
|
|
-It is often desirable to run tests as a specific user.
|
|
|
|
-There are two simple ways of populating the user:
|
|
|
|
-
|
|
|
|
-* <<Running as a User in Spring MVC Test with RequestPostProcessor,Running as a User in Spring MVC Test with RequestPostProcessor>>
|
|
|
|
-* <<Running as a User in Spring MVC Test with Annotations,Running as a User in Spring MVC Test with Annotations>>
|
|
|
|
-
|
|
|
|
-[[test-mockmvc-securitycontextholder-rpp]]
|
|
|
|
-=== Running as a User in Spring MVC Test with RequestPostProcessor
|
|
|
|
-
|
|
|
|
-There are a number of options available to associate a user to the current `HttpServletRequest`.
|
|
|
|
-For example, the following will run as a user (which does not need to exist) with the username "user", the password "password", and the role "ROLE_USER":
|
|
|
|
-
|
|
|
|
-[NOTE]
|
|
|
|
-====
|
|
|
|
-The support works by associating the user to the `HttpServletRequest`.
|
|
|
|
-To associate the request to the `SecurityContextHolder` you need to ensure that the `SecurityContextPersistenceFilter` is associated with the `MockMvc` instance.
|
|
|
|
-A few ways to do this are:
|
|
|
|
-
|
|
|
|
-* Invoking <<test-mockmvc-setup,apply(springSecurity())>>
|
|
|
|
-* Adding Spring Security's `FilterChainProxy` to `MockMvc`
|
|
|
|
-* Manually adding `SecurityContextPersistenceFilter` to the `MockMvc` instance may make sense when using `MockMvcBuilders.standaloneSetup`
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(user("user")))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(user("user"))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-You can easily make customizations.
|
|
|
|
-For example, the following will run as a user (which does not need to exist) with the username "admin", the password "pass", and the roles "ROLE_USER" and "ROLE_ADMIN".
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/admin").with(user("admin").password("pass").roles("USER","ADMIN")))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/admin") {
|
|
|
|
- with(user("admin").password("pass").roles("USER","ADMIN"))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-If you have a custom `UserDetails` that you would like to use, you can easily specify that as well.
|
|
|
|
-For example, the following will use the specified `UserDetails` (which does not need to exist) to run with a `UsernamePasswordAuthenticationToken` that has a principal of the specified `UserDetails`:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(user(userDetails)))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(user(userDetails))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-You can run as anonymous user using the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(anonymous()))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(anonymous())
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-This is especially useful if you are running with a default user and wish to process a few requests as an anonymous user.
|
|
|
|
-
|
|
|
|
-If you want a custom `Authentication` (which does not need to exist) you can do so using the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(authentication(authentication)))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(authentication(authentication))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-You can even customize the `SecurityContext` using the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(securityContext(securityContext)))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(securityContext(securityContext))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-We can also ensure to run as a specific user for every request by using ``MockMvcBuilders``'s default request.
|
|
|
|
-For example, the following will run as a user (which does not need to exist) with the username "admin", the password "password", and the role "ROLE_ADMIN":
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc = MockMvcBuilders
|
|
|
|
- .webAppContextSetup(context)
|
|
|
|
- .defaultRequest(get("/").with(user("user").roles("ADMIN")))
|
|
|
|
- .apply(springSecurity())
|
|
|
|
- .build();
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc = MockMvcBuilders
|
|
|
|
- .webAppContextSetup(context)
|
|
|
|
- .defaultRequest<DefaultMockMvcBuilder>(get("/").with(user("user").roles("ADMIN")))
|
|
|
|
- .apply<DefaultMockMvcBuilder>(springSecurity())
|
|
|
|
- .build()
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-If you find you are using the same user in many of your tests, it is recommended to move the user to a method.
|
|
|
|
-For example, you can specify the following in your own class named `CustomSecurityMockMvcRequestPostProcessors`:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-public static RequestPostProcessor rob() {
|
|
|
|
- return user("rob").roles("ADMIN");
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-fun rob(): RequestPostProcessor {
|
|
|
|
- return user("rob").roles("ADMIN")
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-Now you can perform a static import on `SecurityMockMvcRequestPostProcessors` and use that within your tests:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-import static sample.CustomSecurityMockMvcRequestPostProcessors.*;
|
|
|
|
-
|
|
|
|
-...
|
|
|
|
-
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(rob()))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-import sample.CustomSecurityMockMvcRequestPostProcessors.*
|
|
|
|
-
|
|
|
|
-//...
|
|
|
|
-
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(rob())
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-==== Running as a User in Spring MVC Test with Annotations
|
|
|
|
-
|
|
|
|
-As an alternative to using a `RequestPostProcessor` to create your user, you can use annotations described in xref:servlet/test/method.adoc[Testing Method Security].
|
|
|
|
-For example, the following will run the test with the user with username "user", password "password", and role "ROLE_USER":
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-@Test
|
|
|
|
-@WithMockUser
|
|
|
|
-public void requestProtectedUrlWithUser() throws Exception {
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/"))
|
|
|
|
- ...
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-@Test
|
|
|
|
-@WithMockUser
|
|
|
|
-fun requestProtectedUrlWithUser() {
|
|
|
|
- mvc
|
|
|
|
- .get("/")
|
|
|
|
- // ...
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-Alternatively, the following will run the test with the user with username "user", password "password", and role "ROLE_ADMIN":
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-@Test
|
|
|
|
-@WithMockUser(roles="ADMIN")
|
|
|
|
-public void requestProtectedUrlWithUser() throws Exception {
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/"))
|
|
|
|
- ...
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-@Test
|
|
|
|
-@WithMockUser(roles = ["ADMIN"])
|
|
|
|
-fun requestProtectedUrlWithUser() {
|
|
|
|
- mvc
|
|
|
|
- .get("/")
|
|
|
|
- // ...
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-=== Testing HTTP Basic Authentication
|
|
|
|
-
|
|
|
|
-While it has always been possible to authenticate with HTTP Basic, it was a bit tedious to remember the header name, format, and encode the values.
|
|
|
|
-Now this can be done using Spring Security's `httpBasic` `RequestPostProcessor`.
|
|
|
|
-For example, the snippet below:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(get("/").with(httpBasic("user","password")))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc.get("/") {
|
|
|
|
- with(httpBasic("user","password"))
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-will attempt to use HTTP Basic to authenticate a user with the username "user" and the password "password" by ensuring the following header is populated on the HTTP Request:
|
|
|
|
-
|
|
|
|
-[source,text]
|
|
|
|
-----
|
|
|
|
-Authorization: Basic dXNlcjpwYXNzd29yZA==
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
[[testing-oauth2]]
|
|
[[testing-oauth2]]
|
|
-=== Testing OAuth 2.0
|
|
|
|
|
|
+= Testing OAuth 2.0
|
|
|
|
|
|
When it comes to OAuth 2.0, the same principles covered earlier still apply: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`.
|
|
When it comes to OAuth 2.0, the same principles covered earlier still apply: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`.
|
|
|
|
|
|
@@ -507,12 +52,12 @@ fun foo(@AuthenticationPrincipal user: OidcUser): String {
|
|
then Spring Security's test support can come in handy.
|
|
then Spring Security's test support can come in handy.
|
|
|
|
|
|
[[testing-oidc-login]]
|
|
[[testing-oidc-login]]
|
|
-=== Testing OIDC Login
|
|
|
|
|
|
+== Testing OIDC Login
|
|
|
|
|
|
Testing the method above with Spring MVC Test would require simulating some kind of grant flow with an authorization server.
|
|
Testing the method above with Spring MVC Test would require simulating some kind of grant flow with an authorization server.
|
|
Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.
|
|
Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.
|
|
|
|
|
|
-For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockMvcRequestPostProcessors#oidcLogin` method, like so:
|
|
|
|
|
|
+For example, we can tell Spring Security to include a default `OidcUser` using the `oidcLogin` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`], like so:
|
|
|
|
|
|
====
|
|
====
|
|
.Java
|
|
.Java
|
|
@@ -589,7 +134,7 @@ Further, it also links that `OidcUser` to a simple instance of `OAuth2Authorized
|
|
This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>..
|
|
This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>..
|
|
|
|
|
|
[[testing-oidc-login-authorities]]
|
|
[[testing-oidc-login-authorities]]
|
|
-==== Configuring Authorities
|
|
|
|
|
|
+== Configuring Authorities
|
|
|
|
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
|
|
|
|
@@ -619,7 +164,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oidc-login-claims]]
|
|
[[testing-oidc-login-claims]]
|
|
-==== Configuring Claims
|
|
|
|
|
|
+== Configuring Claims
|
|
|
|
|
|
And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
|
|
And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
|
|
|
|
|
|
@@ -678,7 +223,7 @@ mvc.get("/endpoint") {
|
|
since `OidcUser` collects its claims from `OidcIdToken`.
|
|
since `OidcUser` collects its claims from `OidcIdToken`.
|
|
|
|
|
|
[[testing-oidc-login-user]]
|
|
[[testing-oidc-login-user]]
|
|
-==== Additional Configurations
|
|
|
|
|
|
+== Additional Configurations
|
|
|
|
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
|
|
|
|
@@ -724,7 +269,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oauth2-login]]
|
|
[[testing-oauth2-login]]
|
|
-=== Testing OAuth 2.0 Login
|
|
|
|
|
|
+== Testing OAuth 2.0 Login
|
|
|
|
|
|
As with <<testing-oidc-login,testing OIDC login>>, testing OAuth 2.0 Login presents a similar challenge of mocking a grant flow.
|
|
As with <<testing-oidc-login,testing OIDC login>>, testing OAuth 2.0 Login presents a similar challenge of mocking a grant flow.
|
|
And because of that, Spring Security also has test support for non-OIDC use cases.
|
|
And because of that, Spring Security also has test support for non-OIDC use cases.
|
|
@@ -751,7 +296,7 @@ fun foo(@AuthenticationPrincipal oauth2User: OAuth2User): String? {
|
|
----
|
|
----
|
|
====
|
|
====
|
|
|
|
|
|
-In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockMvcRequestPostProcessors#oauth2User` method, like so:
|
|
|
|
|
|
+In that case, we can tell Spring Security to include a default `OAuth2User` using the `oauth2User` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`], like so:
|
|
|
|
|
|
====
|
|
====
|
|
.Java
|
|
.Java
|
|
@@ -812,7 +357,7 @@ Further, it also links that `OAuth2User` to a simple instance of `OAuth2Authoriz
|
|
This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>.
|
|
This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>.
|
|
|
|
|
|
[[testing-oauth2-login-authorities]]
|
|
[[testing-oauth2-login-authorities]]
|
|
-==== Configuring Authorities
|
|
|
|
|
|
+== Configuring Authorities
|
|
|
|
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
|
|
|
|
@@ -842,7 +387,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oauth2-login-claims]]
|
|
[[testing-oauth2-login-claims]]
|
|
-==== Configuring Claims
|
|
|
|
|
|
+== Configuring Claims
|
|
|
|
|
|
And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
|
|
And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
|
|
|
|
|
|
@@ -897,7 +442,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oauth2-login-user]]
|
|
[[testing-oauth2-login-user]]
|
|
-==== Additional Configurations
|
|
|
|
|
|
+== Additional Configurations
|
|
|
|
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
|
|
|
|
@@ -942,7 +487,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oauth2-client]]
|
|
[[testing-oauth2-client]]
|
|
-=== Testing OAuth 2.0 Clients
|
|
|
|
|
|
+== Testing OAuth 2.0 Clients
|
|
|
|
|
|
Independent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing.
|
|
Independent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing.
|
|
For example, your controller may be relying on the client credentials grant to get a token that isn't associated with the user at all:
|
|
For example, your controller may be relying on the client credentials grant to get a token that isn't associated with the user at all:
|
|
@@ -976,7 +521,7 @@ fun foo(@RegisteredOAuth2AuthorizedClient("my-app") authorizedClient: OAuth2Auth
|
|
====
|
|
====
|
|
|
|
|
|
Simulating this handshake with the authorization server could be cumbersome.
|
|
Simulating this handshake with the authorization server could be cumbersome.
|
|
-Instead, you can use `SecurityMockMvcRequestPostProcessor#oauth2Client` to add a `OAuth2AuthorizedClient` into a mock `OAuth2AuthorizedClientRepository`:
|
|
|
|
|
|
+Instead, you can use the `oauth2Client` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] to add a `OAuth2AuthorizedClient` into a mock `OAuth2AuthorizedClientRepository`:
|
|
|
|
|
|
====
|
|
====
|
|
.Java
|
|
.Java
|
|
@@ -1054,7 +599,7 @@ assertThat(authorizedClient.accessToken.scopes).containsExactly("read")
|
|
The client can then be retrieved as normal using `@RegisteredOAuth2AuthorizedClient` in a controller method.
|
|
The client can then be retrieved as normal using `@RegisteredOAuth2AuthorizedClient` in a controller method.
|
|
|
|
|
|
[[testing-oauth2-client-scopes]]
|
|
[[testing-oauth2-client-scopes]]
|
|
-==== Configuring Scopes
|
|
|
|
|
|
+== Configuring Scopes
|
|
|
|
|
|
In many circumstances, the OAuth 2.0 access token comes with a set of scopes.
|
|
In many circumstances, the OAuth 2.0 access token comes with a set of scopes.
|
|
If your controller inspects these, say like so:
|
|
If your controller inspects these, say like so:
|
|
@@ -1121,7 +666,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-oauth2-client-registration]]
|
|
[[testing-oauth2-client-registration]]
|
|
-==== Additional Configurations
|
|
|
|
|
|
+== Additional Configurations
|
|
|
|
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
|
|
|
|
|
|
@@ -1167,7 +712,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-jwt]]
|
|
[[testing-jwt]]
|
|
-=== Testing JWT Authentication
|
|
|
|
|
|
+== Testing JWT Authentication
|
|
|
|
|
|
In order to make an authorized request on a resource server, you need a bearer token.
|
|
In order to make an authorized request on a resource server, you need a bearer token.
|
|
|
|
|
|
@@ -1177,9 +722,9 @@ All of this can be quite daunting, especially when this isn't the focus of your
|
|
Fortunately, there are a number of simple ways that you can overcome this difficulty and allow your tests to focus on authorization and not on representing bearer tokens.
|
|
Fortunately, there are a number of simple ways that you can overcome this difficulty and allow your tests to focus on authorization and not on representing bearer tokens.
|
|
We'll look at two of them now:
|
|
We'll look at two of them now:
|
|
|
|
|
|
-==== `jwt() RequestPostProcessor`
|
|
|
|
|
|
+== `jwt() RequestPostProcessor`
|
|
|
|
|
|
-The first way is via a `RequestPostProcessor`.
|
|
|
|
|
|
+The first way is via the `jwt` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`].
|
|
The simplest of these would look something like this:
|
|
The simplest of these would look something like this:
|
|
|
|
|
|
====
|
|
====
|
|
@@ -1357,9 +902,9 @@ mvc.get("/endpoint") {
|
|
----
|
|
----
|
|
====
|
|
====
|
|
|
|
|
|
-==== `authentication()` `RequestPostProcessor`
|
|
|
|
|
|
+== `authentication()` `RequestPostProcessor`
|
|
|
|
|
|
-The second way is by using the `authentication()` `RequestPostProcessor`.
|
|
|
|
|
|
+The second way is by using the `authentication()` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`].
|
|
Essentially, you can instantiate your own `JwtAuthenticationToken` and provide it in your test, like so:
|
|
Essentially, you can instantiate your own `JwtAuthenticationToken` and provide it in your test, like so:
|
|
|
|
|
|
====
|
|
====
|
|
@@ -1399,7 +944,7 @@ mvc.get("/endpoint") {
|
|
Note that as an alternative to these, you can also mock the `JwtDecoder` bean itself with a `@MockBean` annotation.
|
|
Note that as an alternative to these, you can also mock the `JwtDecoder` bean itself with a `@MockBean` annotation.
|
|
|
|
|
|
[[testing-opaque-token]]
|
|
[[testing-opaque-token]]
|
|
-=== Testing Opaque Token Authentication
|
|
|
|
|
|
+== Testing Opaque Token Authentication
|
|
|
|
|
|
Similar to <<testing-jwt,JWTs>>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult.
|
|
Similar to <<testing-jwt,JWTs>>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult.
|
|
To help with that, Spring Security has test support for opaque tokens.
|
|
To help with that, Spring Security has test support for opaque tokens.
|
|
@@ -1426,7 +971,7 @@ fun foo(authentication: BearerTokenAuthentication): String {
|
|
----
|
|
----
|
|
====
|
|
====
|
|
|
|
|
|
-In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockMvcRequestPostProcessors#opaqueToken` method, like so:
|
|
|
|
|
|
+In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `opaqueToken` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] method, like so:
|
|
|
|
|
|
====
|
|
====
|
|
.Java
|
|
.Java
|
|
@@ -1484,7 +1029,7 @@ assertThat(token.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read
|
|
Spring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods.
|
|
Spring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods.
|
|
|
|
|
|
[[testing-opaque-token-authorities]]
|
|
[[testing-opaque-token-authorities]]
|
|
-==== Configuring Authorities
|
|
|
|
|
|
+== Configuring Authorities
|
|
|
|
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
|
|
|
|
|
|
@@ -1514,7 +1059,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-opaque-token-attributes]]
|
|
[[testing-opaque-token-attributes]]
|
|
-==== Configuring Claims
|
|
|
|
|
|
+== Configuring Claims
|
|
|
|
|
|
And while granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0.
|
|
And while granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0.
|
|
|
|
|
|
@@ -1569,7 +1114,7 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
[[testing-opaque-token-principal]]
|
|
[[testing-opaque-token-principal]]
|
|
-==== Additional Configurations
|
|
|
|
|
|
+== Additional Configurations
|
|
|
|
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects.
|
|
There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects.
|
|
|
|
|
|
@@ -1615,339 +1160,3 @@ mvc.get("/endpoint") {
|
|
====
|
|
====
|
|
|
|
|
|
Note that as an alternative to using `opaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation.
|
|
Note that as an alternative to using `opaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation.
|
|
-
|
|
|
|
-== SecurityMockMvcRequestBuilders
|
|
|
|
-
|
|
|
|
-Spring MVC Test also provides a `RequestBuilder` interface that can be used to create the `MockHttpServletRequest` used in your test.
|
|
|
|
-Spring Security provides a few `RequestBuilder` implementations that can be used to make testing easier.
|
|
|
|
-In order to use Spring Security's `RequestBuilder` implementations ensure the following static import is used:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-=== Testing Form Based Authentication
|
|
|
|
-
|
|
|
|
-You can easily create a request to test a form based authentication using Spring Security's testing support.
|
|
|
|
-For example, the following will submit a POST to "/login" with the username "user", the password "password", and a valid CSRF token:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-It is easy to customize the request.
|
|
|
|
-For example, the following will submit a POST to "/auth" with the username "admin", the password "pass", and a valid CSRF token:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin("/auth").user("admin").password("pass"))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin("/auth").user("admin").password("pass"))
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-We can also customize the parameters names that the username and password are included on.
|
|
|
|
-For example, this is the above request modified to include the username on the HTTP parameter "u" and the password on the HTTP parameter "p".
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin("/auth").user("u","admin").password("p","pass"))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin("/auth").user("u","admin").password("p","pass"))
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-[[test-logout]]
|
|
|
|
-=== Testing Logout
|
|
|
|
-
|
|
|
|
-While fairly trivial using standard Spring MVC Test, you can use Spring Security's testing support to make testing log out easier.
|
|
|
|
-For example, the following will submit a POST to "/logout" with a valid CSRF token:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(logout())
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(logout())
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-You can also customize the URL to post to.
|
|
|
|
-For example, the snippet below will submit a POST to "/signout" with a valid CSRF token:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(logout("/signout"))
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(logout("/signout"))
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-== SecurityMockMvcResultMatchers
|
|
|
|
-
|
|
|
|
-At times it is desirable to make various security related assertions about a request.
|
|
|
|
-To accommodate this need, Spring Security Test support implements Spring MVC Test's `ResultMatcher` interface.
|
|
|
|
-In order to use Spring Security's `ResultMatcher` implementations ensure the following static import is used:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*
|
|
|
|
-
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-=== Unauthenticated Assertion
|
|
|
|
-
|
|
|
|
-At times it may be valuable to assert that there is no authenticated user associated with the result of a `MockMvc` invocation.
|
|
|
|
-For example, you might want to test submitting an invalid username and password and verify that no user is authenticated.
|
|
|
|
-You can easily do this with Spring Security's testing support using something like the following:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().password("invalid"))
|
|
|
|
- .andExpect(unauthenticated());
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().password("invalid"))
|
|
|
|
- .andExpect { unauthenticated() }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-=== Authenticated Assertion
|
|
|
|
-
|
|
|
|
-It is often times that we must assert that an authenticated user exists.
|
|
|
|
-For example, we may want to verify that we authenticated successfully.
|
|
|
|
-We could verify that a form based login was successful with the following snippet of code:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
- .andExpect(authenticated());
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
- .andExpect { authenticated() }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-If we wanted to assert the roles of the user, we could refine our previous code as shown below:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().user("admin"))
|
|
|
|
- .andExpect(authenticated().withRoles("USER","ADMIN"));
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
- .andExpect { authenticated().withRoles("USER","ADMIN") }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-Alternatively, we could verify the username:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().user("admin"))
|
|
|
|
- .andExpect(authenticated().withUsername("admin"));
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().user("admin"))
|
|
|
|
- .andExpect { authenticated().withUsername("admin") }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-We can also combine the assertions:
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().user("admin"))
|
|
|
|
- .andExpect(authenticated().withUsername("admin").withRoles("USER", "ADMIN"));
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin().user("admin"))
|
|
|
|
- .andExpect { authenticated().withUsername("admin").withRoles("USER", "ADMIN") }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-We can also make arbitrary assertions on the authentication
|
|
|
|
-
|
|
|
|
-====
|
|
|
|
-.Java
|
|
|
|
-[source,java,role="primary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
- .andExpect(authenticated().withAuthentication(auth ->
|
|
|
|
- assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class)));
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-.Kotlin
|
|
|
|
-[source,kotlin,role="secondary"]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(formLogin())
|
|
|
|
- .andExpect {
|
|
|
|
- authenticated().withAuthentication { auth ->
|
|
|
|
- assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken::class.java) }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-----
|
|
|
|
-====
|
|
|
|
-
|
|
|
|
-=== SecurityMockMvcResultHandlers
|
|
|
|
-
|
|
|
|
-Spring Security provides a few ``ResultHandler``s implementations.
|
|
|
|
-In order to use Spring Security's ``ResultHandler``s implementations ensure the following static import is used:
|
|
|
|
-
|
|
|
|
-[source,java]
|
|
|
|
-----
|
|
|
|
-import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*;
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-==== Exporting the SecurityContext
|
|
|
|
-
|
|
|
|
-Often times we want to query a repository to see if some `MockMvc` request actually persisted in the database.
|
|
|
|
-In some cases our repository query uses the xref:features/integrations/data.adoc[Spring Data Integration] to filter the results based on current user's username or any other property.
|
|
|
|
-Let's see an example:
|
|
|
|
-
|
|
|
|
-A repository interface:
|
|
|
|
-[source,java]
|
|
|
|
-----
|
|
|
|
-private interface MessageRepository extends JpaRepository<Message, Long> {
|
|
|
|
- @Query("SELECT m.content FROM Message m WHERE m.sentBy = ?#{ principal?.name }")
|
|
|
|
- List<String> findAllUserMessages();
|
|
|
|
-}
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-Our test scenario:
|
|
|
|
-
|
|
|
|
-[source,java]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(post("/message")
|
|
|
|
- .content("New Message")
|
|
|
|
- .contentType(MediaType.TEXT_PLAIN)
|
|
|
|
- )
|
|
|
|
- .andExpect(status().isOk());
|
|
|
|
-
|
|
|
|
-List<String> userMessages = messageRepository.findAllUserMessages();
|
|
|
|
-assertThat(userMessages).hasSize(1);
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-This test won't pass because after our request finishes, the `SecurityContextHolder` will be cleared out by the filter chain.
|
|
|
|
-We can then export the `TestSecurityContextHolder` to our `SecurityContextHolder` and use it as we want:
|
|
|
|
-
|
|
|
|
-[source,java]
|
|
|
|
-----
|
|
|
|
-mvc
|
|
|
|
- .perform(post("/message")
|
|
|
|
- .content("New Message")
|
|
|
|
- .contentType(MediaType.TEXT_PLAIN)
|
|
|
|
- )
|
|
|
|
- .andDo(exportTestSecurityContext())
|
|
|
|
- .andExpect(status().isOk());
|
|
|
|
-
|
|
|
|
-List<String> userMessages = messageRepository.findAllUserMessages();
|
|
|
|
-assertThat(userMessages).hasSize(1);
|
|
|
|
-----
|
|
|
|
-
|
|
|
|
-[NOTE]
|
|
|
|
-====
|
|
|
|
-Remember to clear the `SecurityContextHolder` between your tests, or it may leak amongst them
|
|
|
|
-====
|
|
|