3

Adding a response header to a controller is easyPeasy enough like:

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test(HttpServletResponse response)
{
 response.setHeader("specialheader", "Special Header");
 return "ok";
}

But that's not the case when the user tries to reach static content on the server (like a .css file), where I obviously don't want to create end points for each files.

So I tried to use a new class (@ControllerAdvice) which implements ResponseBodyAdvice, and overwriting the 'beforeBodyWrite' function I got the expected result for each queries which have end points.

The idea came from this link.

But when I try to reach a simple .css file on the server, the ControllerAdvice is not called, therefore the file opens in the browser without the expected headers set.

How can I add a general response header to every response going out of the server, including static files' responses?

iehrlich
  • 3,572
  • 4
  • 34
  • 43
Macskasztorik
  • 649
  • 2
  • 7
  • 17

2 Answers2

1

You have to apply a filter. This will apply to every HTTP Request's response.

You can find the exact answer here: How to add a filter class in Spring Boot?

Community
  • 1
  • 1
Mr.Fireman
  • 514
  • 1
  • 5
  • 16
1

Note: There may be better options for Spring 2.x.

In my case (1.5.x), FilterRegistrationBean approach didn't add the additional headers to error pages; hence the alternative.

If you want such headers applied across all static content including error pages and regular static content (e.g. as part of a security audit/vulnerability assessment), you may have to follow a composite approach.

First, a simple utility class/method to keep the header addition uniform:

import javax.servlet.http.HttpServletResponse;

public class SecurityUtils {
    public static void addHtmlSecurityHeaders(HttpServletResponse response) {
        // some example content-security related headers
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-Frame-Options", "deny");
    }
}

This adds above headers to standard static/ resources (courtesy of this SO answer):

    @Bean
    public ResourceHttpRequestHandler staticHandler() {
        ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler() {
            @Override
            public void setHeaders(HttpServletResponse response, Resource resource, MediaType mediaType) throws IOException {
                SecurityUtils.addHtmlSecurityHeaders(response);
                super.setHeaders(response, resource, mediaType);
            }
        };
        handler.setLocations(Collections.singletonList(new ClassPathResource("static/")));
        return handler;
    }

    @Bean
    public SimpleUrlHandlerMapping staticMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(Integer.MAX_VALUE - 2);
        Properties urlProperties = new Properties();
        urlProperties.put("/**", "staticHandler");
        mapping.setMappings(urlProperties);
        return mapping;
    }

This adds above headers to error pages:

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {

            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new HandlerInterceptorAdapter() {
                    @Override
                    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
                        SecurityUtils.addHtmlSecurityHeaders(response);
                    }
                });
            }

            // .. more overrides
        };
    }

Plus, some observations regarding other approaches:

  • If you apply the headers at Spring Security level, they would not appear on static content such as the welcome page (base/index HTML) and error pages. (Unless you somehow configure/modify Spring Security to include static paths as well - which would of course be unnecessary, and probably impact performance)
  • If you use only a ResourceHttpRequestHandler to inject the headers, they will not appear Security-intercepted controller responses or (more importantly) on error pages.
  • In HandlerInterceptorAdapter I tried configuring the headers on preHandle() and afterCompletion(); however they did not get applied for some scenarios - esp. for error pages.
Janaka Bandara
  • 1,024
  • 1
  • 12
  • 27