Chapter 14. Using Filters

Using Filters

As of Servlet Specification 2.3 (which you can download at http://jcp.org/aboutJava/communityprocess/first/jsr053/index.html), the Tomcat server supports filters. Here are today's topics:

  • Understanding filters

  • Creating a simple filter that prints to the server console

  • Chaining filters

  • Logging user access data

  • Using a filter's initialization parameters

  • Restricting access to a Web resource to particular times of the day

  • Inserting text into the response from a filter

  • Enforcing user authentication with filters

Filters are much like add-ons for your Web resources, such as JSP pages and servlets. You can use filters as a gateway for those Web resources, granting or restricting access. You can modify the data you send to a Web resource. You can add additional text to the output of a JSP page or servlet. You can also log access to various Web resources. There's plenty you can do with filters, and you'll get a good introduction to all these things today.

Understanding Filters

To use a filter, you have to implement the javax.servlet.Filter interface in your filter's code. This interface has three important methods—init, which is called when the filter is first initialized, destroy, which is called when the filter is destroyed, and doFilter. The doFilter method is where the action is—you use this method to pass the response and request objects on to Web resources and other filters. You can see these methods in more detail in Table 14.1.

The init method is also passed an object of the FilterConfig class, which gives you access to initialization parameters, Web application contexts, and more.

Table 14.1. Methods of the javax.servlet.Filter Interface

Method

Does This

void destroy()

Called by the server when a filter is being taken out of service.

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

Called by the server each time a request/response pair is passed through the filter chain.

void init(FilterConfig filterConfig)

Called by the server when a filter is being placed into service.

The next step is to put this new interface to work.

Creating a Simple Filter

This first example will create a simple filter that you can connect to a Web resource (a servlet, in this case). When the user accesses the servlet, the filter is automatically invoked both before and after the servlet is accessed. The filter, ch14_01.java, will take advantage of that to print "In ch14_01." before the servlet is accessed, and "Leaving ch14_01." after the servlet is accessed to the server console.

You start by implementing the Filter interface in this new class:

import java.io.*; 
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_01 implements Filter
{
        .
        .
        .
}

To implement the Filter interface, you must override three methods—init, destroy, and doFilter. In this case, the init and destroy methods will be empty:

public class ch14_01 implements Filter 
{
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {
        .
        .
        .
  }

    public void destroy()
    {
    }

    public void init(FilterConfig filterConfig)
    {
    }
}

It's the doFilter method that's important here. You'll see how to connect this filter to a servlet in web.xml, so the filter's code is called before the servlet is. You can take advantage of this to print to the server console with the System.out.println method. You can also pass on the current access to the servlet using the doFilter method of the FilterChain object passed to you in the filter's doFilter method. Here's what it looks like in code:

public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain)
    throws IOException, ServletException
{
    System.out.println("In ch14_01.");
    chain.doFilter(request, response);
    System.out.println("Leaving ch14_01.");
}

The call to chain.doFilter calls any additional filters attached to the servlet whose input and output you're filtering. At the end of the filter chain is the Web resource itself, which is a servlet here (if there are no additional filters after this one, chain.doFilter calls the servlet itself). Note that this filter is called when the target servlet is accessed, enabling you to handle the servlet's input in the request object. After the servlet is finished, the call to chain.doFilter returns, enabling you to handle the servlet's output in the response object.

In this case, the first simple filter displays "In ch14_01." on the server console, and then calls the target servlet. When the servlet is done, the filter displays "Leaving ch14_01." on the server console. You can see the complete filter code in Listing 14.1.

Example 14.1. A Simple Filter (ch14_01.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_01 implements Filter
{
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {
        System.out.println("In ch14_01.");
        chain.doFilter(request, response);
        System.out.println("Leaving ch14_01.");
    }

    public void destroy()
    {
    }

    public void init(FilterConfig filterConfig)
    {
    }
}

To use this filter, you need a target Web resource to filter. This example will use the servlet you see in Listing 14.2. The servlet just displays some text in a Web page, as you can see in its code.

Example 14.2. Target Servlet for the Simple Filter (ch14_02.java)

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_02 extends HttpServlet
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("Using a Simple Filter");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<H1>Using a Simple Filter</H1>");

        out.println("The simple filter will send text to the server console.<BR>");

        out.println("</BODY>");
        out.println("</HTML>");
    }
}

After compiling the filter (make sure you include servlet.jar in the classpath) and the target servlet, you can place both compiled files in ch14WEB-INFclasses. The next step is to connect the filter (ch14_01) to the servlet (ch14_02).

This is done in web.xml. In web.xml, the <web-app> element can include two elements, <filter> and <filter-mapping>, like the following:

<web-app> 
    <icon>
    <display-name>
    <description>
    <distributable>
    <context-param>
    <filter>
    <filter-mapping>
    <servlet>
    <servlet-mapping>
    <session-config>
    <mime-mapping>
    <welcome-file-list>
    <error-page>
    <taglib>
    <resource-ref>
    <security-constraint>
    <login-config>
    <security-role>
    <env-entry>
    <ejb-ref>
</web-app>

Here are the subelements you need in the <filter> element:

<filter> 
    <filter-name>
    <filter-class>
</filter>

And here are the subelements you need in the <filter-mapping> element:

<filter-mapping> 
    <filter-name>
    <url-pattern>
</filter-mapping>

In this case, the <filter> element names the filter and associates it with a Java class:

<filter> 
    <filter-name>Simple Filter</filter-name>
    <filter-class>ch14_01</filter-class>
</filter>

The <filter-mapping> element then associates the named filter with a particular Web resource. The Web resource here is the ch14_02 servlet:

<filter-mapping> 
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_02</url-pattern>
</filter-mapping>

This URL pattern only matches the ch14_02 servlet. You can also use other URL patterns, as when you learned how to map servlets in Day 12, “Creating Servlets,” including using * as a wildcard. In addition to connecting the filter to the name ch14_02, you also have to map the target servlet to that name, using the <servlet> and <servlet-mapping> elements, as you see in web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?> 

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <filter>
    <filter-name>Simple Filter</filter-name>
    <filter-class>ch14_01</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_02</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>ch14_02</servlet-name>
    <servlet-class>ch14_02</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ch14_02</servlet-name>
    <url-pattern>/ch14_02</url-pattern>
  </servlet-mapping>

</web-app>

When you navigate to the target servlet, ch14_02 (http://localhost:8080/ch14/ch14_02), you'll see its output, as you see in Figure 14.1.

The simple filter's target servlet.

Figure 14.1. The simple filter's target servlet.

Accessing the simple filter's target servlet also runs the filter's code automatically. You can see the result in the server console in Figure 14.2, where the filter caught the servlet access both before the servlet ran and after it finished.

Using a simple filter.

Figure 14.2. Using a simple filter.

Filtering JSP Pages

That gives you a good start on filters. Note that the target Web resource here was a servlet—but this is a book about JSP; how about adding a filter to a JSP page? Take a look at the JSP page you see in Listing 14.3, which produces output similar to the target servlet you just filtered.

Example 14.3. The Simple Filter's Target JSP (ch14_03.jsp)

<HTML>
    <HEAD>
        <TITLE>Using a Simple Filter With JSP</TITLE>
    </HEAD>
    <BODY>
        <H1>Using a Simple Filter With JSP</H1>
        The simple filter will send text to the server console.
        <BR>
    </BODY>
</HTML>

How can you add the ch14_01 filter to this JSP page? With a <filter-mapping> element, as you'd expect. This time, however, you set the URL pattern for the filter to the name of the JSP page, ch14_03.jsp, such as in web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?> 

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <filter>
    <filter-name>Simple Filter</filter-name>
    <filter-class>ch14_01</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_02</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_03.jsp</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>ch14_02</servlet-name>
    <servlet-class>ch14_02</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ch14_02</servlet-name>
    <url-pattern>/ch14_02</url-pattern>
  </servlet-mapping>

</web-app>

You can see the results in Figure 14.3, where the user is navigating to the target JSP page (http://localhost:8080/ch14/ch14_03.jsp).

Filtering a JSP page.

Figure 14.3. Filtering a JSP page.

Just as the simple filter handled input and output from the target servlet, the filter displays this on the server console when the target is this JSP page:

Starting service Tomcat-Apache 
Apache Tomcat/4.0.3
In ch14_01.
Leaving ch14_01.

Creating Filter Chains

So far you've seen just one filter at work. However, you can chain filters so that one is executed after another. You can do this with the FilterChain object passed to your filter's doFilter method, which has been named chain in the code so far:

public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain)
    throws IOException, ServletException
{
    System.out.println("In ch14_01.");
    chain.doFilter(request, response);
    System.out.println("Leaving ch14_01.");
}

You call the chain object's doFilter method to pass the current access on to the next filter (or the target resource if there are no other filters). The FilterChain class's doFilter method is the same as the version you've seen in the Filter interface, and you call it with the same arguments.

So how can you attach more than one filter to a Web resource? Say that you have a new filter, such as the one you see in Listing 14.4, which prints out the messages "In ch14_04." and "Leaving ch14_04.". How do you connect this filter to a JSP page in addition to the previous filter, ch14_01?

Example 14.4. Using Servlet Sessions (ch14_04.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_04 implements Filter
{
  public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain)
    throws IOException, ServletException
  {

    System.out.println("In ch14_04.");
    chain.doFilter(request, response);
    System.out.println("Leaving ch14_04.");
  }

  public void destroy()
  {
  }

  public void init(FilterConfig filterConfig)
  {
  }

}

As you might expect, you connect both filters to a Web resource in web.xml. For example, suppose you have the JSP page you see in Listing 14.5.

Example 14.5. Chained Filters Target JSP (ch14_05.jsp)

<HTML>
    <HEAD>
        <TITLE>Chaining Filters</TITLE>
    </HEAD>
    <BODY>
        <H1>Chaining Filters</H1>
        Both chained filters will send text to the server console.
        <BR>
    </BODY>
</HTML>

You can attach both filters ch14_01 and ch14_04 to this JSP page in web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?> 

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <filter>
    <filter-name>Simple Filter</filter-name>
    <filter-class>ch14_01</filter-class>
  </filter>

  <filter>
    <filter-name>Chained Filter</filter-name>
    <filter-class>ch14_04</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_02</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_03.jsp</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_05.jsp</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Chained Filter</filter-name>
    <url-pattern>/ch14_05.jsp</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>ch14_02</servlet-name>
    <servlet-class>ch14_02</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ch14_02</servlet-name>
    <url-pattern>/ch14_02</url-pattern>
  </servlet-mapping>

</web-app>

Note that this version of web.xml attaches the ch14_01 filter before the ch14_04. When the user navigates to the filtered JSP page in this example (http://localhost:8080/ch14/ch14_05.jsp), she'll see the results you see in Figure 14.4.

Chaining filters.

Figure 14.4. Chaining filters.

Both chained filters were in fact activated, as you see in Figure 14.5, which shows their output in the server console.

The output of chained filters.

Figure 14.5. The output of chained filters.

Note the order in which the filters were activated:

Starting service Tomcat-Apache 
Apache Tomcat/4.0.3
In ch14_01.
In ch14_04.
Leaving ch14_04.
Leaving ch14_01.

First, ch14_01 was activated, followed by ch14_04. After the chained filters were called, the JSP page was executed. Note that the chained filters were executed in reverse order—ch14_04 first, and then ch14_01. That's how chaining works—the user access went from ch14_01 to ch14_04, and then to the JSP page. After the JSP page finished executing, control returned to the ch14_04 filter, and then back to the ch14_01 filter.

Using Initialization Parameters

As with servlets, you can store initialization parameters for filters in web.xml. You can also access those parameters with the object that implements the javax.servlet.FilterConfig interface you're passed in the doFilter method of the filter itself. You can see the method of the javax.servlet.FilterConfig interface in Table 14.2.

Table 14.2. Methods of the javax.servlet.FilterConfig Interface

Method

Does This

java.lang.String getFilterName()

Returns the name of the filter.

java.lang.String getInitParameter (java.lang.String name)

Returns a String object containing the value of the specified initialization parameter.

java.util.Enumeration getInitParameterNames()

Returns the names of the servlet's initialization parameters as a Java Enumeration of String objects.

ServletContext getServletContext()

Returns a reference to the current servlet context.

Here's an example showing how this works. To create an initialization parameter, you use the <init-param> element with the <param-name> and <param-value> subelements:

<init-param> 
    <param-name>
    <param-value>
</init-param>

The <init-param> element goes inside the <filter> element, and you can have multiple initialization parameters per filter:

<filter> 
    <filter-name>
    <filter-class>
    <init-param>
        <param-name>
        <param-value>
    </init-param>
    <init-param>
        <param-name>
        <param-value>
    </init-param>
    <init-param>
        <param-name>
        <param-value>
    </init-param>
</filter>

You're passed the FilterConfig object in the init method, and that object gives you access to initialization parameters. To get access to those parameters in doFilter, you can save this object in a private variable:

public class ch14_06 implements Filter 
{
    private FilterConfig filterConfig = null;
        .
        .
        .
    public void init(FilterConfig filterConfig)
    {
        this.filterConfig = filterConfig;
    }
}

In the doFilter method, you can use this object's getInitParameter method to get the value of an initialization parameter. For example, if an initialization parameter is named message, you can get and display its value, as you see in Listing 14.6.

Example 14.6. Using Filter Initialization Parameters (ch14_06.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_06 implements Filter
{
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {

        System.out.println("The message is: " + filterConfig.getInitParameter("message"));
        chain.doFilter(request, response);
    }

    public void destroy() { }

    public void init(FilterConfig filterConfig)
    {
        this.filterConfig = filterConfig;
    }
}

Here's how you can create the initialization parameter named message with the value Hello! in web.xml, and connect the filter to a new JSP page named ch14_07.jsp:

<?xml version="1.0" encoding="ISO-8859-1"?> 

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

  <filter>
    <filter-name>Simple Filter</filter-name>
    <filter-class>ch14_01</filter-class>
  </filter>

  <filter>
    <filter-name>Chained Filter</filter-name>
    <filter-class>ch14_04</filter-class>
  </filter>

  <filter>
    <filter-name>Init Parameters Filter</filter-name>
    <filter-class>ch14_06</filter-class>
    <init-param>
      <param-name>message</param-name>
      <param-value>Hello!</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_02</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_03.jsp</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Simple Filter</filter-name>
    <url-pattern>/ch14_05.jsp</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Chained Filter</filter-name>
    <url-pattern>/ch14_05.jsp</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>Init Parameters Filter</filter-name>
    <url-pattern>/ch14_07.jsp</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>ch14_02</servlet-name>
    <servlet-class>ch14_02</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ch14_02</servlet-name>
    <url-pattern>/ch14_02</url-pattern>
  </servlet-mapping>

</web-app>

You can see ch14_07.jsp, the target JSP page for this filter, in Listing 14.7. This JSP page just displays a message saying that the initialization parameter's value will be displayed on the server console.

Example 14.7. The Target JSP for the Initialization Parameters Filter (ch14_07.jsp)

<HTML>
    <HEAD>
        <TITLE>Filters And Initialization Parameters</TITLE>
    </HEAD>
    <BODY>
        <H1>Filters And Initialization Parameters</H1>
        The initialization parameter's value will be displayed on the server console.
        <BR>
    </BODY>
</HTML>

You can see the target JSP page (http://localhost:8080/ch14/ch14_07.jsp) in Figure 14.6.

The target JSP for the initialization parameters filter.

Figure 14.6. The target JSP for the initialization parameters filter.

You can see the message displayed on the server console in Figure 14.7, which shows the value of the initialization parameter.

The value of a retrieved initialization parameter.

Figure 14.7. The value of a retrieved initialization parameter.

The filters you've seen so far display text on the server console—but what about displaying text on a Web page? That's coming up next.

Inserting Text Into the Response

It turns out you can insert text from a filter into a Web resource's response in the same way that you can from a servlet—you use the response object's getWriter method to get a PrintWriter object. The code in Listing 14.8 displays the value of the same initialization value as the previous example—but this time, it inserts that value into the Web page returned by the JSP page it is filtering.

Example 14.8. Inserting Text Into the Response (ch14_08.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_08 implements Filter
{
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {

        chain.doFilter(request, response);
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("The filter got this message: " + filterConfig.getInitParameter
Inserting Text Into the Response (ch14_08.java)("message"));
    }

    public void destroy() { }

    public void init(FilterConfig filterConfig)
    {
        this.filterConfig = filterConfig;
    }
}

Tip

Something useful to know when you're starting to use filters to add text to a Web resource's response is that some Web resources and some servers will buffer their responses. This means that the Web resource's response can come back in pieces, which makes it hard to know where to insert your text. That's why Java includes the javax.servlet.http.HttpServletResponseWrapper class. This class enables you to handle the Web resource's response by writing it byte by byte to an array, which you can work with and send back to the browser yourself. Working with javax.servlet.http.HttpServletResponseWrapper is beyond the scope of this book, however—for more information, take a look at advanced Web develop ment books.

Listing 14.9 details the target JSP into which this filter will insert text.

Example 14.9. The Target JSP for the Response-Insertion Filter (ch14_09.jsp)

<HTML>
    <HEAD>
        <TITLE>Filters And Initialization Parameters</TITLE>
    </HEAD>
    <BODY>
        <H1>Filters And Initialization Parameters</H1>
        <BR>
    </BODY>
</HTML>

You can see the results in Figure 14.8, where the filter has inserted text into the response from the JSP page (http://localhost:8080/ch14/ch14_09.jsp).

Inserting text into the response.

Figure 14.8. Inserting text into the response.

Being able to insert text into the response is important for filters—now you can preface the response from a Web resource with your own text (for example, to include a disclaimer), or add to it at the end (for example, to add a copyright notice).

Logging

Another popular use for filters is to write access information in server logs, and to keep track of user accesses. In this case, the user doesn't see anything unusual, but his IP address and the resources he accessed are logged.

Here's an example that shows how to write to such a log. First, set up a log file for the examples in the ch14 directory on the server in Tomcat's server.xml file. You do this with a new <Context> element (it's inside the <Host> element in server.xml). If you want to follow along in this example, place this <Context> element after the other <Context> elements in server.xml:

<Context path="/ch14" docBase="ch14" debug="0" reloadable="true"> 
    <Logger className="org.apache.catalina.logger.FileLogger"
        directory="logs"  prefix="ch14." suffix=".txt"
        timestamp="true"/>
</Context>

This <Context> element will enable examples in the ch14 directory to write to a log file with the prefix ch14 and the suffix .txt in the jakarta-tomcat-4.0.3logs directory. The actual log filename will also include the date, like this: ch14.2002-05-28.txt.

To write to the log file, you can use the log method of the current context, and you can get the current context with the FilterConfig object's getServletContext method:

public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain)
    throws IOException, ServletException
{
    chain.doFilter(request, response);

    filterConfig.getServletContext().log(
        .
        .
        .
    );
}

For example, you can log the user's IP address using the request.getRemoteAddr method, and the URL of the Web resource she's accessing with the request.getRequestURI method. You can also determine how long it took to execute the Web resource by getting the time (in milliseconds) before calling doFilter, and then subtracting that value from the current time after doFilter finishes. You can see all this at work in Listing 14.10.

Example 14.10. Using a Filter to Write to a Log File (ch14_10.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_10 implements Filter
{
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {
        long start = System.currentTimeMillis();
        String address =  request.getRemoteAddr();
        String file = ((HttpServletRequest) request).getRequestURI();

        chain.doFilter(request, response);

        filterConfig.getServletContext().log(
            "User access! " +
            " User IP: " + address +
            " Resource: " + file +
            " Milliseconds used: " + (System.currentTimeMillis()- start)
        );
    }

    public void destroy() { }

    public void init(FilterConfig filterConfig)
    {
        this.filterConfig = filterConfig;
    }
}

The filter you see in Listing 14.10 will log accesses to the JSP page you see in Listing 14.11.

Example 14.11. Using Servlet Sessions (ch14_11.jsp)

<HTML>
    <HEAD>
        <TITLE>Filters And Logging</TITLE>
    </HEAD>
    <BODY>
        <H1>Filters And Logging</H1>
        This filter writes to a log file.
        <BR>
    </BODY>
</HTML>

You can see the target JSP page (http://localhost:8080/ch14/ch14_11.jsp) in Figure 14.9, where you see a message indicating that the current filter writes to a log file.

Logging user accesses with a filter.

Figure 14.9. Logging user accesses with a filter.

You can see the results in the log file, such as jakarta-tomcat-4.0.3logsch14.2002-05-28.txt (replacing the date in this log file's name with the current date, of course). You can find the entry in this log file caused by the user access you see in Figure 14.9 at the end of this file (you must stop Tomcat to read the log file):

2002-05-28 12:06:37 WebappLoader[/ch14]: Deploying class repositories to work directory D:
Logging user accesses with a filter.	omcatjakarta-tomcat-4.0.3worklocalhostch14
2002-05-28 12:06:37 WebappLoader[/ch14]: Reloading checks are enabled for this Context
2002-05-28 12:06:37 StandardManager[/ch14]: Seeding random number generator class java
Logging user accesses with a filter..security.SecureRandom
2002-05-28 12:06:37 StandardManager[/ch14]: Seeding of random number generator has been
Logging user accesses with a filter. completed
2002-05-28 12:06:37 StandardWrapper[/ch14:default]: Loading container servlet default
2002-05-28 12:06:37 default: init
2002-05-28 12:06:37 StandardWrapper[/ch14:invoker]: Loading container servlet invoker
2002-05-28 12:06:37 invoker: init
2002-05-28 12:06:37 jsp: init
2002-05-28 12:06:46 jsp: init
2002-05-28 12:06:46 User access! User IP: 127.0.0.1 Resource: /ch14/ch14_11.jsp
Logging user accesses with a filter. Milliseconds used: 100

The log file records the user's IP address (127.0.0.1 here), the resource accessed (/ch14/ch14_11.jsp), and the number of milliseconds used by the resource (100 here). Unless the JSP page included a message about logging, the user will never know their actions are being recorded (in fact, because you're using a filter, the Web resource has no way of determining that logging is in effect, either).

Using Filters for User Authentication

Because filters can serve as gateways to Web resources, you can use them to enforce user authentication with usernames and passwords (see the section “Supporting User Authentication” in Day 13, “Creating More Powerful Servlets”).

For example, say that you present the user with an HTML page that uses a password control named password. A filter can read that password directly from the request object and check it. Suppose that the required password is opensesame—if that is what the user has entered, you can pass the request on to the user resource with the doFilter method:

public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain)
    throws IOException, ServletException
{
    String password = ((HttpServletRequest) request).getParameter("password");

    if(password.equals("opensesame")) {
        chain.doFilter(request, response);
    } else {
        .
        .
        .
    }
}

On the other hand, if the password isn't right, you don't pass the user request on to the Web access at all—you simply return an error page, as you see in Listing 14.12.

Example 14.12. Supporting User Authentication (ch14_12.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_12 implements Filter
{
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {
        String password = ((HttpServletRequest) request).getParameter("password");

        if(password.equals("opensesame")) {
            chain.doFilter(request, response);
        } else {
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println("<HTML>");
            out.println("<HEAD>");
            out.println("<TITLE>");
            out.println("Incorrect Password");
            out.println("</TITLE>");
            out.println("</HEAD>");
            out.println("<BODY>");
            out.println("<H1>Incorrect Password</H1>");
            out.println("Sorry, that password was incorrect.");
            out.println("</BODY>");
            out.println("</HTML>");
        }
    }

    public void destroy() { }

    public void init(FilterConfig filterConfig)
    {
        this.filterConfig = filterConfig;
    }
}

You can see the HTML page (ch14_13.html) that asks the user for his password in Listing 14.13.

Example 14.13. User Authentication HTML Page (ch14_13.html)

<HTML>
    <HEAD>
        <TITLE>Using Passwords and Filters</TITLE>
    </HEAD>

    <BODY>
        <H1>Using Passwords and Filters</H1>
        <FORM ACTION="ch14_14.jsp" METHOD="POST">
            Please enter your password:
            <INPUT TYPE="PASSWORD" NAME="password">
            <BR>
            <INPUT TYPE="SUBMIT" VALUE="Submit">
        </FORM>
    </BODY>
<HTML>

Finally, you can see the Web resource that the user is trying to access (ch14_14.jsp) in Listing 14.14.

Example 14.14. The Target JSP Page for User Authentication (ch14_14.jsp)

<HTML>
    <HEAD>
        <TITLE>Filters And User Authentication</TITLE>
    </HEAD>
    <BODY>
        <H1>Filters And User Authentication</H1>
        Congratulations, you're in!
        <BR>
    </BODY>
</HTML>

When the user navigates to the HTML page that asks for a password (http://localhost:8080/ch14/ch14_13.html in this example), she'll see the HTML page that asks for her password, as you see in Figure 14.10.

Entering a password.

Figure 14.10. Entering a password.

If the password is correct, the filter passes the user request on to the Web resource, ch14_14.jsp, as you see in Figure 14.11.

Successful password access.

Figure 14.11. Successful password access.

On the other hand, if the password is not correct, the filter returns an error page and doesn't grant access to the Web resource, as you see in Figure 14.12.

Unsuccessful password access.

Figure 14.12. Unsuccessful password access.

Restricting Access

Using filters as gateways to Web resources is a popular thing to do. Here's another example that shows how it works—in this case, the filter will block employee access to a Web resource during working hours (for example, the Web resource might be a stock ticker, a personal Web page, a game, or something similar).

The example in Listing 14.15 is similar to the password example you just saw, except that it just checks the time of day (using the Date and Calendar classes you saw in Day 6, “Creating JSP Components: JavaBeans”) and blocks access to the requested Web resource between the hours of 9 a.m. to 5 p.m.

Example 14.15. A Filter That Restricts User Access (ch14_15.java)

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ch14_15 implements Filter
{
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain)
        throws IOException, ServletException
    {

        GregorianCalendar calendar = new GregorianCalendar();
        Date date1 = new Date();
        calendar.setTime(date1);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        if(hour < 9 || hour > 17) {
            chain.doFilter(request, response);
        } else {
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println("<HTML>");
            out.println("<HEAD>");
            out.println("<TITLE>");
            out.println("Get Back to Work!");
            out.println("</TITLE>");
            out.println("</HEAD>");
            out.println("<BODY>");
            out.println("<H1>Get Back to Work!</H1>");
            out.println("Sorry, that resource is not available now.");
            out.println("</BODY>");
            out.println("</HTML>");
        }
    }

    public void destroy()
    {
    }

    public void init(FilterConfig filterConfig)
    {
    }
}

You can attach the filter in Listing 14.15 to the JSP page you see in Listing 14.16, ch14_06.jsp.

Example 14.16. A Restricted Web Resource (ch14_16.jsp)

<HTML>
    <HEAD>
        <TITLE>Using Filters to Restrict Access</TITLE>
    </HEAD>
    <BODY>
        <H1>Using Filters to Restrict Access</H1>
        Congratulations, you're in!
        <BR>
    </BODY>
</HTML>

You can see the results in Figure 14.13 when the user tries to access the restricted Web resource (http://localhost:8080/ch14/ch14_16.jsp) during working hours.

Restricting user access.

Figure 14.13. Restricting user access.

Summary

Today, you learned how to work with filters—how to work with the various filter classes, and how to build and install a filter. You work with elements in web.xml to attach a filter to a Web resource, and you can attach a filter to such resources as servlets and JSP pages.

It's easy for a filter to gain access to a Web resource's request and response objects both before and after the Web resource is accessed. A filter can display text on the server console with the System.out.println method, as well as insert text into the response from the filtered Web resource.

You can also chain filters together by attaching multiple filters to the same Web resource. The filters are called in the order you specify in web.xml before calling the Web resource, and then in reverse order after the Web resource executes.

A filter has access to initialization parameters, just as a servlet does, and you can use Tomcat log files to log user actions using filters.

You can use a filter to grant or deny access to a Web resource by implementing password-protected user authentication using filters, as well as denying access to a Web resource during certain hours of the day.

Tomorrow you'll take a look at another important aspect of JSP programming—working with files on the server. It's an important topic, because being able to work with files is one of the main reasons that programmers switch from client-side programming to server-side programming (as when you want to create a guest book, for example).

Q&A

Q1:

Are filters restricted to the Tomcat server?

A1:

No, filters are part of the Servlet 2.3 specification, so they work across servers. Actually, Tomcat did include a private mechanism similar to filters before filters came out, but now that functionality has been taken over by the use of filters.

Q2:

Are there more uses for filters than were covered today?

A2:

Yes, there certainly are—for example, another popular use for filters is to provide for the automatic encrypting and decrypting of sensitive data. If you only want to send encrypted data over the Internet, filters can automatically encrypt and decrypt that data for you. Another use is to compress data—using a browser's HTTP headers, you can determine if the browser can support compressed data, and if so, compress the data you're sending to it, saving a great deal of transmission time. You can also use filters to translate between data formats, such as from XML to HTML and back again.

Q3:

Can I also filter static resources like Web pages?

A3:

Yes, just connect the filter to the appropriate relative URL (for an HTML page, just use the relative URL of the HTML page).

Workshop

This workshop tests whether you understand all the concepts you learned today. It's a good idea to master today's concepts by honing your knowledge here before starting tomorrow's material. You can find the answers to the quiz questions in Appendix A.

Quiz

1:

What three methods do you need to override to implement the Filter interface?

2:

What element do you use in web.xml to create a filter? To attach it to a Web resource?

3:

Name the two subelements you need to use to connect a filter to a Web resource.

4:

If you chain Filters A, B, and C (in that order) to Web resource D, what is the order in which control passes through these items when the user accesses the Web resource?

5:

How do you gain access to an initialization parameter in a filter?

Exercises

1:

Create an HTML page that passes the text in a text field to a JSP page, which displays that text. Next, create and use a filter that uses the String method toUpper to convert the text to uppercase before it's displayed in the JSP page.

2:

Create a filter that checks the browser the user has (using the request.getHeader("User-Agent") method you saw in Day 4, “Reading Data from Web Pages: Buttons and Text Fields”), and have this filter return an error page unless the user is using your favorite browser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.233.43