Filter specific HTTP verbs in spring mvc - spring-mvc

I'm getting a lot of errors in my spring MVC app caused by some clients requesting HTTP PROPFIND:
16:59:39,402 ERROR [foo.bar.controllers.ExceptionHandlingController] (default task-12) Uncaught Error: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PROPFIND' not supported
How can I filter this requests so that they don't generate an error in each controller ?
Thanks!

You can use Spring's HandlerInterceptor as below to allow & process the required requests.
RequestMethodInterceptor class:
package com.myproject.RequestMethodInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestMethodInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//Added PROPFIND method, add any other types NOT allowed
if(request.getMethod().equals("PROPFIND") ) {
//Log or Ignore upon your requirement & return false
return false;
} else {
return true;
}
}
}
XML Configuration:
<mvc:interceptors>
<bean class="com.myproject.RequestMethodInterceptor" />
</mvc:interceptors>

You have to configure the spring org.springframework.web.servlet.DispatcherServlet in managing OPTIONS
In the web.xml you should add something like this
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

Related

How do I make jcaptcha work with Spring Session?

We implemented Spring Session backed by Redis and have a cluster of Tomcat servers. When we turned sticky sessions off by not setting the jvmRoute we keep getting "Text verification failed" in the jcaptcha service. I assume this is because the jcaptcha servlet knows nothing about the Spring Dispatcher servlet, which has all of the Spring Session filters, and thus cannot read the session variable. How can we make jcaptcha work with Spring Session?
Here is our setup:
Web.xml
<servlet>
<servlet-name>my-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>my-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jcaptcha</servlet-name>
<servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jcaptcha</servlet-name>
<url-pattern>/jcaptcha/jcaptcha.jpg</url-pattern>
</servlet-mapping>
CustomHttpSessionAppInitializer.java
public class CustomHttpSessionAppInitializer extends AbstractHttpSessionApplicationInitializer {}
RedisSessionConfig.java
#Configuration
#EnableRedisHttpSession
public class RedisSessionConfig {
#Value("${spring.redis.host}")
private String redisServerName;
#Value("${spring.redis.port}")
private Integer redisServerPort;
#Value("${spring.redis.database}")
private Integer redisServerDatabase;
#Value("${spring.redis.password}")
private String redisServerPassword;
#Value("${spring.server.affinity}")
private Boolean isServerAffinity = Boolean.FALSE;
#Autowired
private SessionIdentifierService sessionIdentifierService;
#Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisServerName, redisServerPort);
config.setDatabase(redisServerDatabase);
config.setPassword(RedisPassword.of(redisServerPassword));
return new JedisConnectionFactory(config);
}
/*
* We need to register every HttpSessionListener as a bean to translate SessionDestroyedEvent and SessionCreatedEvent into
* HttpSessionEvent. Otherwise we will got a lot of warning messages about being Unable to publish Events for the session.
* See Spring Session Docs at:
* {#link} https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-httpsessionlistener
*/
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setUseBase64Encoding(false);
if (isServerAffinity) {
serializer.setJvmRoute(sessionIdentifierService.getJvmRoute());
}
return serializer;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
There shouldn't be a problem integrating jcaptcha with Spring Session. As long as there is a way of loading the session from Redis (via a SESSION cookie in this case) and the session exists, calling request.getSession() or request.getSession(false) will return the Redis-backed session.
This works in any filter and servlet that is called AFTER the springSessionRepositoryFilter. If you look at the source code of SessionRepositoryFilter, you will see that the HttpServletRequest is swapped with a SessionRepositoryRequestWrapper.
So your SimpleImageCaptchaServlet and whichever servlet you use to validate the user response will obtain a SessionRepositoryRequestWrapper that will seemlessly give you access to the Redis-backed session.
The problem then might be your configuration; the springSessionRepositoryFilter might not be registered with the container, especially since you’re using both web.xml and a Servlet 3.0+ WebApplicationInitializer. If your app works properly, then your web.xml is most likely working fine. Are you using a WebApplicationInitializer to load your web.xml? If not, then it might be that your Java Config is not loading. Make sure your web.xml loads your configuration somehow, perhaps by enabling component scanning (<context:component-scan/>) in the contextLoaderListener xml config file to load your Java Config along with:
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
to load the configuration that will create the filter, which you must then add to your web.xml:
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
Check out the Spring Session reference on XML config

Spring 3.2 MVC #RequestParam unexpected decoding

Consider following query string
siteId=360798&listingId=695332&stockFilename=6065/1mb%20&%20(Custom).jpg&uploadToken=2640861a8f06799416bae9d0a58839dd&username=Chuck%20Norris
I have the following on my MVC controller
#RequestParam("stockFilename") String stockFilename
I expect to get as value in my param to have "6065/1mb & (Custom).jpg", yet the result is "6065/1mb". My first guess was that this was an encoding issue so I went to web.xml and looked at my encoding filter but this seems to be in order
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
Next up I went to look at my server setting
Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"
I've added the URIEncoding attribute, since I found an article explaining this as a possible culprit. However it didn't fix my problem at all. I'm actually out of ideas now I'm still quite sure it has to be an encoding issue, and spring is decoding it wrongly. I'll continue searching but any help would be appreciated.
Add this method and bean in your servlet-context :
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
public final class DoNotTruncateMyUrls implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping)bean).setUseSuffixPatternMatch(false);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Bean :
<beans:bean class="com.packagename.DoNotTruncateMyUrls" />
Let me know if this works, or I have another solutions.

servlet or ejb singleton to initialize web app

I need to initialize state of web application.
I can use initialization servlet with load-on-startup = 0.
Or I can use singleton ejb service.
What is better to use and why?
i think load-on-startup is better than ejb.
i have used load on start up in my web application.
<servlet>
<servlet-class>com.agileinfotech.bsviewer.servlet.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>InitServlet</servlet-name>
<url-pattern>/InitServlet</url-pattern>
</servlet-mapping>
There is a special "thing" for initialization of web app - ServletContextListener.
it is used like this:
package example;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ServletContextExample implements ServletContextListener{
ServletContext context;
public void contextInitialized(ServletContextEvent contextEvent) {
... some init work
}
public void contextDestroyed(ServletContextEvent contextEvent) {
... some destroy work
}
}
And in web.xml
<listener>
<listener-class>
example.ServletContextExample
</listener-class>
</listener>

Issue with making a controller method asynchronous with Spring 3.2.RC2

I am trying to test the latest asynchronous feature of Spring MVC controllers but I have not been able to get it to work.
Here is the code for my async method:
#RequestMapping(value = "/hello")
public Callable<String> async(final Model model) {
System.out.println("entered async controller method");
return new Callable<String>() {
public String call() throws Exception {
Thread.sleep(2000L);
model.addAttribute("message", "asyncRequest dealt with");
System.out.println("about to return from call()");
return "hello";
}
};
}
Here is the relevant portion from web.xml:
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
However "about to return from call()" is never printed in the console and I never get to see such logs as this: 08:25:17 [MvcAsync1] WebAsyncManager - ...
in the console...
FYI, I use Spring 3.2.RC2
To further configure the async-support implement a AsyncTaskExecutor
By default Spring MVC uses a SimpleAsyncTaskExecutor to execute Callable instances returned by controller methods. For production you must replace it with an AsyncTaskExecutor implementation configured appropriately for your environment.
Create the AsyncTaskExecutor
<beans:bean id="asyncTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<beans:property name="corePoolSize" value="5" />
<beans:property name="maxPoolSize" value="10" />
<beans:property name="queueCapacity" value="25" />
</beans:bean>
Then assign the task-executor
<mvc:annotation-driven >
<mvc:async-support default-timeout="30000" task-executor="asyncTaskExecutor" />
</mvc:annotation-driven>
http://blog.springsource.org/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/
I finally found why my sample was not working: one has to add the following to the web-mvc configuration file:
<mvc:annotation-driven>
<mvc:async-support default-timeout="3000"/>
</mvc:annotation-driven>
Hi doing a similar application with the following code :
public String doSlowWork() {
System.out.println("Start slow work");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("finish slow work");
return "index"; // return view's name
}
returning index but not giving the log info of the method

web.xml error-page location redirect is not going through my filter definitions

In my web.xml I've done the following error-page mappings, but when they are invoked those invoked requests are not passing through the filter definitions specified in web.xml file.
<error-page>
<error-code>403</error-code>
<location>/error.vm?id=403</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/error.vm?id=400</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.vm?id=404</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/servlet-exception.vm</location>
</error-page>
My application is using spring-mvc and I want to handle the handler not found condition from spring mvc. My application is an multi tenant application where some filters are responsible for setting some information related to the schema.
The requests are reaching in my error.vm controller but since they are passing through the filter I'm not able to determine the theme and SecurityContext etc.
How to solve this problem?
Thank you.
Instead of using web.xml's error pages you could use a servlet filter. The servlet filter could be used to catch all exceptions, or just a particular exception such as org.springframework.web.portlet.NoHandlerFoundException. (Is that what you mean by "handler not found" exception?)
The filter would look something like this:
package com.example;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.web.portlet.NoHandlerFoundException;
public class ErrorHandlingFilter implements Filter {
public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
// Or you could catch Exception, Error, Throwable...
// You probably want to add exception logging code here.
// Putting the exception into request scope so it can be used by the error handling page
request.setAttribute("exception", e);
// You probably want to add exception logging code here.
request.getRequestDispatcher("/WEB-INF/view/servlet-exception.vm").forward(request, response);
}
}
}
Then, set this up in web.xml with the help of Spring's DelegatingFilterProxy:
<filter>
<filter-name>errorHandlingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And then finally, turn the filter into a spring bean inside your spring context xml:
<bean id="errorHandlingFilter" class="com.example.ErrorHandlingFilter" />
You might have to experiment with the order of the filter in the filter chain so that failed requests still go through the other filters you mentioned. If you're having trouble with that, a variation would be to do an HTTP redirect instead of a forward, like this:
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
request.getSession().setAttribute("exception", e);
response.sendRedirect("/servlet-exception.vm");
}
That would force the browser to request your error handling page as a new http request, which might make it easier to ensure it goes through all of the right filters first. If you need the original exception object, then you could put it in the session instead of the request.
maybe
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Resources