Since spring 4, this solution has a formLogin layout and logout using sessions, not cookies, because the spring security test does not return cookies.
Since this is not the best practice for test inheritance, you can @Autowire this component in your tests and call it methods.
With this solution, each operation on mockMvc will be called as authenticated if you called performLogin at the end of the test, which you can call performLogout .
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.stereotype.Component; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import javax.servlet.Filter; import static com.condix.SessionLogoutRequestBuilder.sessionLogout; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; 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.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Component public class SessionBasedMockMvc { private static final String HOME_PATH = "/"; private static final String LOGOUT_PATH = "/login?logout"; @Autowired private WebApplicationContext webApplicationContext; @Autowired private Filter springSecurityFilterChain; private MockMvc mockMvc; public MockMvc createSessionBasedMockMvc() { final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path"); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext) .defaultRequest(defaultRequestBuilder) .alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest())) .apply(springSecurity(springSecurityFilterChain)) .build(); return this.mockMvc; } public void performLogin(final String username, final String password) throws Exception { final ResultActions resultActions = this.mockMvc.perform(formLogin().user(username).password(password)); this.assertSuccessLogin(resultActions); } public void performLogout() throws Exception { final ResultActions resultActions = this.mockMvc.perform(sessionLogout()); this.assertSuccessLogout(resultActions); } private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder, final MockHttpServletRequest request) { requestBuilder.session((MockHttpSession) request.getSession()); return request; } private void assertSuccessLogin(final ResultActions resultActions) throws Exception { resultActions.andExpect(status().isFound()) .andExpect(authenticated()) .andExpect(redirectedUrl(HOME_PATH)); } private void assertSuccessLogout(final ResultActions resultActions) throws Exception { resultActions.andExpect(status().isFound()) .andExpect(unauthenticated()) .andExpect(redirectedUrl(LOGOUT_PATH)); } }
Since LogoutRequestBuilder does not support a session by default, we need to create another exit request constructor.
import org.springframework.beans.Mergeable; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import javax.servlet.ServletContext; import java.util.ArrayList; import java.util.List; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; public final class SessionLogoutRequestBuilder implements ConfigurableSmartRequestBuilder<SessionLogoutRequestBuilder>, Mergeable { private final List<RequestPostProcessor> postProcessors = new ArrayList<>(); private String logoutUrl = "/logout"; private MockHttpSession session; private SessionLogoutRequestBuilder() { this.postProcessors.add(csrf()); } static SessionLogoutRequestBuilder sessionLogout() { return new SessionLogoutRequestBuilder(); } @Override public MockHttpServletRequest buildRequest(final ServletContext servletContext) { return post(this.logoutUrl).session(session).buildRequest(servletContext); } public SessionLogoutRequestBuilder logoutUrl(final String logoutUrl) { this.logoutUrl = logoutUrl; return this; } public SessionLogoutRequestBuilder session(final MockHttpSession session) { Assert.notNull(session, "'session' must not be null"); this.session = session; return this; } @Override public boolean isMergeEnabled() { return true; } @SuppressWarnings("unchecked") @Override public Object merge(final Object parent) { if (parent == null) { return this; } if (parent instanceof MockHttpServletRequestBuilder) { final MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent; if (this.session == null) { this.session = (MockHttpSession) ReflectionTestUtils.getField(parentBuilder, "session"); } final List postProcessors = (List) ReflectionTestUtils.getField(parentBuilder, "postProcessors"); this.postProcessors.addAll(0, (List<RequestPostProcessor>) postProcessors); } else if (parent instanceof SessionLogoutRequestBuilder) { final SessionLogoutRequestBuilder parentBuilder = (SessionLogoutRequestBuilder) parent; if (!StringUtils.hasText(this.logoutUrl)) { this.logoutUrl = parentBuilder.logoutUrl; } if (this.session == null) { this.session = parentBuilder.session; } this.postProcessors.addAll(0, parentBuilder.postProcessors); } else { throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]"); } return this; } @Override public SessionLogoutRequestBuilder with(final RequestPostProcessor postProcessor) { Assert.notNull(postProcessor, "postProcessor is required"); this.postProcessors.add(postProcessor); return this; } @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { for (final RequestPostProcessor postProcessor : this.postProcessors) { request = postProcessor.postProcessRequest(request); if (request == null) { throw new IllegalStateException( "Post-processor [" + postProcessor.getClass().getName() + "] returned null"); } } return request; } }
After the performLogin operation is performLogin all your request in the test will be automatically executed as a registered user.
Nagy Attila Nov 02 '17 at 7:15 2017-11-02 07:15
source share