diff --git a/src/main/java/com/eactive/testmaster/api/controller/MockApiHandlerMapping.java b/src/main/java/com/eactive/testmaster/api/controller/MockApiHandlerMapping.java index 5eb84fc..c909a15 100644 --- a/src/main/java/com/eactive/testmaster/api/controller/MockApiHandlerMapping.java +++ b/src/main/java/com/eactive/testmaster/api/controller/MockApiHandlerMapping.java @@ -2,6 +2,7 @@ package com.eactive.testmaster.api.controller; import com.eactive.testmaster.api.entity.MockRoute; import com.eactive.testmaster.api.service.MockRouteService; +import com.eactive.testmaster.config.CsrfExclusionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,9 @@ public class MockApiHandlerMapping { @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping; + @Autowired + private CsrfExclusionService csrfExclusionService; + @PostConstruct public void initialize() { mockRouteService.initAllRoutes(); @@ -43,6 +47,9 @@ public class MockApiHandlerMapping { try { requestMappingHandlerMapping.registerMapping(mappingInfo, mockApiController, MockApiController.class.getDeclaredMethod("handleRequest", HttpServletRequest.class)); + if (mockRoute.getMethod().equalsIgnoreCase("POST") || mockRoute.getMethod().equalsIgnoreCase("PUT")) { + csrfExclusionService.addExcludedPath(mockRoute.getPathPattern()); + } logger.info("Registered mapping for {} {}", mockRoute.getMethod(), mockRoute.getPathPattern()); } catch (NoSuchMethodException e) { logger.error("Error registering mapping", e); @@ -54,7 +61,9 @@ public class MockApiHandlerMapping { .paths(mockRoute.getPathPattern()) .methods(RequestMethod.valueOf(mockRoute.getMethod())) .build(); - + if (mockRoute.getMethod().equalsIgnoreCase("POST") || mockRoute.getMethod().equalsIgnoreCase("PUT")) { + csrfExclusionService.removeExcludedPath(mockRoute.getPathPattern()); + } requestMappingHandlerMapping.unregisterMapping(mappingInfo); logger.info("Unregistered mapping for {} {}", mockRoute.getMethod(), mockRoute.getPathPattern()); } diff --git a/src/main/java/com/eactive/testmaster/config/CsrfExclusionService.java b/src/main/java/com/eactive/testmaster/config/CsrfExclusionService.java new file mode 100644 index 0000000..b94bf26 --- /dev/null +++ b/src/main/java/com/eactive/testmaster/config/CsrfExclusionService.java @@ -0,0 +1,23 @@ +package com.eactive.testmaster.config; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.stereotype.Service; + +@Service +public class CsrfExclusionService { + private Set excludedPaths = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + public void addExcludedPath(String path) { + excludedPaths.add(path); + } + + public void removeExcludedPath(String path) { + excludedPaths.remove(path); + } + + public boolean shouldExclude(String path) { + return excludedPaths.contains(path); + } +} diff --git a/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java b/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java index 9af6c51..4dcf431 100644 --- a/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java +++ b/src/main/java/com/eactive/testmaster/config/PortalConfigSecurity.java @@ -2,6 +2,8 @@ package com.eactive.testmaster.config; import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -12,7 +14,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; @Configuration @EnableWebSecurity @@ -25,13 +30,14 @@ public class PortalConfigSecurity { private final PortalAuthenticationManager portalAuthenticationManager; - @Autowired - public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, - PortalAuthenticationSuccessHandler authenticationSuccessHandler, - PortalAuthenticationManager portalAuthenticationManager) { + private final CsrfExclusionService csrfExclusionService; + + public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, PortalAuthenticationSuccessHandler authenticationSuccessHandler, PortalAuthenticationManager portalAuthenticationManager, + CsrfExclusionService csrfExclusionService) { this.authenticationFailureHandler = authenticationFailureHandler; this.authenticationSuccessHandler = authenticationSuccessHandler; this.portalAuthenticationManager = portalAuthenticationManager; + this.csrfExclusionService = csrfExclusionService; } @Bean @@ -44,6 +50,29 @@ public class PortalConfigSecurity { return registrationBean; } + @Bean + public CsrfTokenRepository csrfTokenRepository(CsrfExclusionService csrfExclusionService) { +// HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); + CookieCsrfTokenRepository repository = new CookieCsrfTokenRepository(); + repository.setCookieHttpOnly(false); + return new CsrfTokenRepository() { + @Override + public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { + repository.saveToken(token, request, response); + } + + @Override + public CsrfToken loadToken(HttpServletRequest request) { + return repository.loadToken(request); + } + + @Override + public CsrfToken generateToken(HttpServletRequest request) { + return repository.generateToken(request); + } + }; + } + @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http @@ -59,8 +88,11 @@ public class PortalConfigSecurity { .logoutRequestMatcher(new AntPathRequestMatcher("/actionLogout.do", "GET")) .logoutSuccessUrl("/")) .csrf(csrf -> csrf - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - .ignoringAntMatchers("/h2-console/**") + .csrfTokenRepository(csrfTokenRepository(csrfExclusionService)) + .ignoringRequestMatchers(request -> csrfExclusionService.shouldExclude(request.getServletPath())) + .ignoringAntMatchers("/h2-console/**") + +// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ).headers(headers -> headers.frameOptions().sameOrigin()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) @@ -68,7 +100,6 @@ public class PortalConfigSecurity { .changeSessionId() ); - return http.build(); } } diff --git a/src/main/java/com/eactive/testmaster/loadtest/controller/LoadTestPageController.java b/src/main/java/com/eactive/testmaster/loadtest/controller/LoadTestPageController.java index ccf0f42..10b4b73 100644 --- a/src/main/java/com/eactive/testmaster/loadtest/controller/LoadTestPageController.java +++ b/src/main/java/com/eactive/testmaster/loadtest/controller/LoadTestPageController.java @@ -1,5 +1,6 @@ package com.eactive.testmaster.loadtest.controller; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; @@ -9,6 +10,7 @@ import org.springframework.web.bind.annotation.GetMapping; public class LoadTestPageController { @GetMapping("/mgmt/load_tester.do") + @Secured("ROLE_API_TESTER") public String testView(ModelMap model) { return "page/tester/loadTester"; }