    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.hayabusa.filter;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019
    020import java.io.IOException;
    021import java.io.PrintWriter;
    022
    023import javax.servlet.Filter;
    024import javax.servlet.FilterChain;
    025import javax.servlet.FilterConfig;
    026import javax.servlet.ServletException;
    027import javax.servlet.ServletRequest;
    028import javax.servlet.ServletResponse;
    029import javax.servlet.RequestDispatcher;
    030import javax.servlet.http.HttpServletResponse;
    031import javax.servlet.http.HttpServletRequest;
    032
    033import org.opengion.fukurou.security.URLHashMap;
    034import org.opengion.fukurou.util.StringUtil;
    035import org.opengion.fukurou.util.FileUtil;                                                              // 6.4.5.2 (2016/05/06)
    036import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;              // 6.1.0.0 (2014/12/26) refactoring
    037import org.opengion.fukurou.system.HybsConst;                                                   // 6.4.5.2 (2016/05/06)
    038
    039/**
    040 * URLHashFilter は、Filter インターフェースを継承した URLチェッククラスです。
    041 * web.xml で filter 設定することにより、処理を開始します。
    042 * filter 処理は、設定レベルとURLの飛び先により処理方法が異なります。
    043 * このフィルターでは、ハッシュ化/暗号化ではなく、アドレスに戻す作業になります。
    044 * 内部URLの場合はハッシュ化、外部URLの場合は暗号化に適用されます。
    045 *
    046 * 基本的には、外部へのURLでエンジンシステムへ飛ばす場合は、暗号化になります。
    047 * 内部へのURLは、基本的に、パラメータのみ暗号化を行います。なお、直接画面IDを
    048 * 指定して飛ばす場合を、止めるかどうかは、設定レベルに依存します。
    049 *
    050 * フィルターの設定レベルは、システムリソースの URL_ACCESS_SECURITY_LEVEL 変数で
    051 * 設定します。
    052 * なお、各レベル共通で、戻し処理はレベルに関係なく実行されます。
    053 *   レベル０：なにも制限はありません。
    054 *   レベル１：Referer チェックを行います。つまり、URLを直接入力しても動作しません。
    055 *             ただし、Refererが付いてさえいれば、アクセス許可を与えます。
    056 *             Referer 無しの場合でも、URLにパラメータが存在しない、または、
    057 *             アドレスがハッシュ化/暗号化されている場合は、アクセスを許可します。
    058 *             レベル１の場合、ハッシュ戻し/複合化処理は行います。あくまで、ハッシュ化
    059 *             暗号化されていない場合でも、Refererさえあれば、許可するということです。
    060 *             (パラメータなし or ハッシュあり or Refererあり の場合、許可)
    061 *   レベル２：フィルター処理としては、レベル１と同じです。
    062 *             異なるのは、URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。
    063 *             (パラメータなし or ハッシュあり or Refererあり の場合、許可)
    064 *   レベル３：URLのパラメータがハッシュ化/暗号化されている必要があります。
    065 *             レベル１同様、URLにパラメータが存在しない場合は、アクセスを許可します。
    066 *             レベル１と異なるのは、パラメータは必ずハッシュ化か、暗号化されている
    067 *             必要があるということです。(内部/外部問わず)
    068 *             (パラメータなし or ハッシュあり の場合、許可)
    069 *   それ以外：アクセスを停止します。
    070 *
    071 * フィルターに対してweb.xml でパラメータを設定します。
    072 *   ・filename   :停止時メッセージ表示ファイル名(例:/jsp/custom/refuseAccess.html)
    073 *   ・initPage   :最初にアクセスされる初期画面アドレス(初期値:/jsp/index.jsp)
    074 *   ・debug      :デバッグメッセージの表示(初期値:false)
    075 *
    076 * 【WEB-INF/web.xml】
    077 *     &lt;filter&gt;
    078 *         &lt;filter-name&gt;URLHashFilter&lt;/filter-name&gt;
    079 *         &lt;filter-class&gt;org.opengion.hayabusa.filter.URLHashFilter&lt;/filter-class&gt;
    080 *         &lt;init-param&gt;
    081 *             &lt;param-name&gt;filename&lt;/param-name&gt;
    082 *             &lt;param-value&gt;/jsp/custom/refuseAccess.html&lt;/param-value&gt;
    083 *         &lt;/init-param&gt;
    084 *          &lt;init-param&gt;
    085 *              &lt;param-name&gt;initPage&lt;/param-name&gt;
    086 *              &lt;param-value&gt;/jsp/index.jsp&lt;/param-value&gt;
    087 *          &lt;/init-param&gt;
    088 *          &lt;init-param&gt;
    089 *              &lt;param-name&gt;debug&lt;/param-name&gt;
    090 *              &lt;param-value&gt;false&lt;/param-value&gt;
    091 *          &lt;/init-param&gt;
    092 *     &lt;/filter&gt;
    093 *
    094 *     &lt;filter-mapping&gt;
    095 *         &lt;filter-name&gt;URLHashFilter&lt;/filter-name&gt;
    096 *         &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
    097 *     &lt;/filter-mapping&gt;
    098 *
    099 * @og.group フィルター処理
    100 *
    101 * @og.rev 5.2.2.0 (2010/11/01) 新規追加
    102 *
    103 * @version  5.2.2.0 (2010/11/01)
    104 * @author   Kazuhiko Hasegawa
    105 * @since    JDK1.6,
    106 */
    107public final class URLHashFilter implements Filter {
    108        private static final String REQ_KEY = HybsSystem.URL_HASH_REQ_KEY ;
    109
    110        private static final int ACCS_LVL = HybsSystem.sysInt( "URL_ACCESS_SECURITY_LEVEL" );
    111
    112        private String          initPage        = "/jsp/index.jsp";
    113        private String          filename        = "jsp/custom/refuseAccess.html" ;                      // 6.3.8.3 (2015/10/03) アクセス拒否時メッセージ表示ファイル名
    114        private boolean         isDebug         ;
    115
    116        /**
    117         * デフォルトコンストラクター
    118         *
    119         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    120         */
    121        public URLHashFilter() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    122
    123        /**
    124         * フィルター処理本体のメソッドです。
    125         *
    126         * @og.rev 5.3.0.0 (2010/12/01) 文字化け対策として、setCharacterEncoding を実行する。
    127         * @og.rev 6.3.8.3 (2015/10/03) アクセス拒否を示すメッセージファイルの内容を取り出します。
    128         *
    129         * @param       request         ServletRequestオブジェクト
    130         * @param       response        ServletResponseオブジェクト
    131         * @param       chain           FilterChainオブジェクト
    132         * @throws IOException 入出力エラーが発生したとき
    133         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
    134         */
    135        public void doFilter( final ServletRequest request,
    136                                                        final ServletResponse response,
    137                                                        final FilterChain chain ) throws IOException, ServletException {
    138
    139                final HttpServletRequest req = (HttpServletRequest)request ;
    140                req.setCharacterEncoding( "UTF-8" );    // 5.3.0.0 (2010/12/01)
    141
    142                if( isValidAccess( req ) ) {
    143                        final String h_r = req.getParameter( REQ_KEY );
    144                        // ハッシュ化キーが存在する。
    145                        // 6.0.2.5 (2014/10/31) refactoring: findBugs:未チェック/未確認のキャスト対応。
    146                        if( h_r != null && response instanceof HttpServletResponse ) {
    147                                final HttpServletResponse resp = ((HttpServletResponse)response);
    148                                final String qu = URLHashMap.getValue( h_r );
    149                                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    150                                // キーに対する実アドレスが存在しない。(行き先無しのケース)
    151                                if( qu == null ) {
    152                                        final String url = resp.encodeRedirectURL( initPage );
    153                                        resp.sendRedirect( url );
    154                                }
    155                                // キーに対する実アドレスが存在する。
    156                                else {
    157                                        final String requestURI = req.getRequestURI();          // /gf/jsp/index.jsp など
    158                                        final String cntxPath   = req.getContextPath();         // /gf など
    159                                        // 自分自身のコンテキストと同じなので、forward できる。
    160                                        if( requestURI.startsWith( cntxPath ) ) {
    161                                                final String url = requestURI.substring(cntxPath.length()) + "?" + qu ;
    162                                                final RequestDispatcher rd = request.getRequestDispatcher( url );
    163                                                rd.forward( request,response );
    164                                        }
    165                                        // そうでない場合、リダイレクトする。
    166                                        else {
    167                                                final String url = resp.encodeRedirectURL( requestURI + "?" + qu );
    168                                                resp.sendRedirect( url );
    169                                        }
    170                                }
    171                        }
    172                        // ハッシュ化キーが存在しない。
    173                        else {
    174                                chain.doFilter(request, response);
    175                        }
    176                }
    177                else {
    178                        // アクセス拒否を示すメッセージファイルの内容を出力する。
    179                        response.setContentType( "text/html; charset=UTF-8" );
    180                        final PrintWriter out = response.getWriter();
    181                        out.println( refuseMsg() );                                                     // 6.3.8.3 (2015/10/03)
    182                        out.flush();
    183                }
    184        }
    185
    186        /**
    187         * フィルターの初期処理メソッドです。
    188         *
    189         * フィルターに対してweb.xml で初期パラメータを設定します。
    190         *   ・filename   :停止時メッセージ表示ファイル名
    191         *   ・initPage   :最初にアクセスされる初期画面アドレス(初期値:/jsp/index.jsp)
    192         *   ・debug      :デバッグメッセージの表示(初期値:false)
    193         *
    194         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
    195         * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、HybsSystem経由で、取得する。
    196         * @og.rev 6.3.8.3 (2015/10/03) filenameの初期値設定。
    197         *
    198         * @param config FilterConfigオブジェクト
    199         */
    200        public void init( final FilterConfig config ) {
    201                initPage = StringUtil.nval( config.getInitParameter("initPage"), initPage );
    202                isDebug  = StringUtil.nval( config.getInitParameter("debug")   , isDebug  );
    203
    204                filename = HybsSystem.getRealPath() + StringUtil.nval( config.getInitParameter("filename") , filename );        // 6.3.8.3 (2015/10/03)
    205
    206        }
    207
    208        /**
    209         * フィルターの終了処理メソッドです。
    210         *
    211         */
    212        public void destroy() {
    213                // ここでは処理を行いません。
    214        }
    215
    216        /**
    217         * アクセス拒否を示すメッセージ内容。
    218         *
    219         * @og.rev 6.3.8.3 (2015/10/03) アクセス拒否を示すメッセージファイルの内容を取り出します。
    220         * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
    221         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
    222         *
    223         * @return アクセス拒否を示すメッセージファイルの内容
    224         */
    225        private String refuseMsg() {
    226                // アクセス拒否を示すメッセージファイルの内容を管理する FileString オブジェクトを構築する。
    227
    228                // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
    229                return FileUtil.getValue( filename , HybsConst.UTF_8 );                 // 6.4.5.2 (2016/05/06)
    230        }
    231
    232        /**
    233         * フィルターの内部状態をチェックするメソッドです。
    234         *
    235         * 判定条件は、URL_ACCESS_SECURITY_LEVEL 変数 に応じて異なります。
    236         *     レベル０：なにも制限はありません。
    237         *     レベル１：Referer チェックを行います。つまり、URLを直接入力しても動作しません。
    238         *     レベル２：URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。(チェックは、レベル１と同等)
    239         *     レベル３：URLのパラメータがハッシュ化/暗号化されている必要があります。
    240         *     それ以外：アクセスを停止します。
    241         *
    242         * @param request HttpServletRequestオブジェクト
    243         *
    244         * @return      (true:許可  false:拒否)
    245         */
    246        private boolean isValidAccess( final HttpServletRequest request ) {
    247                if( ACCS_LVL == 0 )      { return true;  }      // レベル０：無条件アクセス
    248
    249                final String httpReferer = request.getHeader( "Referer" );
    250                final String requestURI  = request.getRequestURI();
    251                final String queryString = request.getQueryString();
    252                final String hashVal     = request.getParameter( REQ_KEY );
    253
    254                if( isDebug ) {
    255                        System.out.println( "URLHashFilter#httpReferer = " + httpReferer );
    256                        System.out.println( "URLHashFilter#requestURI  = " + requestURI  );
    257                }
    258
    259                // 基準となる許可：パラメータなし or ハッシュありの場合
    260                final boolean flag2 = queryString == null || hashVal != null ;
    261
    262                // レベル１,２：パラメータなし or ハッシュあり or Refererあり の場合、許可
    263                if( ACCS_LVL == 1 || ACCS_LVL == 2 ) {
    264                        return flag2 || httpReferer != null ;
    265                }
    266
    267                // レベル３：パラメータなし or ハッシュありの場合、許可
    268                if( ACCS_LVL == 3 ) {
    269                        final String cntxPath = request.getContextPath();               // /gf など
    270                        // 特別処置
    271                        return flag2 ||
    272                                          requestURI.equalsIgnoreCase( initPage )            ||
    273                                          requestURI.startsWith( cntxPath + "/jsp/menu/"   ) ||
    274                                          requestURI.startsWith( cntxPath + "/jsp/custom/" ) ||
    275                                          requestURI.startsWith( cntxPath + "/jsp/common/" ) ;
    276                }
    277
    278                return false;   // それ以外：無条件拒否
    279        }
    280
    281        /**
    282         * 内部状態を文字列で返します。
    283         *
    284         * @return      このクラスの文字列表示
    285         * @og.rtnNotNull
    286         */
    287        @Override
    288        public String toString() {
    289                final StringBuilder sb = new StringBuilder( BUFFER_MIDDLE )
    290                        .append( this.getClass().getCanonicalName() ).append( " : ")
    291                        .append( "initPage = [" ).append( initPage ).append( "] , ")
    292                        .append( "isDebug  = [" ).append( isDebug  ).append( "]");
    293                return sb.toString();
    294        }
    295}