How do I make jcaptcha work with Spring Session? - servlets

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

Related

Filter specific HTTP verbs in 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>

Get servlet init params in servlet context listener

<listener>
<listener-class>config</listener-class>
</listener>
<servlet>
<servlet-name>ProcessReg</servlet-name>
<servlet-class>ProcessReg</servlet-class>
<init-param>
<param-name>text</param-name>
<param-value>HelloWorld1</param-value>
</init-param>
public class config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
String text1 = servletContext.getInitParameter("text");
In method contextInitialized(ServletContextEvent event) , If There would be two servlets , for example , Let's say name of the second servlet be Servlet2 and it let's say has also has init - param called text with value HelloWorld2 .
How does listener know to take ProcessReg servlet ?
How to get param from Servlet2 ??
You need to distinguish between servlet initialization parameters and context initialization parameters.
Context initialization parameters are:
context-wide;
declared in <context-param> elements directly under the <web-app> root;
typically accessed using the ServletContext.getInitParameter() method, e.g. from inside a listener's contextInitialized() method.
Servlet initialization parameters are:
servlet-specific;
declared in <init-param> elements inside a <servlet> element;
typically accessed using the ServletConfig.getInitParameter() method, e.g. from inside the servlet's init() method.

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>

JBoss AS7: ServletContextListener#contextInitialized called after Servlet instantiation

I am trying to deploy my webservice (originally running on JBoss AS5) on the new JBoss AS7 (Everything) Final.
The problem is that i used a ServletContextListener to initialize my environment (f.e. to initialize the resource management).
But when I try to deploy the war file on JBoss AS 7 the configured Servlets are instantiated before the #contextInitialized-method of the listener is called (wich cases an Exception because a mandatory resource can not be found).
My listener:
#WebListener
public class StandardStartup implements ServletContextListener {
#Override
public void contextDestroyed(final ServletContextEvent arg0) {
//destroy environment
}
#Override
public void contextInitialized(final ServletContextEvent arg0) {
//initialize environment
}
}
My web.xml:
<?xml version="1.0"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>any.displayname</display-name>
<listener>
<display-name>Startup</display-name>
<listener-class>any.StandardStartup</listener-class>
</listener>
<servlet>
<servlet-name>AnyServlet</servlet-name>
<servlet-class>any.AnyServlet</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AnyServlet</servlet-name>
<url-pattern>/AnyServlet</url-pattern>
</servlet-mapping>
</web-app>
Can someone explain me why the listener is not called before the instantiation of the servlets (maybe with a possible workaround) or knows what I am doing wrong?
PS.: I tried to implement the listener as a Servlet and set the load-on-startup to 0, but JBoss AS7 says I have declare a servlet-mapping which I do not want in that case.
Thanks for your help!

Resources