Simple instance of Embedded Tomcat with a Servlet and a Filter:
Tomcat tomcat = new Tomcat();
Context rootCtx = tomcat.addContext("", base.getAbsolutePath());
FilterDef filterDefinition = new FilterDef();
filterDefinition.setFilterName(URLFilter.class.getSimpleName());
filterDefinition.setFilterClass(URLFilter.class.getName());
rootCtx.addFilterDef(filterDefinition);
FilterMap filter1mapping = new FilterMap();
filter1mapping.setFilterName(URLFilter.class.getSimpleName());
filter1mapping.addURLPattern("/*");
rootCtx.addFilterMap(filter1mapping);
Tomcat.addServlet(rootCtx, "Servlet1", new Servlet1());
rootCtx.addServletMapping("/Servlet1", "Servlet1");
URL Filter Implementation:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Boolean filtered = false;
System.out.println("request intercepted");
if (request.getAttribute("filtered") != null) {
filtered = true;
request.setAttribute("filtered", filtered);
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendRedirect("/Servlet1");
return;
} else {
filterChain.doFilter(request, response);
}
}
For some reason this ends up in an infinite loop and never arrives at Servlet1. What is the correct way of implementing a URL Filter so that I can double check the parameters around the request before delivering the response?
Look at the JavaDocs for sendRedirect(). You're telling the client on each request to go back to /Servlet1, but then you're filtering it indefinitely. At some point you need to stop sending redirects in your filter!
To clarify some more. If you don't intend to actively filter a request, the only thing you need to do is call
filterChain.dofilter(request, response);
Don't send back a redirect unless you really mean to send an HTTP 302 (Temporary Redirect). The Servlet Filter Chain is an important part of the servlet process and although it may seem counterintuitive, you can make your filter appear to do nothing by calling the filterChain.doFilter command, which allows the request to proceed to other filters that are configured by your application server.
What's not clear to me is what filtering your filter is attempting to do. If you intend to filter by URL then you should look for matching URLs and then redirect only if there's a match.
As this solved your problem, posting it as an answer.
Firstly, you need to ensure that you're either passing the request along the application filter chain, or you're redirecting it:
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
Boolean filtered = false;
System.out.println("request intercepted");
if (!filtered) {
filtered = true;
((HttpServletResponse) response).sendRedirect("/Servlet1");
return;
}
filterChain.doFilter(request, response);
}
Then you need to ensure that the filter knows when an incoming request has already been redirected, so that it doesn't redirect it again:
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
Boolean filtered = false;
System.out.println("request intercepted");
if (request.getAttribute("filtered") != null) {
filtered = (Boolean) request.getAttribute("filtered");
}
if (!filtered) {
request.setAttribute("filtered", Boolean.TRUE);
((HttpServletResponse) response).sendRedirect("/Servlet1");
return;
}
filterChain.doFilter(request, response);
}
Related
Hi I have some existing code in my application that use a security filter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (someCondition==TRUE) {
super.doFilter(request, response, chain);
}else{
chain.doFilter(request, response);
}
}
Now I want to redirect the user to a certain URL after the filter flow is completed. I have added the sendRedirect Method like the below example in code, but this is breaking the filter flow in some cases.
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (someCondition==TRUE) {
String targetURL = request.getParameter("targetURL");
if(targetURL != null && targetURL != "undefined") {
LOGGER.info("redirecting to this targetURL : " + targetURL);
response.sendRedirect(targetURL);
// this is an internal URL and should be redirected after executing the filter flow
} else {
super.doFilter(request, response, chain);
}
}else{
chain.doFilter(request, response);
}
}
I have a feeling that this is not correct way to redirect the User to an internal URL as it breaks the filter flow. What is correct way of redirecting to URL after filter processing has been completed?
I'm trying to write a servlet filter that will add headers to the response depending on the status of the request. I know I have to wrap the response with a HttpServletResponseWrapper before passing to the chain.doFilter but the headers never get sent, so I'm obviously missing something very obvious.
Code looks something like:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(httpServletResponse);
chain.doFilter(request, responseWrapper);
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
}
Is there something I have to capture in the wrapper to prevent the response from going out to the client until the check is complete?
So the frustrating part about this spec is that you have to completely intercept the ServletOutputStream and buffer it. I ended up following the example here :: https://stackoverflow.com/a/11027170/76343
The base class HttpServletResponseWrapper is a complete passthrough and as soon as the output stream is closed, all further modifications to the response are mute.
Unfortunately there doesn't seem to be a more elegant way to accomplish this.
You need to extend HttpResponseWrapper() and override the appropriate methods. Just using a vanilla HttpResponseWrapper by itself accomplishes exactly nothing.
The order of this code is inverted:
chain.doFilter(request, responseWrapper);
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
Try this instead:
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
chain.doFilter(request, responseWrapper);
The doFilter method does not return to your method until after the response has been sent on the wire.
This is actually possible. But because after calling chain.doFilter(request, response) the response is already committed, we have to set the headers after receiving the status code, but before the response is committed. Here is an example:
public class HeadersFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
chain.doFilter(request, new ResponseWrapper(response));
}
public static class ResponseWrapper extends HttpServletResponseWrapper {
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void setStatus(int sc) {
super.setStatus(sc);
// SET YOUR HEADERS HERE
// setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
}
}
#Override
public void destroy() {
}
}
I'm implementing a (sort of) load balancing HandlerInterceptor using Spring Boot.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
if (shouldUseServer1(uri)) {
response.sendRedirect(server1Uri);
} else {
response.sendRedirect(server2Uri);
}
}
The idea is, that based on the url, we either redirect to one service or another. The application doesn't have any explicit RequestMappings (yet).
Now the problem is, when the interceptor is called, the request is redirected to the default Spring error handler. As a result the URI stored in the HttpServletRequest is replaced by /error (effectively denying the access to the original URI).
Is there any way to intercept a request before it is rerouted to the error handler (or to get the original uri)?
EDIT:
Because of the way Spring MVC handles requests with no mapping, you'll either need a filter:
#Component
public class CustomFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
request.getSession().setAttribute("ORIGINAL_REQUEST_URI", request.getRequestURI());
chain.doFilter(request, response);
// alternatively, ignore the last 2 lines
// and just do your redirects from here
// and don't continue the filter chain
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Otherwise, if you'd rather not rely on the session, you'll need to make the DispatcherServlet throw an exception in case no handler mapping is found, and then send the redirect from a #ControllerAdvice error handler:
#ControllerAdvice
class NoHandlerFoundExceptionExceptionHandler {
#ExceptionHandler(value = NoHandlerFoundException.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, NoHandlerFoundException e) throws Exception {
String uri = // resolve the URI
return new ModelAndView("redirect:" + uri);
}
}
To avoid duplication, you may want to have a common class that you'll call from both the interceptor and the error handler.
I have an authentication filter for my web app, and it successfulyl loads up the css for all the web pages after logging in, except for three pages the homepage(index) the about us page(about) and the register page(register). For some reason I cannot figure out why my filter is not deciding to load up the css for those three pages but for the other pages it works, I've checked the jsp files for all the pages and cant spot whats wrong.
I was told I had to fix something in my doFilter method with the excludeURLs but I'm still confused.
Here is my doFilter:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpSession session = httpReq.getSession(false); // if the fail login it doesnt create a session
if (session != null && session.getAttribute("user") == null && !excludeURLs.contains(httpReq.getServletPath()) ){
RequestDispatcher rd = httpReq.getRequestDispatcher("login.jsp");
rd.forward(request, response);
return;
}
chain.doFilter(request, response);
}
And here is the init method where I exclude specific URLs:
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("AuthenticationFilter Initiated...");
//Pages/Servlet
excludeURLs.add("/login");
excludeURLs.add("/login.jsp");
excludeURLs.add("/index");
excludeURLs.add("/index.jsp");
excludeURLs.add("/about");
excludeURLs.add("/about.jsp");
excludeURLs.add("/register");
excludeURLs.add("/signup.jsp");
//Images
excludeURLs.add("/Images");
excludeURLs.add("/css");
excludeURLs.add("/js");
}
Assuming:
Your css files to have the extension ".css",
Unauthenticated access to css files isn't an issue for your you/your organization, a quick fix would be:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpSession session = httpReq.getSession(false); // if the fail login it doesnt create a session
String path= httpReq.getRequestURI();
if(path.endsWith(".css")){
chain.doFilter(request,response);
return;
}
if (session != null && session.getAttribute("user") == null && !excludeURLs.contains(httpReq.getServletPath()) ){
RequestDispatcher rd = httpReq.getRequestDispatcher("login.jsp");
rd.forward(request, response);
return;
}
chain.doFilter(request, response);
}
I have this piece of code in each Servlet I have:
httpServletRequest.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("text/html;charset=utf-8");
Is there any way to set these globally for the application?
Use a Filter that will interrupt the request and set the Character Encoding and Content Type for all the servlets.
Please have a look at The Essentials of Filters
Modifying the request headers and data. You do this by providing a customized version of the request.
Modifying the response headers and data. You do this by providing a customized version of the response.
Sample code directly from Example: Modifying the Request Character Encoding
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws
IOException, ServletException {
String encoding = selectEncoding(request);
if (encoding != null){
//request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
//response.setContentType(contentType);
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws
ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
}
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}