Tomcatでフィルタを使う

[サーバの実験室 Slackware]

作成 : 2005/01/01

"サーバの実験室"の検索


Tomcatのフィルタ

フィルタを使うと、JSPやサーブレットを処理する事前と事後に、いろいろな処理をすることができるらしい。 決められたIPアドレス以外からのアクセスに対しては、アクセス拒否メッセージのページを表示したり...

フィルタクラス

フィルタを使用するためのクラスを作ってみる。 下の例で、赤い文字が事前・事後の処理を書く部分。 それ以外は、だいたい共通的に使用できるらしい。

/* 標準出力(Tomcatのログ)に文字列を書き出すフィルタ myFilter */

package filters;    // (コンテキストパス)/WEB-INF/classes/filtersディレクトリに置くという前提で

import javax.servlet.*;
import javax.servlet.http.*;

public class myFilter implements Filter {

    private FilterConfig filterConfig;

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;    // フィルタの初期化
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     throws java.io.IOException, javax.servlet.ServletException {
        System.out.println("myFilter:Before");    // クライアントからのリクエスト時に処理

        chain.doFilter(request, response);    // 次のフィルタに処理を移す(複数フィルタ適用時)

        System.out.println("myFilter:After");    // クライアントへのレスポンス時に処理
    }

    public void destroy() {
        this.filterConfig = null;    // フィルタの破棄
    }

}

あとはコンパイルして、(コンテキストパス)/WEB-INF/classes/filtersディレクトリに置く。

フィルタの定義とマッピング(web.xml)

フィルタを使用するためには、web.xmlでフィルタを定義し、URLまたはサーブレットとのマッピングを指定する。 フィルタを定義するときに、初期化パラメータを指定することができる。

<!-- (コンテキストパス)/WEB-INF/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/dtd/web-app_2_3.dtd">
<web-app>
  <filter>    // <filter>〜</filter>でフィルタを定義
    <filter-name>myFilter1</filter-name>    // フィルタの名前
    <filter-class>filters.myFilter</filter-class>    // フィルタのクラス名
    <init-param>    // <init-param>〜</init-param>で初期化パラメータを指定
      <param-name>param</param-name>    // パラメータの名前
      <param-value>1</param-value>    // パラメータの値
    </init-param>
  </filter>

  <filter>
    <filter-name>myFilter2</filter-name>
    <filter-class>filters.myFilter</filter-class>
    <init-param>
      <param-name>param</param-name>
      <param-value>2</param-value>
    </init-param>
  </filter>

  <filter>
    <filter-name>myFilter3</filter-name>
    <filter-class>filters.myFilter</filter-class>
    <init-param>
      <param-name>param</param-name>
      <param-value>3</param-value>
    </init-param>
    <init-param>    // 初期化パラメータは複数渡すことができる
      <param-name>other</param-name>
      <param-value>memo</param-value>
    </init-param>
  </filter>



  <filter-mapping>    // <filter-mapping>〜</filter-mapping>でマッピングを定義
    <filter-name>myFilter1</filter-name>    // フィルタの名前
    <url-pattern>/*</url-pattern>    // URLとのマッピング(先頭にスラッシュがつく)
  </filter-mapping>

  <filter-mapping>
    <filter-name>myFilter2</filter-name>
    <servlet-name>Servlet</servlet-name>    // サーブレットとのマッピング(先頭にスラッシュがつかない)
  </filter-mapping>

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



  <servlet>
    <servlet-name>Servlet</servlet-name>
    <servlet-class>HelloWorld</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Servlet</servlet-name>
    <url-pattern>/HelloWorld</url-pattern>
  </servlet-mapping>
</web-app>

フィルタが適用される順番

フィルタが適用される順番は、URLとのマッピング(url-mapping)のほうがサーブレットとのマッピング(servlet-name)より優先される。 あとはfilter-mappingで定義した順番に適用される。

上のweb.xmlと、初期化パラメータparamの値をログに書き出すフィルタmyFilterクラスを使って、フィルタが適用される順番を確認してみる。

/* 初期化パラメータの値をログに書き出す myFilter */

package filters;

import javax.servlet.*;
import javax.servlet.http.*;

public class myFilter implements Filter {

    private FilterConfig filterConfig;

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

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     throws java.io.IOException, javax.servlet.ServletException {
        System.out.println("BEFORE:" + filterConfig.getInitParameter("param"));    // 赤の部分で初期化パラメータparamの値を取得している

        chain.doFilter(request, response);

        System.out.println("AFTER:" + filterConfig.getInitParameter("param"));    // 赤の部分で初期化パラメータparamの値を取得している
    }

    public void destroy() {
        this.filterConfig = null;
    }
}

HelloWorldサーブレットを実行した後、ログ($CATALINA_HOME/logs/catalina.out)を確認してみると、以下のようになっているはず。 filter-mappingで定義された順番は「1 -> 2 -> 3」だが、適用される順番は「1 -> 3 -> 2」となる。 また、この順番はリクエストが渡される順番であり、レスポンスが渡される順番は「2 -> 3 -> 1」となる。

BEFORE:1    <--- myFilter1の出力(url-mapping)
BEFORE:3    <--- myFilter3の出力(url-mapping)
BEFORE:2    <--- myFilter2の出力(servlet-name)
AFTER:2    <--- myFilter2の出力(servlet-name)
AFTER:3    <--- myFilter3の出力(url-mapping)
AFTER:1    <--- myFilter1の出力(url-mapping)

初期化パラメータを取得する

上の例でもちらっと出てきたが、単一の初期化パラメータを取得するときは、FilterConfigオブジェクトのgetInitParameterメソッドを使う。 複数の初期化パラメータを取得するときは、getInitParameterNamesメソッドを使用する。

/* 初期化パラメータの値をログに書き出す myFilter */

package filters;

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

public class myFilter implements Filter {

    private FilterConfig filterConfig;

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

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     throws java.io.IOException, javax.servlet.ServletException {
        Enumeration iniParas = filterConfig.getInitParameterNames();    // 赤の部分で初期化パラメータの値を取得している
        if(iniParas != null) {
            while(iniParas.hasMoreElements()) {
                String paraName = (String) iniParas.nextElement();
                System.out.println("InitParameterNames(" + paraName + "):" + filterConfig.getInitParameter(paraName));
            }
        }

        chain.doFilter(request, response);
    }

    public void destroy() {
        this.filterConfig = null;
    }
}

getInitParameterNamesメソッドの戻り値はEnumerationオブジェクトになるので、java.utilクラスをインポートすること。

ホストベースでアクセス制御する

ServletRequestオブジェクトのgetRemoteAddrメソッドでクライアントのIPアドレスを取得する。 決められたIPアドレス以外からのアクセスの場合は、RequestDispatcherオブジェクトのforwardメソッドで別のページに転送する。

/* 10.1.1.X以外からのアクセスをforward.htmlに転送する myFilter */

package filters;

import javax.servlet.*;
import javax.servlet.http.*;

public class myFilter implements Filter {

    private FilterConfig filterConfig;

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

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     throws java.io.IOException, javax.servlet.ServletException {
         String remoteAddress = request.getRemoteAddr();    // getRemoteAddrメソッドでクライアントのIPアドレスを取得

         if (remoteAddress.startsWith("10.1.1.")) {    // クライアントのIPアドレスが"10.1.1."で始まるならば
            chain.doFilter(request, response);    // 次のフィルタへ処理を移す
        } else {
            RequestDispatcher rd = request.getRequestDispatcher("/forward.html");    // 転送するリソースを指定
            rd.forward(request, response);    // 転送する
        }
    }

    public void destroy() {
        this.filterConfig = null;
    }
}

[サーバの実験室 Slackware]