Working with requests

In filters, you can work directly with the request object's headers or the ServletContext. You can also override request methods by wrapping the request in objects that extend the ServletRequestWrapper or HttpServletRequestWrapper. For more information on using wrapper classes, see "Using wrappers".

This section describes some common ways of working with the requests.

Working with request headers

Request objects contain header information that filters can use in a variety of ways. These headers are generated by the client browser. By examining request headers, filters can do these tasks:

Filters commonly use the following request headers:

Logging header information

A common task for filters is to print the HTTP request and response headers to a log file or standard output. This is useful for debugging and analyzing requests and responses.

The following filter code sample shows several techniques:

package jrunsamples.filters;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class HeaderFilter extends GenericFilter {
�public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
  throws java.io.IOException, javax.servlet.ServletException {
��ServletContext context = filterConfig.getServletContext();
��chain.doFilter(req, resp);
��HttpServletRequest request = (HttpServletRequest)req;
��context.log("******REQUEST HEADERS******");
��context.log("Request: " + request.getRequestURI());
��Enumeration headers = request.getHeaderNames();
��if (headers == null) {
��} else {
���while (headers.hasMoreElements()) {
����String name = (String) headers.nextElement();
����String value = request.getHeader(name);
����context.log(name + "=" + value);
���}
��}
��HttpServletResponse response = (HttpServletResponse)resp;
��context.log("******RESPONSE INFO******");
��String charencode = response.getCharacterEncoding();
��int bufsize = response.getBufferSize();
��context.log("Char encoding: " + charencode);
��context.log("Buffer size: " + bufsize);
�}
}

In the web application's web.xml deployment descriptor, define the filter and add its mapping; for example:

<filter> 
 <filter-name>HeaderFilter</filter-name>
 <filter-class>jrunsamples.filters.HeaderFilter</filter-class> 
</filter>
<filter-mapping> 
 <filter-name>HeaderFilter</filter-name>
 <url-pattern>/*</url-pattern> 
</filter-mapping> 

When you request any resource in the web application, this filter produces output similar to the following example:

11/21 08:04:34 info JSPServlet: init
11/21 08:04:34 info ******REQUEST HEADERS******
11/21 08:04:34 info Request: /welcome.jsp
11/21 08:04:34 info Accept=image/gif, image/x-xbitmap, image/jpeg, 
    image/pjpeg,application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
11/21 08:04:34 info Cookie=JSESSIONID=1887861006284883609
11/21 08:04:34 info Connection=Keep-Alive
11/21 08:04:34 info Host=localhost:8100
11/21 08:04:34 info Accept-Encoding=gzip, deflate
11/21 08:04:34 info User-Agent=Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
11/21 08:04:34 info Accept-Language=en-us
11/21 08:04:34 info ******RESPONSE INFO******
11/21 08:04:34 info Char encoding: ISO-8859-1
11/21 08:04:34 info Buffer size: 8192

Redirecting requests based on request parameters

Filters can access request parameters. Request parameters usually set by the requesting page in a <FORM> block or they are derived from values in a Session object. They can be part of the request query string (as in a GET request) or part of the request body (as in a POST request).

Filters usually pass the request to the next filter in the chain without challenge. However, this behavior is not required. While working with a request, a filter can forward the request to another resource or generate its own response. This is useful to block access with security checks, redirect users, or forward a request based on browser type.

By using the RequestDispatcher and forwarding a request, you break the filter chain. After a call to forward in a servlet or in a filter's doFilter method, any filters before or after that request in the chain are not processed. This includes filters that already preprocessed a request.

The following code sample redirects a request if the request parameter "username" does not equal "nick":

...
public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) 
  throws IOException, ServletException {
�if (request.getParameter("username").equals("nick")) {
��...//normal filter processing
�} else {
��forwardToErrorPage(request, response);
�}
�chain.doFilter(request, response);
}
public void forwardToErrorPage (ServletRequest req, ServletResponse res) {
�System.out.println("Forwarded to Error page");
�try {
��RequestDispatcher rd = req.getRequestDispatcher("/Error.jsp");
��rd.forward(req, res);
�} catch (Exception e) {
��System.out.println(e.toString());
�}
}
...

Redirecting requests based on User-Agents

Another example of redirecting a request is based on the type of requesting browser. You can check the User-Agent header of the Request object to detect the type of browser and then redirect noncompatible browsers. You must cast the ServletRequest object as an HttpServletRequest to directly access the headers; for example:

...
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
  throws IOException, ServletException {
�HttpServletRequest request = (HttpServletRequest)req;
�String agent = (String) request.getHeader("User-Agent");
�System.out.println("agent=" + agent);
�if (agent.startsWith("Mozilla") || agent.startsWith("Microsoft")) {
��chain.doFilter(req, resp);
�} else {
��try {
���RequestDispatcher rd = request.getRequestDispatcher("/Non-Com/");
���rd.forward(req, resp);
��} catch (Exception e) {
���System.out.println(e.toString());
��}
�}
�chain.doFilter(req,resp);
}
...

Using filters for simple authentication

Authentication is the process of gathering user credentials (user name and password) and validating them in the system. This usually requires checking the credentials against some user repository, such as a database or LDAP server, and authenticating the user's identity. By examining the request headers, you can use a filter to provide your web application resources with simple host-based authentication.

The following filter example shows host-based authentication by examining the IP address of the incoming request and allowing requests only from the localhost (IP address beginning with 127) to continue down the chain. The filter uses the RequestDispatcher to forward requests from nonauthenticated IP addresses to an error page.

The request headers can be spoofed, so this is not a robust authentication scheme, but it can be useful as part of an overall security strategy.

package jrunsamples.filters;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HostAuthFilter extends GenericFilter {
�public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
  throws java.io.IOException, javax.servlet.ServletException {
��String remoteaddr = request.getRemoteAddr();
��System.out.println("remoteaddr = " + remoteaddr);
��if (remoteaddr.startsWith("127.")) {
���System.out.println("Requesting host authenticated");
���chain.doFilter(request, response);
��} else {
���RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
���System.out.println("Requesting host NOT authenticated");
���rd.forward(request, response);
��}
�}
}

In the web application's web.xml deployment descriptor, define the filter and add its mapping; for example:

<filter> 
 <filter-name>HostAuthFilter</filter-name>
 <filter-class>jrunsamples.filters.HostAuthFilter</filter-class> 
</filter>
<filter-mapping> 
 <filter-name>HostAuthFilter</filter-name>
 <url-pattern>/startpage.html</url-pattern> 
</filter-mapping>

When you request the startpage.html in the web application from the localhost, this filter produces output similar to the following message:

remoteaddr = 127.0.0.1
Requesting host authenticated

If you request any resource from a different host that does not match the authentication criteria (in this case, any foreign host), this filter forwards the request to Error.jsp and produces output similar to the following message:

remoteaddr = 10.1.116.192
Requesting host NOT authenticated

Using filters for simple authorization

Authorization is the process of ensuring that the user can access a given resource (by using access control lists, for example). Before using authorization, a servlet engine authenticates the user. If a user is not authorized to view a resource, the servlet container does not allow access.

With access to request headers, filters can check to see if a user has been authorized before allowing the request to continue to the target resource. You can use filters in combination with other methods of web container security to create a simple security system.

The following procedure shows how to set up a filter that checks the Authorization header. If the header is set, then the user has logged in and can now proceed with the request. If the user has not successfully logged in (the Authorization request header is not set), the filter redirects the request to the login page.

The request headers can be spoofed, so this is not a robust authorization scheme, but it can be useful as part of an overall security strategy.

To set up a filter with a simple authorization check:

  1. Compile the following filter:
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;
    public class AuthFilter extends GenericFilter {
    �public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
     throws java.io.IOException, javax.servlet.ServletException {
    ��HttpServletRequest req = (HttpServletRequest)request;
    ��String authorization = req.getHeader("Authorization");
    ��if (authorization != null) {
    ���System.out.println("Authorized access: " + req.getRemoteUser());
    ���chain.doFilter(request,response);
    ��} else {
    ���System.out.println("Unauthorized access: " + req.getRemoteUser());
    ���RequestDispatcher rd = request.getRequestDispatcher("/login.html");
    ���rd.forward(request, response);
    ��}
    �}
    }
    
  2. Add the filter and mapping to the web.xml file; for example:
    <filter> 
     <filter-name>AuthFilter</filter-name>
     <filter-class>jrunsamples.filters.AuthFilter</filter-class> 
    </filter>
    ...
    <filter-mapping> 
     <filter-name>AuthFilter</filter-name>
     <url-pattern>protected_resource_mapping</url-pattern> 
    </filter-mapping>
    
  3. Add a new user and assign that user a role in the jrun_root/server_name/SERVER-INF/jrun-users.xml file; for example:
    <user>
    �<user-name>nick</user-name>
    �<password>danger</password>
    </user>
    <role>
    �<role-name>manager</role-name>
    �<user-name>nick</user-name>
    </role>
    
  4. In the web.xml file, add security constraints on target resources. This forces users to log in when they request that page. The simplest example uses BASIC authentication, which causes the browser to prompt clients for a user name and password. The client's input, if authenticated, sets the Authorization header of future requests.
  5. Restart the JRun server.
  6. Add the security settings in the web.xml file; for example:
    <login-config>
      <auth-method>BASIC</auth-method>
     </login-config>
     <security-constraint>
      <web-resource-collection>
       <web-resource-name>Protected Page</web-resource-name>
       <url-pattern>/login.html</url-pattern>
       <http-method>GET</http-method>
       <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
       <role-name>manager</role-name>
      </auth-constraint>
     </security-constraint>
    

Modifying the request

Filters have the ability to examine request data and change it on the fly. This is useful for validating form input or making internal settings that the requesting client cannot access.

There are many ways to modify the Request object in the filter or change a user's state before the request reaches the target resource. These ways are implemented by the following:

The following sections show examples of some of these methods.

Setting request attributes to modify the request

You can use the request.setAttribute method to add or change attributes of a request before the filter passes the request on to the target resource.

You can also set other objects as an attribute, not just String objects. The following code snippet from a filter sets two attributes of a request, a String object and an Integer object.

...
String sVeggies = "broccoli and spinach";
Integer x = new Integer(6);
//must called before chain.doFilter()
request.setAttribute("vegetables",sVeggies);
request.setAttribute("some_number",x);
chain.doFilter(request, response);
...

The following expression in a JSP gets the values of those attributes. Since the getAttribute returns generic objects, you must cast the result to the object type that you want (unlike the request.getParameter method which returns only Strings).

In this case, the value of the vegetables attribute is cast to a String and x to an Integer.

<%= (String)request.getAttribute("vegetables") %>
<%
 Integer x = (Integer)request.getAttribute("some_number");
 out.println(x.intValue() * 30);
%>

Using Session objects to pass variables across multiple requests is the preferred method. Using setAttribute on the request object is not generally used unless the request is being handled by a controller servlet or RequestDispatcher.

Working with sessions

Session objects have much greater scope than request objects. Request attributes can only be used by the currently requested resource, while session attributes persist across multiple requests. You can use filters to create sessions, set values in the session object, and make these values accessible from all pages that the client requests.

When you create and populate session objects in a filter rather than in target resources, you benefit from keeping the work of web components separate. This lets you reuse code and it is often used in design pattern strategies, such as the Front Controller.

The following filter creates a session object (if one did not already exist), stores the value of a request parameter as name, sets the name as a session attribute, and then finishes processing the request by passing the request down the chain to the targeted resource.

public class SessionFilter extends GenericFilter {
�public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
 throws java.io.IOException, javax.servlet.ServletException {
��//Don't forget to cast ServletRequest to HttpServletRequest so you can access the session object
��HttpServletRequest req = (HttpServletRequest)request;
��//Create new session
��HttpSession session = req.getSession(true);
��String name = request.getParameter("name");
��session.setAttribute("name", name);
��//pass control on to target resource
��chain.doFilter(req, response);
�}
}

Setting character encoding

In internationalized web applications that support multiple character sets and locales, you might have to set the character encoding of a request. However, request parameters are often parsed in the container's default character encoding. To prevent parsing errors, you can set the character encoding of the request using a filter.

You must call the setCharacterEncoding method before passing the request on; for example:

...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
throws IOException, ServletException {
�request.setCharacterEncoding("ISO-8859-1");
�chain.doFilter(request, response);
}
...

For more information on working with locale-aware web applications, see Chapter 3, "Internationalization and Localization".

 

Send me an e-mail when comments are added to this page | Comment Report

Current page: http://livedocs.adobe.com/jrun/4/Programmers_Guide/filters5.htm