Content Application Developer Manual / Version 2406.0
Table Of Contents
When updating to CoreMedia CMCC from an older version to 2007 or newer, there may be custom templates
(other ones than those that are provided with Blueprint) that cannot instantly be updated to using the
org.springframework.security.web.csrf.CsrfToken
instead of the former _CSRFToken
string. To allow such legacy templates to still work
(for a migration period) with the Spring Security CSRF implementation that is now used by CoreMedia CMCC,
the following code snippets show how to add backward compatibility to the project.
To allow the legacy templates to still render the _CSRFToken
parameters with the string
value, a HandlerInterceptor
has to be added that provides the _CSRFToken
request attribute to the templates:
package com.coremedia.blueprint.component.cae.csrf; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; class CsrfLegacyTokenSetter implements HandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); if (csrfToken != null) { request.setAttribute("_CSRFToken", csrfToken.getToken()); } } }
Example 4.15. Implementing a CsrfLegacyTokenSetter
To verify the token, Spring Security expects the CSRF token to be provided with different parameter
and header names. To allow Spring Security to also verify tokens that are sent by the legacy templates,
a filter has to be added that wraps the HttpServletRequest
with one that gets the token
using the old parameter or header name when it is not provided with the new name:
package com.coremedia.blueprint.component.cae.csrf; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; class CsrfLegacyTokenGetterFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { filterChain.doFilter(new CsrfLegacyTokenRequestWrapper(request), response); } private static class CsrfLegacyTokenRequestWrapper extends HttpServletRequestWrapper { public CsrfLegacyTokenRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); if (value == null && "_csrf".equals(name)) { value = super.getParameter("_CSRFToken"); } return value; } @Override public String getHeader(String name) { String value = super.getHeader(name); if (value == null && "X-CSRF-TOKEN".equals(name)) { value = super.getHeader("X-CSRFToken"); } return value; } } }
Example 4.16. Implementing a CsrfLegacyTokenGetterFilter
Both classes have to be added to the application context for the CAE:
package com.coremedia.blueprint.component.cae.csrf; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.servlet.handler.MappedInterceptor; @Configuration(proxyBeanMethods = false) public class CaeCsrfBackwardCompatibilityConfiguration { private static final int ORDER_CSRF_LEGACY_TOKEN_FILTER = Ordered.HIGHEST_PRECEDENCE + 347_483_648; // == -1_800_000_000 @Bean public MappedInterceptor csrfLegacyTokenSetter() { // Register the token setter for all paths return new MappedInterceptor(null, new CsrfLegacyTokenSetter()); } @Bean public FilterRegistrationBean<CsrfLegacyTokenGetterFilter> csrfLegacyTokenGetterFRB() { var registrationBean = new FilterRegistrationBean<>( new CsrfLegacyTokenGetterFilter()); // Register the filter before the Spring Security filter chain registrationBean.setOrder(ORDER_CSRF_LEGACY_TOKEN_FILTER); return registrationBean; } }
Example 4.17. Configuring CSRF backward compatibility