Spring 3.2 MVC #RequestParam unexpected decoding - spring-mvc

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.

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

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.

CORS filter issue for AuthEntryPoint

I've implemented CORS for my Spring MVC application. The following is in my Web.xml:
<filter>
<filter-name>simpleCORSFilter</filter-name>
<filter-class>com.tcs.filters.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>simpleCORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
After consulting CORS with spring-boot and angularjs not working , I implemented a SimpleCORSFilter.java class
And now I implemented the following in a Spring-security.xml file:
<http
use-expressions="true" auto-config="false" create-session="stateless"
disable-url-rewriting="true" entry-point-ref="entryPoint"
authentication-manager-ref="authenticationManager">
<bean id="entryPoint" class="com.tcs.web.EntryPoint" />
But when it is executing it's not calling the class below for either the "wrong/incorrect"-password case or the correct-password case.
So, CORS configuration is blocking the call to this, but if I remove CORS, it is calling UnauthorizedEntryPoint.
Could you let know how to properly call this?
public class EntryPoint implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException{
// here some custom code about user like values etc
String userid = request.getParameter("userId")
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized: Authentication token was either missing or invalid.");
}
}

Spring Security Allows Unauthorized User Access to Restricted URL from a Forward

Spring Security 3.2.0.RC2
Given:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers("/restricted/**").hasRole("admin")
.anyRequest().authenticated()
// etc
;
}
A user without the admin role trying to access /myapp/restricted/foo.request correctly receives an HTTP 403.
However, given:
#Controller
public class FooController {
#RequestMapping("/bar.request")
public String bar() {
return "forward:/restricted/foo.request";
}
}
If the user accesses /myapp/bar.request, the user is forwarded to the restricted /myapp/restricted/foo.request.
How can this be blocked without explicitly blocking "/bar.request"?
#kungfuters is correct that the first step is ensuring the Filter is intercepting that request in the first place. To do so with a web.xml you would use the following:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher> <!-- Include FORWARD here -->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
To do so with Java Configuration you would use the following:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
return return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD);
}
}
The last piece is that the FilterSecurityInterceptor (the piece that ensures URLs are protected) by default will only intercept the REQUEST and not additional dispatches (i.e. forwards). This is done because it is quite rare to protect the URLs that are forwarded to (typically you would protect the URL that does the forwarding). To enable that you need to use the following with xml configuration you need to use http#once-per-request=true:
<http once-per-request="true">
<!-- ... -->
</http>
Similarly, there is a oncePerRequest property within Java Configuration that can be used. For example:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.filterSecurityInterceptorOncePerRequest(false)
// make sure to grant access to any login page you are forwarding to
.antMatchers("/restricted/login").permitAll()
.antMatchers("/restricted/**").hasRole("admin")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
// etc
;
}
If you are using web.xml to configure your filter, try this:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher> <!-- Include FORWARD here -->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
...or use the Servlet3 Java-based Config equivalent, which is to extend AbstractSecurityWebApplicationInitializer and override the getSecurityDispatcherTypes() method:
public class YourSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
// Return dispatcher types here, in your case you'll want the defaults,
// which are DispatcherType.REQUEST and DispatcherType.ERROR
// ...as well as the one you need for your use case: DispatcherType.FORWARD
}
}
I typed that here, so hopefully there are no errors. Should get you going, though.

Resources