2
0
Эх сурвалжийг харах

SEC-2593: Support stateless mode in Spring Security Test

Rob Winch 11 жил өмнө
parent
commit
7e3e821db1

+ 44 - 2
test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

@@ -233,12 +233,17 @@ public final class SecurityMockMvcRequestPostProcessors {
          */
         final void save(SecurityContext securityContext,
                 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();
 
             HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(
                     request, response);
-            SecurityContextRepository securityContextRepository = WebTestUtils
-                    .getSecurityContextRepository(request);
             securityContextRepository.loadContext(requestResponseHolder);
 
             request = requestResponseHolder.getRequest();
@@ -247,6 +252,43 @@ public final class SecurityMockMvcRequestPostProcessors {
             securityContextRepository.saveContext(securityContext, request,
                     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);
+            }
+        }
     }
 
     /**

+ 19 - 0
test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java

@@ -61,6 +61,25 @@ public abstract class WebTestUtils {
         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
      * {@link HttpServletRequest}. If one is not found, the default

+ 100 - 0
test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationStatelessTests.java

@@ -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";
+            }
+        }
+    }
+}