web.xml error-page location redirect is not going through my filter definitions - spring-mvc

In my web.xml I've done the following error-page mappings, but when they are invoked those invoked requests are not passing through the filter definitions specified in web.xml file.
<error-page>
<error-code>403</error-code>
<location>/error.vm?id=403</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/error.vm?id=400</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.vm?id=404</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/servlet-exception.vm</location>
</error-page>
My application is using spring-mvc and I want to handle the handler not found condition from spring mvc. My application is an multi tenant application where some filters are responsible for setting some information related to the schema.
The requests are reaching in my error.vm controller but since they are passing through the filter I'm not able to determine the theme and SecurityContext etc.
How to solve this problem?
Thank you.

Instead of using web.xml's error pages you could use a servlet filter. The servlet filter could be used to catch all exceptions, or just a particular exception such as org.springframework.web.portlet.NoHandlerFoundException. (Is that what you mean by "handler not found" exception?)
The filter would look something like this:
package com.example;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.web.portlet.NoHandlerFoundException;
public class ErrorHandlingFilter implements Filter {
public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
// Or you could catch Exception, Error, Throwable...
// You probably want to add exception logging code here.
// Putting the exception into request scope so it can be used by the error handling page
request.setAttribute("exception", e);
// You probably want to add exception logging code here.
request.getRequestDispatcher("/WEB-INF/view/servlet-exception.vm").forward(request, response);
}
}
}
Then, set this up in web.xml with the help of Spring's DelegatingFilterProxy:
<filter>
<filter-name>errorHandlingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And then finally, turn the filter into a spring bean inside your spring context xml:
<bean id="errorHandlingFilter" class="com.example.ErrorHandlingFilter" />
You might have to experiment with the order of the filter in the filter chain so that failed requests still go through the other filters you mentioned. If you're having trouble with that, a variation would be to do an HTTP redirect instead of a forward, like this:
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
request.getSession().setAttribute("exception", e);
response.sendRedirect("/servlet-exception.vm");
}
That would force the browser to request your error handling page as a new http request, which might make it easier to ensure it goes through all of the right filters first. If you need the original exception object, then you could put it in the session instead of the request.

maybe
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Related

Servlet WebServlet multiple urlPattern

I must write a servlet that should do multiple operations according to the button I press on the html page.
I have one button that is a Insert into the Db, another is a Delete, another is a Select.
So I would like my servlet will do all those three operations in the doGet method.
My clue was to put
#WebServlet("select", "insert", "delete")
public class MyServlet extends HttpServlet {
...
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getServletPath()!=null?request.getServletPath():"";
switch(path){
case "/select":
...
break;
switch(path){
case "/insert":
...
break;
switch(path){
case "/delete":
...
break;
then, into the javascript file it would have been (for select)
var req={
method: 'GET',
url: 'http://localhost:8080/AngularDb/select',
}
$http(req);
and into the web.xml
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
But all this doesn't work.
May you tell me what is wrong?

Spring 4 upgrade broke error page filter chain

Scenario:
We have an interceptor that looks for bogus attributes in URLs and throws a NoSuchRequestHandlingMethodException if it finds one. We then display a custom 404 page.
All pages go through the same filter chain to set up the local request state, log some information, and then display the requested page. In Spring 4, it stopped going through the filter chain for the 404 page in this case. It still goes through it if you go to a completely bogus page, and the 404 works, but when we throw the NoSuchRequestHandlingMethodException, the filters don't happen.
Spring 3:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. New filter chain starts
5. We log the error page metrics
6. We display a nice 404 page to the customer
Spring 4:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. We try to log the error page metrics, but NPE since a second filter chain never started
5. We display a terrible blank page to the customer
Filter code in web.xml:
<!-- The filter that captures the HttpServletRequest and HttpServletResponse-->
<filter>
<filter-name>ServletObjectFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>xxxxxxx.servletObjectFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ServletObjectFilter</filter-name>
<servlet-name>springmvc</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
<error-page>
<error-code>404</error-code>
<location>/errors/404</location>
</error-page>
Filter code:
public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
throws ServletException, IOException {
try {
getServletContainer().setServletObjects( request, response );
chain.doFilter( request, response );
} finally {
getServletContainer().removeAll();
}
ServletContainer:
static final ThreadLocal< HttpServletRequest > REQUESTS = new ThreadLocal< HttpServletRequest >();
static final ThreadLocal< HttpServletResponse > RESPONSES = new ThreadLocal< HttpServletResponse >();
public void setServletObjects( HttpServletRequest request, HttpServletResponse response ) {
REQUESTS.set( request );
RESPONSES.set( response );
}
public void removeAll() {
REQUESTS.remove();
RESPONSES.remove();
}
Code that then fails:
public class RequestResponseAwareBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization( Object bean, String beanName ) {
...
if ( bean instanceof RequestAware ) {
HttpServletRequest request = getServletContainer().getRequest();
if ( request == null ) {
throw new IllegalStateException( "The request object is NULL" );
}
RequestAware requestAware = (RequestAware) bean;
requestAware.setRequest( request );
}
}
I "solved" the problem by splitting up my error page #Controller into two, one where they're the targets of internal redirects and don't get the filter chain, and one where they are directly loaded, and do get the filter chain. I then added the redirect #Controller to the interceptor blacklist, so it doesn't require any logic or data from the filters. It solved this specific problem, but I'm worried that something else in my codebase also relies on this behavior.

Getting ClassNotFoundException when trying to implement web filter in my JSF app

i have a JSF app and for some reasons i need to refresh the page on browser back button.I tried implementing the solution given in Force JSF to refresh page / view / form when opened via link or back button by BalusC ,the only difference is that my app runs with servlet version 2.5 so i did the mapping in web.xml as below
<?xml version="1.0" encoding="UTF-8"?>
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
javax.faces.STATE_SAVING_METHOD
client
javax.faces.CONFIG_FILES
/WEB-INF/faces-config.xml
com.sun.faces.config.ConfigureListener
javax.faces.PROJECT_STAGE
Production
javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE
true
login.xhtml
FacesServlet
javax.faces.webapp.FacesServlet
1
FacesServlet
/faces/
FacesServlet
.jsf
FacesServlet
.faces
FacesServlet
.xhtml
SessionUtil
SessionUtil
com.gaic.lpsr.utilclasses.SessionUtil
SessionUtil
/SessionUtil
<filter>
<filter-name>cacheFilter</filter-name>
<filter-class>com.gaic.lpsr.utilclasses.NoCacheFilter.java</filter-class>
</filter>
<filter-mapping>
<filter-name>cacheFilter</filter-name>
<servlet-name>FacesServlet</servlet-name>
</filter-mapping>
My filter class is
public class NoCacheFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(req, res);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
// ...
}
I have included the jar servlet-api-2.5.jar.When i try to deploy app in tomcat server(version 6.0.29) i am getting the below error.
SEVERE: Exception starting filter cacheFilter
java.lang.ClassNotFoundException: com.gaic.lpsr.utilclasses.NoCacheFilter.java
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1645)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1491)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:269)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:422)
at org.apache.catalina.core.ApplicationFilterConfig.(ApplicationFilterConfig.java:115)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4001)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4651)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
at org.apache.catalina.core.StandardService.start(StandardService.java:519)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
Please guide me how to fix this problem.
Did silly mistake in web.xml :).Solved by removing .java extension in filter mapping
<filter-class>com.gaic.lpsr.utilclasses.NoCacheFilter</filter-class>

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.

Servlet + redirection

I need some suggestions. I have defined servlet mapping as
<servlet-mapping>
<servlet-name>My Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
However there are some static html files. So i have mapped them to the default servlet to serve the static html files
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
However, i want the user to have access to them only when the user has logged in. The welcome page is Login.html. If the user tries to access any other static file and has not logged in i.e there is not session then i should redirect user to the login page. But with current mapping the user is able to access index.html file as the request is served by default servlet.
Please suggest .
Your intent is to have a front controller servlet, not to replace the default servlet. So you should actually not be mapping your front controller servlet on /.
You should map the controller servlet on a more specific URL pattern, such as /app/*. To keep URLs transparent, your best bet is to create a filter which determines the request URI and continues the chain on static content like HTML and dispatches the remnant to the controller servlet.
E.g.
String uri = request.getRequestURI();
if (uri.endsWith(".html")) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}
You can extend the DefaultServlet of your web server.The extended servlet will be your front controller. In the doGET or doPOST method forward your static pages to the super class. DefaultServlet is the servlet that is mapped to url "/" by default. I have used it with jetty server but it can be implemented in tomcat as well.
public class FrontController extends DefaultServlet {
#Override
public void init() throws UnavailableException {
super.init();
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
/*
* if request is trying to access inside /static then use the default
* servlet. YOU CAN USE YOUR OWN BUSINESS LOGIC TO FORWARD REQUESTS
* TO DEFAULTSERVLET
*/
if (uri.startsWith("/static/")) {
super.doGet(request, response);
return;
} else {
// else use your custom action handlers
}
}
}
In the above code samples I have forwarded all the requests starting with /static/ to the default servlet to process. In this way you can map the FrontController to "/" level .
<servlet>
<description></description>
<display-name>FrontController</display-name>
<servlet-name>FrontController</servlet-name>
<servlet-class>FrontController</servlet-class>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/</url-pattern>

Resources