|
5 | 5 |
|
6 | 6 | import eu.webeid.example.security.AuthTokenDTOAuthenticationProvider; |
7 | 7 | import eu.webeid.example.security.WebEidAjaxLoginProcessingFilter; |
| 8 | +import jakarta.servlet.http.HttpServletRequest; |
| 9 | +import jakarta.servlet.http.HttpServletResponse; |
| 10 | +import org.apache.commons.lang3.StringUtils; |
| 11 | +import org.slf4j.Logger; |
| 12 | +import org.slf4j.LoggerFactory; |
| 13 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 14 | +import org.springframework.boot.autoconfigure.web.ServerProperties; |
8 | 15 | import org.springframework.context.annotation.Bean; |
9 | 16 | import org.springframework.context.annotation.Configuration; |
| 17 | +import org.springframework.http.HttpStatus; |
| 18 | +import org.springframework.security.config.Customizer; |
10 | 19 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; |
11 | 20 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; |
12 | 21 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
13 | 22 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
14 | 23 | import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; |
15 | 24 | import org.springframework.security.web.SecurityFilterChain; |
| 25 | +import org.springframework.security.web.authentication.HttpStatusEntryPoint; |
16 | 26 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
17 | 27 | import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; |
| 28 | +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; |
| 29 | +import org.springframework.security.web.csrf.CsrfToken; |
| 30 | +import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; |
| 31 | +import org.springframework.security.web.csrf.CsrfTokenRequestHandler; |
| 32 | +import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler; |
| 33 | +import org.springframework.web.servlet.config.annotation.CorsRegistry; |
18 | 34 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; |
19 | 35 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
20 | 36 |
|
| 37 | +import java.util.function.Supplier; |
| 38 | + |
21 | 39 | @Configuration |
22 | 40 | @EnableWebSecurity |
23 | 41 | @EnableMethodSecurity(securedEnabled = true) |
24 | 42 | public class ApplicationConfiguration implements WebMvcConfigurer { |
25 | 43 |
|
| 44 | + private static final String CSRF_COOKIE_NAME = "WEBEID-XSRF-TOKEN"; |
| 45 | + |
26 | 46 | @Bean |
27 | | - public SecurityFilterChain filterChain(HttpSecurity http, AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider, AuthenticationConfiguration authConfig) throws Exception { |
| 47 | + public SecurityFilterChain filterChain( |
| 48 | + HttpSecurity http, |
| 49 | + AuthTokenDTOAuthenticationProvider authTokenDTOAuthenticationProvider, |
| 50 | + AuthenticationConfiguration authConfig, |
| 51 | + YAMLConfig validationConfig, |
| 52 | + CsrfConfigurationProperties csrfConfig, |
| 53 | + ServerProperties serverConfig |
| 54 | + ) throws Exception { |
| 55 | + if (csrfConfig.isUseSpaConfiguration()) { |
| 56 | + http |
| 57 | + .csrf(csrf -> csrf |
| 58 | + .csrfTokenRepository(createDefaultCsrfTokenRepository(csrfConfig.getCookieDomain())) |
| 59 | + .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())) |
| 60 | + .cors(Customizer.withDefaults()); |
| 61 | + } |
28 | 62 | return http |
29 | 63 | .authenticationProvider(authTokenDTOAuthenticationProvider) |
30 | 64 | .addFilterBefore(new WebEidAjaxLoginProcessingFilter("/auth/login", authConfig.getAuthenticationManager()), |
31 | 65 | UsernamePasswordAuthenticationFilter.class) |
32 | | - .logout(logout -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) |
| 66 | + .logout(logout -> logout |
| 67 | + .deleteCookies(StringUtils.defaultIfBlank(serverConfig.getServlet().getSession().getCookie().getName(), "JSESSIONID")) |
| 68 | + .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())) |
33 | 69 | .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) |
| 70 | + .exceptionHandling(ex -> ex.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) |
34 | 71 | .build(); |
35 | 72 | } |
36 | 73 |
|
| 74 | + @ConditionalOnProperty(name = "web-eid-auth-token.csrf.use-spa-configuration", havingValue = "true") |
| 75 | + @Bean |
| 76 | + public WebMvcConfigurer corsConfigurer(YAMLConfig config) { |
| 77 | + return new WebMvcConfigurer() { |
| 78 | + @Override |
| 79 | + public void addCorsMappings(CorsRegistry registry) { |
| 80 | + registry.addMapping("/**") |
| 81 | + .allowedOrigins(config.getCorsAllowedOrigin()) |
| 82 | + .allowCredentials(true); |
| 83 | + } |
| 84 | + }; |
| 85 | + } |
| 86 | + |
37 | 87 | @Override |
38 | 88 | public void addViewControllers(ViewControllerRegistry registry) { |
39 | 89 | registry.addViewController("/").setViewName("index"); |
40 | 90 | registry.addViewController("/welcome").setViewName("welcome"); |
41 | 91 | } |
42 | 92 |
|
| 93 | + private CookieCsrfTokenRepository createDefaultCsrfTokenRepository(String cookieDomain) { |
| 94 | + CookieCsrfTokenRepository cookieCsrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse(); |
| 95 | + cookieCsrfTokenRepository.setCookieName(CSRF_COOKIE_NAME); |
| 96 | + cookieCsrfTokenRepository.setCookieCustomizer(cookie -> cookie |
| 97 | + .domain(cookieDomain) |
| 98 | + .sameSite("Lax") |
| 99 | + ); |
| 100 | + return cookieCsrfTokenRepository; |
| 101 | + } |
| 102 | + |
| 103 | + private static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler { |
| 104 | + private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler(); |
| 105 | + private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler(); |
| 106 | + |
| 107 | + @Override |
| 108 | + public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) { |
| 109 | + xor.handle(request, response, csrfToken); |
| 110 | + csrfToken.get(); |
| 111 | + } |
| 112 | + |
| 113 | + @Override |
| 114 | + public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { |
| 115 | + String headerValue = request.getHeader(csrfToken.getHeaderName()); |
| 116 | + return (StringUtils.isNotBlank(headerValue) ? plain : xor).resolveCsrfTokenValue(request, csrfToken); |
| 117 | + } |
| 118 | + } |
| 119 | + |
43 | 120 | } |
0 commit comments