Possible misunderstanding of the doFilter method - java

I am actually pretty new to the Java EE specifications since I am kind of young. I never learned this things at school and I am facing a weird behaviour with the doFilter method.
Consider the following filter :
#WebFilter(filterName = "URLFilter", value = "/test")
public class URLFilter implements Filter {
public void destroy() {}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletResponse response = (HttpServletResponse) resp;
if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND)
response.sendRedirect("/");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {}
}
There is no servlet or page present in /test and in my browser, the status is obviously a 404 not found error when accessing the URL and so should be the value returned to me by the getStatus() method which isn't. (actually having a 200 status code)
Why is my filter not redirecting me to / as requested ? Do I misunderstand the use of Filters in general ?
UPDATE:
My question was about redirecting the client (using the sendRedirect()) when a page is not found. I did not understand the filter part because I didn't know that resp and req are actually filled with the new data when chain.doFilter() is called. (which I actually found strange since the doFilter is calling the next Filter chained by the COR pattern)
I've made a class inheriting the HttpServletResponseWrapper, implemented it, passed it to the Filter and it's working fine now.

Your filter is invoked before trying to access the actual resource (servlet, page, file, whatever) located at /test. So the response status can't be 404 yet at this time.
Then your filter invokes chain.doFilter(), thus telling the container to actually serve the resource at /test. Since there is no such resource, you get a 404.

Related

Disallow anything but GET for certain links in spring boot

I have a simple blog webpage with a lot of links. They are all using GetMapping. Therefore whenever I have a bot try to access those pages using POST I get an error saying
Request method 'Post' not supported.
I understand that this is caused because I use #GetMapping, and if I switch to #RequestMapping then everything will be fine. However I do not want to allow anyone to access my blog with POST. The only page that should be POST is /contact/message except that page everything should be accessed through GET.
So I have two questions:
How do I enforce people to use GET only.
How do I catch attempts to use POST and redirect them to /error?
Sidenote: I do not use spring security, there is no logging in or anything that is hidden behind an account. Also most of my mappings are using regex. Do not know if this info is of any help or not.
#GetMapping(value = {
"", "{page:^[1-9][0-9]*$}", "{section:^\\d*[a-zA-Z][a-zA-Z0-9]*[^.]+$}",
"{section:^\\d*[a-zA-Z][a-zA-Z0-9]*[^.]+$}/{page:^[1-9][0-9]*$}"})
You can add a filter in your application:
public class Tfil implements Filter {
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (!"GET".equals(req.getMethod())) {
//redirect or error page
HttpServletResponse res = (HttpServletResponse) servletResponse;
//here redirect
res.sendRedirect("redirect url");
//or return 404 code
res.setStatus(404);
}
filterChain.doFilter(servletRequest,servletResponse);
}
}

HttpServletRequest session expires while redirect in Microsoft Edge browser

I'm trying some URL on browser, it works well on all browsers including IE 10 but when on Microsoft Edge, it fails at a point while doing HttpServletResponse sendRedirect, which expires HttpServletRequest session and the expected page does not appear.
Please help for how we can resolve this browser specific redirect-session issue.
Basic code:
public class MyServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// some code where values are set in session
session.setAttribute(myAttribute, value);
response.sendRedirect("https://qa.sys.com/MainPage.jsf");
}
public class MyFilter implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
// myAttribute below comes null as request.getSession() is null
Boolean myAttribute = request.getSession().getAttribute(myAttribute);
}
In logs, I got this Exception:-
java.lang.IllegalStateException: Response already committed
at weblogic.servlet.internal.ServletResponseImpl.objectIfCommitted(ServletResponseImpl.java:1861)
at weblogic.servlet.internal.ServletResponseImpl.sendRedirect(ServletResponseImpl.java:961)
at weblogic.servlet.internal.ServletResponseImpl.sendRedirect(ServletResponseImpl.java:956)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
Truncated. see log file for complete stacktrace
You could also use F12 dev tools to check the Network tab in Edge and see if there's any error in console. This error usually occurs when a java servlet is attempting to write to the output stream (response) after the response has been committed. You could refer to this thread to find out why the response will get committed.
In this blog, it gives the solution and you could also refer to the sample code in the post:
It is always better to ensure that no content is added to the response after the forward or redirect is done to avoid IllegalStateException. It can be done by including a ‘return’ statement immediately next to the forward or redirect statement.
I also find an answer with detailed information about this issue and you could check it.

Adding Response headers after chain.doFilter

I have searched and seen a couple of answers about this problem, but still don't know how it's possible...
I'm asked to Implement a filter that returns response-time of an HTTP Request in the response header, eg. response-header: XX
in order to get collected by a web analytics client side library.
here's my code :
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse)response;
long startTime = System.nanoTime();
chain.doFilter(request, response);
long endTime = System.nanoTime();
httpResp.addHeader("response-time",endTime-startTime);
}
as well I tried to user HttpServletResponseWrapper
I managed to modify the response using OutputStream but didn't succeed with setting the response headers.
Thanks,Jay
Extend wrapper utility class like HttpServletResponseWrapper (using custom output streams) and pass it to the chain doFilter() method. If you dont do that, after chain doFilter() returns, the original (not wrapped) response will be gone and you will not have a chance to modify it.
Here you can find an example: Looking for an example for inserting content into the response using a servlet filter

Change ServletRequest server name programmatically

I need to change the serverName of the ServletRequest object in my Grails controller. I'm having trouble figuring out how to do this since the serverName is a read-only property.
The most correct thing to do is probably to set up a clever filter or redirect which "fixes" your request URL before your servlet even gets involved. I know nothing about how to do that; you should ask on serverfault.com if you want to do that.
In java, you can fake it by creating your own subclass of HttpServletRequestWrapper which provides setServerName() and overrides getServerName() while delegating all other methods to the superclass. You can then provide a filter which creates an instance of your request and sends that one down the chain.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
YourHttpServletRequest yourRequest =
new YourHttpServletRequest(request, newServerName);
chain.doFilter(yourRequest, response);
}
If I understand this correctly, CORS filter might help
I've used http://software.dzhuvinov.com/cors-filter.html in my previous project.
But you can also lookup on github for example https://github.com/eBay/cors-filter

Get Cookies from ServletRequest

I'm using ServletRequestListener to attach to new requests, get a ServletRequest object and extract cookies from it.
I've noticed that only HTTPServletRequest has cookies but I haven't found a connection between those two objects.
Is it okay to use
HttpServletRequest request = ((HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest());
to retrieve the request while in a RequestInitialized method? (I do want to run on every request)
FYI - This is all done in a JSF 1.2 Application
This is not correct. The FacesContext isn't available in a ServletRequestListener per se. The getCurrentInstance() might return null, leading to NPE's.
If you're running the webapp on a HTTP webserver (and thus not some Portlet webserver for example), you could just cast the ServletRequest to HttpServletRequest.
public void requestInitialized(ServletRequestEvent event) {
HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
// ...
}
Note that a more common practice is to use a Filter for this since you can map this on a fixed URL pattern like *.jsf or even on specific servlets so that it runs only when the FacesServlet runs. You might for example want to skip cookie checks on static resources like CSS/JS/images.
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
// ...
chain.doFilter(req, res);
}
When you happens to be already inside the JSF context (in a managed bean, phaselistener or whatever), you could just use ExternalContext#getRequestCookieMap() to get the cookies.
Map<String, Object> cookies = externalContext.getRequestCookieMap();
// ...
When running JSF on top of Servlet API, the map value is of type javax.servlet.http.Cookie.
Cookie cookie = (Cookie) cookies.get("name");
Yes, you can do that. In Web scenarios, this will always be ok. If you want to be sure, you could do a check for the type first. (Good practice anyway):
if (FacesContext.getCurrentInstance().getExternalContext().getRequest() instanceof HttpServletRequest) {
...
By the way: Why do you have to use FacesContext? From where are you calling this code?

Categories

Resources