Spring MVC Interceptor does not execute for resource handler URLs - spring-mvc

In the following setup, the TimingInterceptor and CORSHeaders interceptor execute on all URL requests, except for /resources/** URLs. How do I make the interceptors work for /resources/** URLs served by the ResourceHttpRequestHandler?
#EnableWebMvc //equivalent to mvc:annotation-driven
#Configuration
#PropertySource("classpath:configuration.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private TimingInterceptor timingInterceptor;
#Inject
private CORSHeaders corsHeaders;
// equivalent to mvc:resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
// equivalent to mvc:interceptors
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timingInterceptor).addPathPatterns("/**");
registry.addInterceptor(corsHeaders).addPathPatterns("/**");
}
}

Update: As of Spring Framework 5.0.1 (and SPR-16034), interceptors are automatically mapped on ResourceHttpRequestHandler by default.
I think the configured interceptors aren't mappped on the resource handler, but on the one handling #RequestMapping requests.
Maybe try this instead?
#EnableWebMvc //equivalent to mvc:annotation-driven
#Configuration
#PropertySource("classpath:configuration.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private TimingInterceptor timingInterceptor;
#Inject
private CORSHeaders corsHeaders;
// equivalent to mvc:resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public MappedInterceptor timingInterceptor() {
return new MappedInterceptor(new String[] { "/**" }, timingInterceptor);
}
#Bean
public MappedInterceptor corsHeaders() {
return new MappedInterceptor(new String[] { "/**" }, corsHeaders);
}
}
This should be better documented with SPR-10655.

I never tried to use Spring interceptors for serving resources. The power of interceptors is to have a hook before controller and between controller and view.
To add pre- or post-processing around resources, you'd better use filters.

Related

Camel Rest and RestController in the same project

I have a camel rest in my project and a servlet is configured for it. At the moment I'm trying to add a regular RestController without camel. Can two types of rest be in the same project?
For example
#Override
public void configure() throws Exception {
rest("/dictionary/get-dictionary")
.get()
.param().name("dictionary").required(true).type(RestParamType.query).endParam()
.param().name("name").required(true).type(RestParamType.query).endParam()
.....
.endRest();
CamelServlet implemented for camel
#RestController
#RequestMapping("/createOrder")
#RequiredArgsConstructor
public class OrderController {
private final OrderService OrderService;
#PostMapping
public void createForm(#RequestBody App app) {
orderService.createFullOrder(app);
}
}
When I request createOrder, I always get 404. How can I make both types of controllers work?
Thank in advice
Solution
#Configuration
public class AppConfig {
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#SuppressWarnings("rawtypes")
#Bean
public ServletRegistrationBean axisServletRegistrationBean() {
#SuppressWarnings("unchecked")
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/createOrder");
registration.addUrlMappings("/createOrder");
return registration;
}
}
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.package.package")
public class WebConfig {
}

How do I register a HandlerInterceptor with constructor dependencies in Spring Boot

My use case is running custom code before a controller method by annotating methods.
HandlerInterceptor seems the way to go but it seems impossible to inject dependencies into it because it needs to be registered before the context is being created.
All examples I've found so far use empty constructors (see spring boot adding http request interceptors) or autowire properties in the configuration which fails because I declare dependent beans in the same configuration (Requested bean is currently in creation: Is there an unresolvable circular reference?).
Is there a better way that does not involve AOP?
Assume that your interceptor has constructor dependencies like that:
public class CustomInterceptor extends HandlerInterceptor {
private final DependentBean bean;
public CustomInterceptor(DependentBean bean) {
this.bean = bean;
}
}
Then you can register your handler like that:
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Bean
public DependentBean dependentBean() {
return new DependentBean();
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean());
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
#Configuration will ensure each Bean method call return the same bean instance
Building on the answer above from Mạnh, if using component scan for dependency injection of the dependency, then that can be Autowired in the WebConfig
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Autowired
DependentBean dependentBean;
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean);
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
Also building on previous answers, and if you use Lombok, you can further simplify.
Have your interceptor implementation been a #Component
Add a private final DependentBean field to it.
Also add a #RequiredArgsConstructor annotation to it, to have Lombok generating a constructor with a single DependentBean parameter.
In your WebConfig, use the same technic to have a private final CustomInterceptor field been injected by Spring IOC.
This way the CustomInterceptor instance will be available & initialized the right way when addInterceptors will be called
Here are the corresponding code samples :
The CustomInterceptor :
#Component
#RequiredArgsConstructor
public class CustomInterceptor implements HandlerInterceptor {
private final DependentBean dependentBean;
#Override
public boolean preHandle( final HttpServletRequest request,
final HttpServletResponse response,
final Object handler ) throws Exception {
// your Interceptor Implementation goes here ...
}
}
The WebConfig :
#Configuration
#RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final CustomInterceptor customInterceptor;
#Override
public void addInterceptors( final InterceptorRegistry registry ) {
registry.addInterceptor( customInterceptor );
}
}

spring security webSecurity.ignoring()

I am using spring security via spring boot.
I have two kinds of rest services.
public/** --> Every one can access and use these services
secure/** --> Only authenticated users can use.
#Slf4j
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(requestHeaderAuthenticationFilter(authenticationManager()),
BasicAuthenticationFilter.class)
.authorizeRequests().antMatchers("/secure/**").fullyAuthenticated();
}
#Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}
When i want to access a resource under public i got exception.
exception: "org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException"
message: "MY_HEADER header not found in request."
Why does my filter activated under public resource while it is configured as ignored resource?
Thanks is advance
This is an issue in WebSecurity.ignoring() as discussed in Spring Security Github when using Beans as Filters.
You can work around this by removing the #Bean annotation in your Filter declaration.
// #Bean - Remove or Comment this
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}

Reconfigure Spring Data Rest to Index at Page 1

I thought I had this figured out but the setting does not seem to change the index. setOneIndexedParameters(true)
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setOneIndexedParameters(true);
argumentResolvers.add(resolver);
super.addArgumentResolvers(argumentResolvers);
}
.... other config stuff
Expected result is that instead of the base URL for spring data rest being http://localhost:8080/api/text?page=0&size=20 it would change to http://localhost:8080/api/text?page=1&size=20 as the initial page.
Did I do this correctly or is this a bug?
The answer was here Spring Data Rest - Configure pagination
I moved the configuration to extending RepositoryRestMvcConfiguration
#Configuration
class CustomRestMvcConfiguration extends RepositoryRestMvcConfiguration {
#Override
#Bean
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
HateoasPageableHandlerMethodArgumentResolver resolver = super.pageableResolver();
resolver.setOneIndexedParameters(true);
return resolver;
}
}

How to set context-param in spring-boot

In the classic web.xml type configuration you could configure context parameters like so
web.xml
...
<context-param>
<param-name>p-name</param-name>
<param-value>-value</param-value>
</context-param>
...
How is this achieved in spring-boot. I have a filter that requires parameters.
I'm using #EnableAutoConfiguration and have included <artifactId>spring-boot-starter-jetty</artifactId> in my pom.
You can set parameters using the server.servlet.context-parameters application property. For example:
server.servlet.context-parameters.p-name=p-value
In Spring Boot 1.x, which is no longer supported, this property was named server.context-parameters:
servlet.context-parameters=p-name=p-value
Alternatively, you can configure parameters programmatically by declaring a ServletContextInitializer bean:
#Bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "-value");
}
};
}
You can actually achieve this using Java config. If you have filter that requires some parameters, just put them in your application.yml (or .properties), inject them using #Value in your config class and register them in FilterRegistrationBean.
For example:
#Value("${myFilterParam}")
private String myFilterParam;
#Bean(name="myFilter")
public FilterRegistrationBean myFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
filterRegistrationBean.setInitParameters(Collections.singletonMap("p-name", "p-value"));
return filterRegistrationBean;
}
Also JavaDoc for FilterRegistrationBean:
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/embedded/FilterRegistrationBean.html
Update
You can register parameters for servlet context in SpringBootServletInitializer#onStartup() method. Your Application class can extend the SpringBootServletInitializer and you can override the onStartup method and set the parameters there. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "p-value");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Other alternative is to define ServletContextInitializer bean as suggested by Andy Wilkinson.
Since Spring Boot 2.0.0 they updated the way to add context param:
server.servlet.context-parameters.yourProperty.
You can see more updates on this link
Also you can define InitParameterConfiguringServletContextInitializer in your configuration. Example:
#Bean
public InitParameterConfiguringServletContextInitializer initParamsInitializer() {
Map<String, String> contextParams = new HashMap<>();
contextParams.put("p-name", "-value");
return new InitParameterConfiguringServletContextInitializer(contextParams);
}

Resources