SEC-2593: Support stateless mode in Spring Security Test
This commit is contained in:
parent
626b521c0e
commit
7e3e821db1
|
@ -233,12 +233,17 @@ public final class SecurityMockMvcRequestPostProcessors {
|
||||||
*/
|
*/
|
||||||
final void save(SecurityContext securityContext,
|
final void save(SecurityContext securityContext,
|
||||||
HttpServletRequest request) {
|
HttpServletRequest request) {
|
||||||
|
SecurityContextRepository securityContextRepository = WebTestUtils.getSecurityContextRepository(request);
|
||||||
|
boolean isTestRepository = securityContextRepository instanceof TestSecurityContextRepository;
|
||||||
|
if(!isTestRepository) {
|
||||||
|
securityContextRepository = new TestSecurityContextRepository(securityContextRepository);
|
||||||
|
WebTestUtils.setSecurityContextRepository(request, securityContextRepository);
|
||||||
|
}
|
||||||
|
|
||||||
HttpServletResponse response = new MockHttpServletResponse();
|
HttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(
|
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(
|
||||||
request, response);
|
request, response);
|
||||||
SecurityContextRepository securityContextRepository = WebTestUtils
|
|
||||||
.getSecurityContextRepository(request);
|
|
||||||
securityContextRepository.loadContext(requestResponseHolder);
|
securityContextRepository.loadContext(requestResponseHolder);
|
||||||
|
|
||||||
request = requestResponseHolder.getRequest();
|
request = requestResponseHolder.getRequest();
|
||||||
|
@ -247,6 +252,43 @@ public final class SecurityMockMvcRequestPostProcessors {
|
||||||
securityContextRepository.saveContext(securityContext, request,
|
securityContextRepository.saveContext(securityContext, request,
|
||||||
response);
|
response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to wrap the SecurityContextRepository to provide support for testing in stateless mode
|
||||||
|
*/
|
||||||
|
private static class TestSecurityContextRepository implements SecurityContextRepository {
|
||||||
|
private final String ATTR_NAME = TestSecurityContextRepository.class.getName().concat(".REPO");
|
||||||
|
|
||||||
|
private final SecurityContextRepository delegate;
|
||||||
|
|
||||||
|
private TestSecurityContextRepository(SecurityContextRepository delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
|
||||||
|
SecurityContext result = getContext(requestResponseHolder.getRequest());
|
||||||
|
// always load from the delegate to ensure the request/response in the holder are updated
|
||||||
|
// remember the SecurityContextRepository is used in many different locations
|
||||||
|
SecurityContext delegateResult = delegate.loadContext(requestResponseHolder);
|
||||||
|
return result == null ? delegateResult : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
request.setAttribute(ATTR_NAME, context);
|
||||||
|
delegate.saveContext(context, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsContext(HttpServletRequest request) {
|
||||||
|
return getContext(request) != null || delegate.containsContext(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecurityContext getContext(HttpServletRequest request) {
|
||||||
|
return (SecurityContext) request.getAttribute(ATTR_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -61,6 +61,25 @@ public abstract class WebTestUtils {
|
||||||
return (SecurityContextRepository) ReflectionTestUtils.getField(filter, "repo");
|
return (SecurityContextRepository) ReflectionTestUtils.getField(filter, "repo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link SecurityContextRepository} for the specified
|
||||||
|
* {@link HttpServletRequest}.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* the {@link HttpServletRequest} to obtain the
|
||||||
|
* {@link SecurityContextRepository}
|
||||||
|
* @param securityContextRepository
|
||||||
|
* the {@link SecurityContextRepository} to set
|
||||||
|
* @return the {@link SecurityContextRepository} for the specified
|
||||||
|
* {@link HttpServletRequest}
|
||||||
|
*/
|
||||||
|
public static void setSecurityContextRepository(HttpServletRequest request, SecurityContextRepository securityContextRepository) {
|
||||||
|
SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);
|
||||||
|
if(filter != null) {
|
||||||
|
ReflectionTestUtils.setField(filter, "repo", securityContextRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link CsrfTokenRepository} for the specified
|
* Gets the {@link CsrfTokenRepository} for the specified
|
||||||
* {@link HttpServletRequest}. If one is not found, the default
|
* {@link HttpServletRequest}. If one is not found, the default
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2014 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
|
||||||
|
*
|
||||||
|
* http://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.test.web.servlet.request;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
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.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration
|
||||||
|
@WebAppConfiguration
|
||||||
|
public class SecurityMockMvcRequestPostProcessorsAuthenticationStatelessTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebApplicationContext context;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Filter springSecurityFilterChain;
|
||||||
|
|
||||||
|
private MockMvc mvc;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mvc = MockMvcBuilders
|
||||||
|
.webAppContextSetup(context)
|
||||||
|
.addFilters(springSecurityFilterChain)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void userRequestPostProcessorWorksWithStateless() throws Exception {
|
||||||
|
mvc
|
||||||
|
.perform(get("/").with(user("user")))
|
||||||
|
.andExpect(status().is2xxSuccessful());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvcSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class Config extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
super.configure(http);
|
||||||
|
|
||||||
|
http
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth
|
||||||
|
.inMemoryAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class Controller {
|
||||||
|
@RequestMapping
|
||||||
|
public String hello() {
|
||||||
|
return "Hello";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue