HttpServletResponse contained in servlet filter does not perform redirect - spring

I am using Spring 4.0.6 in a servlet application. I have an abstract base controller with some general methods for all my controllers to use.
One of these methods is a redirect. I want to have a method with signature
redirect(String path)
To send a redirect, I am using
response.sendRedirect(response.encodeRedirectURL(path));
As I would like to keep method signatures short and clean, I need to get access to the response object inside the superclass method.
In order to do this, I've followed a suggestion found online, and defined a servlet filter with a ThreadLocal HttpServletResponse.
public class ResponseFilter extends OncePerRequestFilter {
private static final ThreadLocal<HttpServletResponse> responses = new ThreadLocal<HttpServletResponse>();
public static HttpServletResponse getResponse() {
return responses.get();
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
responses.set(response);
} finally {
try {
filterChain.doFilter(request, response);
} finally {
responses.remove();
}
}
}
}
As I am using Spring security with a Java configuration, I'm adding this filter in my WebSecurityConfigurerAdapter subclass:
.addFilterAfter(rf, SwitchUserFilter.class)
Note that I have also tried adding the filter as first in the filterchain, and that I have tried using an Interceptor instead. All with the same results.
I have compared hashcodes on the response objects, and near as I can tell, the hashcodes match, but the redirect seems to be ignored. I have also looked at object ids on breakpoints in Eclipse, and there again, I have a match. The symptom is that the spring DispatcherServlet enters processDispatchResult and seems to think it needs to resolve a view. That view does not exist, as I expect to do a redirect:
javax.servlet.ServletException: File "/WEB-INF/views/application/redirecttest.jsp" not found
I have noticed that, if I add the response object back in my requestmapping controller method signature, the superclass redirect seems to work (even though I do not use the controller method response object at all).
Unfortunately, this behavior is reproducible both on a Mac and on Linux. I use Tomcat 7 as container.

Your filter should work just fine, but the problem you're facing is another. If you are using views (as you appear to do in the example) you need to return a redirect view from your controller in order to force a redirect; just instructing the response object to redirect won't work because Spring MVC infrastructure will try to do its thing (i.e. view resolution) before the Response is returned to the Servlet container.
For instance, if you use the convention to return the view name as a String from your controller method, you need to do the following in your controller:
#RequestMapping("/redirectTest")
public String redirectTest() {
return "redirect:http://www.example.com";
}

Related

Spring boot - running check on every request and storing objects for lifecycle

I have a scenario where I need to run a few db checks at the start of every web request, and in the case of success I need to store objects for use later in the request by the controller, or in the case of failure I need to render an error page.
A very similar real world example would be a SaaS app checking and loading the account based on a vanity url, then storing the account for use by controllers to avoid multiple db requests.
What are the best ways to achieve this in a Spring boot app? I have experimented with Filters but I think an Interceptor might be better at the task, that covers running the check but what about storing the objects for later use? Is there a request lifecycle context of some kind that I can store against?
Spring supports request scope for beans. You can use them for storing data used during request execution.
In my experience, best way I've done similar stuff is through HandlerMethodArgumentResolver.
Basically imagine you have a custom type, let's call it UserContext where you store the information that's needed for the request. And you have a UserContextService let's say that has a method getUserContext(HttServletRequest), that is used to retrieve the context based on the request, from which you can call your database based on whatever request parameter/header/path-variable, etc. You can refine that as you need. But based on this simple assumptions, you can have a controller that looks like this:
#RequestMapping("/some/url")
public SomeResponse someMethod(UserContext userContext, ...) {
//do something here with UserContext
}
The way that Spring will inject this UserContext into your controller would be with a custom HandlerMethodArgumentResolver like this:
#Component
public class UserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
UserContextService
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(UserContext.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest req = (HttpServletRequest)webRequest.getNativeRequest();
UserContext userContext = userContextService.getUserContext(req);
if (userContext != null) {
return userContext;
} else {
return WebArgumentResolver.UNRESOLVED;
//Or throw exception
}
}
}
That you'll register by overriding the WebMvcConfigurer.addArgumentResolvers method in your WebMvcConfigurer bean/config-class.
This mechanism is the same used by #PathVariable, #RequestParam, etc...

Spring Boot Groovy Templates Not Adding _csrf to model

I have a very simple Spring Boot Application which consists of a main Application.java (with the default main method), a MainController (which has one requestMapping to /login), and a SecurityConfig (with mainly default values).
My problem is with the _csrf support in Groovy Templates. Everything works fine with FreeMarker, but when I switch to GroovyTemplates the _csrf param does not get put into the model.
Is there a bug in the Groovy Templates, something manually I have to do to grab the token, or is there some configuration step I'm missing (although I don't know why it would work for FreeMarker) ?
UPDATE:
I printed this.properties (HashMap) on the login.tpl (Groovy Template) :
{class=class login, out=java.io.BufferedWriter#5e2aead3, model={error=Optional.empty, org.springframework.validation.BindingResult.error=org.springframework.validation.BeanPropertyBindingResult: 0 errors, spring=org.springframework.web.servlet.support.RequestContext#1d99fb33, springMacroRequestContext=org.springframework.web.servlet.support.RequestContext#7fcc5c78}}
The model key in the properties map includes the parameters
I added error in the Controller action using the:
#RequestMapping(value="/login", method = RequestMethod.GET)
public ModelAndView login(#RequestParam Optional<String> error) {
return new ModelAndView("views/login", "error", error);
}
When using the GroovyMarkupView and the GroovyMarkupViewResolver the properties of the view only contain properties available in the model (and some added for Groovy).
To include the request attributes set the exposeRequestAttributes property of the GroovyMarkupViewResolver to true. Ideally this is done by setting the following property in the application.properties.
spring.groovy.template.exposeRequestAttributes=true
However due to this issue that currently isn't possible.
To work around it create a BeanPostProcessor which check if the incoming bean is a GroovyMarkupViewResolver (or AbstractTemplateViewResolver if you want a more general approach). If so set the exposeRequestAttributes to true.
public class TemplateViewResolverPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instance GroovyMarkupViewResolver) {
((GroovyMarkupViewResolver) bean).setExposeRequestAttributes(true);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
After doing that the CsfrToken is available with the key _csfr, be aware that this is the actual CsfrToken.
Another solution is to create a HandlerInterceptor implement the postHandle method and add the _csfr property to the model. That way you can simply add the value of the token instead of the actual token itself. This will work with any view technology used.
public class CsrfAddingInterceptor extends HandlerInterceptorAdapter {
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mav) throws Exception {
CsrfToken token = (CsrfToken) req.getAttribute(CsrfToken.class.getName())
if (token != null) {
mav.addAttribute(token.getParameterName(), token.getToken());
}
}
}
Then add it as an interceptor and you will have the value available.
So the hacked up way to handle this is to manually add the _csrf attribute to your model in your Controller. For example:
model.addAttribute( "_csrf", request.getAttribute("_csrf") );
I wouldn't recommend that if you have lots of views on your server. I'd suggest you follow one of the options from #M-Deinnum. But, for quick testing this works.

Spring MVC image or character generic controller

I would like to implement a generic controller with one or two methods that react to any GET request. I am trying to simplify this to the point where I can return byte (image etc.) or character based (XML, CSS) without having to map each content type and put a RequestMapping in for each.
The app must be abel to handle any request with any content type.
My dispatcher is currently set to handle all requests via /.
The couple of attempts I have made so far throw ambigious handler errors, or the mapping doesn;t work to the point where text is sent back as byte[] or the other way around.
Has anyone made anything like this work ?
Regards,
Andy
You can have a controller like so
#Controller
public class YourController {
#RequestMapping("/*")
public String doLogic(HttpServletRequest request, HttpServletResponse response) throws Exception {
OutputStream out = response.getOutputStream();
out.write(/* some bytes, eg. from an image*/); // write the response yourself
return null; // this is telling spring that this method handled the response itself
}
}
The controller is mapped to every url and every http method. Spring has a set of accepted return types for its handler methods. With String, if you return null, Spring assumes you've handled the response yourself.
As #NilsH commented, you might be better off using a simple servlet for this.

How to configure which controllers Spring #ControllerAdvice will be applied to?

I have two types of controllers in my spring application.
View controllers that forward to views to generate HTML
API controllers that return JSON directly from the controllers
Both the API and View controllers are part of the same spring dispatcher servlet. Spring 3.2 introduced the #ControllerAdvice annotation to allow for a global location to handle exception.
The documentation implies that #ControllerAdvice will be applied to every controller associated with a Dispatcher Servlet.
Is there a way to configure which controllers #ControllerAdvice will apply to?
For example in my scenario I want a #ControllerAdvice for my View Controllers and separate #ControllerAdvice for my API controllers.
For people that will still find this question:
As from Spring 4 ControlerAdvice's can be limited to Controler's with the specified annotations. Take a look at:
http://blog.codeleak.pl/2013/11/controlleradvice-improvements-in-spring.html
(second half of this article) for more details.
UPDATE
I am using spring 4. You can do one of 2 below options.
(1) You can add the packages you want. (Inside those packages you have controllers that you want to follow #ControllerAdvice).
Ex:
#ControllerAdvice(basePackages={"my.pkg.a", "my.pkg.b"})
(2) You can directly add the controller classes you want.
Ex:
#ControllerAdvice(basePackageClasses={MyControllerA.class, MyControllerB.class})
I do not think this is possible now. If you can make the API and View controllers throw different Exception types, then you could define two different #ExceptionHandlers and achieve what you want.
// For handling API Exceptions
#ExceptionHandler(APIException.class) // Single API Exception
#ExceptionHandler({APIException.class, ..., ,,,}) // Multiple API Exceptions
// For handling View Exceptions
#ExceptionHandler(ViewException.class) // Single View Exception
#ExceptionHandler({ViewException.class, ..., ...}) // Multiple View Exceptions
You could use aop to translate the Exceptions coming out of APIs to a standard APIException. See this thread on spring forums.
Hope it helps.
Your exceptions should not dictate the content-type of your response. Instead check the request's Accept header for what the browser expects.
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {
...
String header = request.getHeader("Accept");
if(supportsJsonResponse(header)) {
//return response as JSON
response.setContentType(JSON_MEDIA_TYPE.toString());
return Json.stringify(responseMap);
} else {
//return as HTML
response.setContentType("text/html");
}
#ExceptionHandler(value=Exception.class)
public ModelAndView error(Exception ex) {
return new ModelAndView("redirect:/error/m");
}
...//ErrorController
#RequestMapping(value = "/m", produces="text/html")
public ModelAndView error()...
#RequestMapping(value = "/m", produces="application/json")
#ResponseBody
public Map errorJson()...

Spring AnnotationMethodHandlerAdapter and annotation-reading interceptors

I have a basic Spring MVC controller that looks like this:
#Controller
public void MyController {
#RequestMapping("/secret")
public String show() {
return "secret.jsp";
}
}
I am going to have several similar URLs that can only be reached by signed-in users. Since this is a cross-cutting concern, I'd like to use AOP, and I'd like to make this work via annotations. In other words, I'd like to throw a #RequiresLogin annotation on every controller method that needs to be secret.
AnnotationMethodHandlerAdapter supports the concept of interceptors, which seems on the surface like the right way to go for this. However, I want to know which method is going to be invoked so that I can check it for my #RequiresLogin annotation. I see that there's an "Object handler" parameter that's passed in, but I'm not sure how to turn that into a Class and Method that will be invoked.
Ideas?
There are no good ways to get a method signature in the interceptor.
Try to apply a regular AOP advise to your controller, Spring MVC plays well with it as long as target class proxying is used.
As axtavt writes correctly, Spring-AOP works well with controllers if using proxy-target-class. But there is also the possibility of using JDK proxies if you follow some (tedious) conventions:
Working with interface-based #Controller classes
A common pitfall when working with
annotated controller classes happens
when applying functionality that
requires creating a proxy proxy for
the controller object (e.g.
#Transactional methods). Usually you
will introduce an interface for the
controller in order to use JDK dynamic
proxies. To make this work you must
move the #RequestMapping annotations
to the interface as the mapping
mechanism can only "see" the interface
exposed by the proxy. As an
alternative, you may choose to
activate proxy-target-class="true" in
the configuration for the
functionality applied to the
controller (in our transaction
scenario in <tx:annotation-driven />).
Doing so indicates that CGLIB-based
subclass proxies should be used
instead of interface-based JDK
proxies. For more information on
various proxying mechanisms see
Section 7.6, “Proxying mechanisms”.
Source: 15.3.2 Mapping requests with #RequestMapping
While using spring security would be the optimal approach here, you can implement similar functionality using Spring Aspects. Here is an example of using an Aspect to check for a method containing a particular Annotation.
#Aspect
public class MyAspect {
#Around("execution(* com.test.controllers..*.**(..)) && " +
"within(#org.springframework.sterotype.Controller *)")
public Object execute(ProceedingJoinPoint joinPoint) {
Object target = joinPoint.getTarget();
if (target != null) {
Signature tSig = joinPoint.getSignature();
if (tSig instanceof MethodSignature) {
MethodSignature mSig = (MethodSignature) tSig;
Method method = mSig.getMethod();
if (method != null && method.isAnnotationPresent(MyAnnotation.class)) {
// do something
// parameters are available from joinPoint.getArgs();
}
}
}
}
// allow method invocation to continue
return joinPoint.proceed();
}
The format of the #Around advice will be specific to your application. In this example, it checks for any class annotated with Controller in the package com.test.controllers and all subpackages. See http://static.springsource.org/spring/docs/3.0.x/reference/aop.html for additional options.
Good luck!
How about ResolveHandlerMethodInterceptor using reflection.
Below code is experimental and version-dependent(spring 3.0.2).
import java.lang.reflect.Method;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
public class ResolveHandlerMethodInterceptor implements HandlerInterceptor {
public final static String HANDLER_METHOD = "handlerMethod";
// Here is your servlet name
public final static String SERVLET_NAME = "XXXXX";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView)
throws Exception {
Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
System.out.println("postHandle>>>" + handlerMethod);
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception)
throws Exception {
Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
System.out.println("afterCompletion>>>" + handlerMethod);
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
ServletContext servletContext = request.getSession().getServletContext();
String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + SERVLET_NAME;
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext, attrName);
AnnotationMethodHandlerAdapter adapter = context.getBean(AnnotationMethodHandlerAdapter.class);
Method getMethodResolverMethod = adapter.getClass().getDeclaredMethod("getMethodResolver", Object.class);
getMethodResolverMethod.setAccessible(true);
Object servletHandlerMethodResolver = getMethodResolverMethod.invoke(adapter, object);
Method resolveHandlerMethod = servletHandlerMethodResolver.getClass().getMethod("resolveHandlerMethod", HttpServletRequest.class);
resolveHandlerMethod.setAccessible(true);
Method handlerMethod = (Method) resolveHandlerMethod.invoke(servletHandlerMethodResolver, request);
request.setAttribute(HANDLER_METHOD, handlerMethod);
System.out.println("preHandle>>>" + handlerMethod);
return true;
}
}
==reference==
http://toby.epril.com/?p=934
http://www.jarvana.com/jarvana/view/org/springframework/spring-webmvc/3.0.2.RELEASE/spring-webmvc-3.0.2.RELEASE-sources.jar!/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java?format=ok
So, these approaches listed are good, but they all have limitations. The AOP stuff is a good idea, but its limitation is that I need a way to get ahold of the request and response objects if I want to redirect or modify the response. The controller methods don't necessarily need the requests and responses, and requiring that they appear seems inelegant. I could use spring magic to get the request object from the Aspect, but I couldn't find a way to get the response.
Eventually, I came up with a middle way. I used a filter bean to get the request and the response objects and store them in a ThreadLocal. Then I created an aspect that has a reference to that filter, so that it could easily see the request and response objects.
Then I made the aspect wrap around methods based on the annotation, so I didn't even need to check on whether the annotation was present using code.
This combination approach appears to be working perfectly!
The only downside is that I can't figure out a good way to write an integration test that verifies that the aspect is invoked when there's an incoming request to that URL. It's a little scary that removing a single annotation leaves all my tests passing but allows unauthorized users through.
Thanks everybody for the great suggestions!

Resources