Section 3.8. Servlet Filters


3.8. Servlet Filters

Version 2.3 of the Servlet API introduced a new method of handling requests using the javax.servlet.Filter class. When filters are used, the servlet container creates a filter chain, which consists of zero or more Filter objects and a destination resource, either a servlet or another resource available on the web server (such as an HTML or JSP file).

Filters are installed in the server and associated with particular request paths (just like servlets). When a filtered resource is requested, the servlet constructs a filter chain and calls the doFilter( ) method of the first filter in the filter chain, passing a ServletRequest, a ServletResponse, and the FilterChain object. The filter can then perform processing on the request. Filters are often used to implement logging, control security, or set up connection-specific objects. A filter can also wrap the ServletRequest and ServletResponse classes with its own versions, overriding particular methods. For instance, one of the example filters included with the Tomcat server adds support for returning compressed output to browsers that support it.

After the filter has processed the response, it can call the doFilter( ) method of the FilterChain to invoke the next filter in the sequence. If there are no more filters, the request is passed on to its ultimate destination. After calling doFilter(), the filter can perform additional processing on the response received from farther down the chain.

In the event of an error, the filter can stop processing, returning to the client whatever response has already been created or forwarding the request to a different resource.

Example 3-5 provides a form-based authentication filter that could be customized to provide additional functionality. It works by intercepting each request and checking the HttpSession for an attribute called enterprise.login. If that attribute contains a Boolean.TRUE, access is permitted. If not, the filter checks for request parameters named login_name and login_pass and searches for a match in a hashtable containing valid username/password pairs. If valid login credentials are found, filter chain processing continues. If not, the user is served a login page located at /login.jsp, retrieved via a RequestDispatcher.[*]

[*] This isn't a highly secure system. Unless the client has connected via SSL, the username/password combination is transmitted unencrypted over the Internet. Also, successful logins leave the login_name and login_pass parameters in the request when processing it, potentially making them available to a malicious JSP file or servlet. This can be an issue when designing a shared security scheme for dynamic content created by a group of different users (such as at an ISP). One way to get around this is to create a custom HttpServletRequest wrapper that filters out the login_name and login_pass parameters for filters and resources further down the chain.

Astute readers will note that we try to retrieve the user's hashtable from a servlet context attribute. We showed how to set this attribute at the web application level earlier in the chapter. In case you don't have that set up, the filter's init( ) method will create its own if it can't find one in the context.

Example 3-5. AuthenticationFilter
 import javax.servlet.*; import javax.servlet.http.*; import java.util.Hashtable;   public class AuthenticationFilter implements Filter {     private Hashtable users = null;   public void init(FilterConfig config)     throws javax.servlet.ServletException {       users = (Hashtable)config.getServletContext(  ).getAttribute(                                "enterprise.users");     if(users == null) {        users = new Hashtable(5);        users.put("test", "test");     }   }     public void doFilter(     ServletRequest req, ServletResponse res, FilterChain chain)     throws java.io.IOException, javax.servlet.ServletException {       HttpServletRequest request = (HttpServletRequest)req;     HttpSession sess = request.getSession(true);       if(sess != null) {       Boolean loggedIn = (Boolean)sess.getAttribute("enterprise.login");       if (loggedIn != Boolean.TRUE) {         String login_name = request.getParameter("login_name");         String login_pass = request.getParameter("login_pass");         if((login_name != null) && (login_pass != null))           if(users.get(login_name).toString(  ).equals(login_pass)) {             loggedIn = Boolean.TRUE;             sess.setAttribute("enterprise.login", Boolean.TRUE);             sess.setAttribute("enterprise.loginname", login_name);           }       }         if (loggedIn == Boolean.TRUE) {         chain.doFilter(req, res);       }  else {         request.setAttribute("originaluri", request.getRequestURI(  ));         request.getRequestDispatcher("/login.jsp").forward(req, res);       }     }   }     public void destroy(  ) {     // Code cleanup would be here   } }

Here's the JSP page used to display the login form. The important thing to note is that the form submits back to the original URI. The filter uses the setAttribute( ) method of HttpServletRequest to specify the URI to post the form back to; the filter is then reapplied, and if the user has provided appropriate credentials, access to the resource is granted. For more on JSP, see Chapter 4.

 <html><body bgcolor="white">   <% out.print ("<form method=post action=\""+request.getAttribute("originaluri").toString(  ) +"\">"); %>   Login Name: <input type=text name="login_name"><br> Password: <input type=password name="login_pass"> <input type=submit value="Log In"> </form>   </body></html>

When configuring the filter (we'll see an example in the next section), map it only to the paths you wish to protect. Mapping it to /* will not work since that would also protect the login.jsp file. If you did want to protect your whole application, you could build the login form into the filter, but that's not good practiceyou have to recompile for every change.

3.8.1. Filters and Request Dispatchers

In the original filter specification, a filter would run only in response to an original request from a remote client. Requests created internally via the RequestDispatcher class would not trigger new filter chains. The example in the previous section relies on this behaviorotherwise the filter would never be able to display the login.jsp file.

Servlet API 2.4 gives us a little more flexibility here. When declaring a filter mapping in web.xml, we can indicate whether it runs on requests only (the default) or whether it runs on includes, forwards, or any combination of the three. For example:

 <filter-mapping>    <filter-name>Authentication Filter</filter-name>    <url-pattern>/secrets/*</url-pattern>    <dispatcher>FORWARD</dispatcher>    <dispatcher>REQUEST</dispatcher> </filter-mapping>

This configuration causes the filter to run on request forwards as well as request dispatches, ensuring that security is applied even if an unprotected portion of the application redirects the user to a protected portion. However, use this power with care: many applications, including the various web application frameworks, make extensive use of request forwarding, which could cause the filter to run several times in the course of a single user request.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net