    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.File;                                                    // 5.7.3.2 (2014/02/28) Tomcat8 対応
    021import java.io.IOException;
    022import java.io.PrintWriter;
    023
    024import javax.servlet.Filter;
    025import javax.servlet.FilterChain;
    026import javax.servlet.FilterConfig;
    027import javax.servlet.ServletContext;
    028import javax.servlet.ServletException;
    029import javax.servlet.ServletRequest;
    030import javax.servlet.ServletResponse;
    031import javax.servlet.RequestDispatcher;
    032import javax.servlet.http.HttpServletResponse;
    033import javax.servlet.http.HttpServletRequest;
    034
    035import org.opengion.fukurou.security.URLHashMap;
    036import org.opengion.fukurou.util.StringUtil;
    037import org.opengion.fukurou.util.FileString;
    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 FileString      refuseMsg       = null;                 // アクセス拒否時メッセージファイルの内容(キャッシュ)
    114        private boolean         isDebug         = false;
    115
    116        /**
    117         * フィルター処理本体のメソッドです。
    118         *
    119         * @og.rev 5.3.0.0 (2010/12/01) 文字化け対策として、setCharacterEncoding を実行する。
    120         *
    121         * @param       request         ServletRequestオブジェクト
    122         * @param       response        ServletResponseオブジェクト
    123         * @param       chain           FilterChainオブジェクト
    124         * @throws IOException 入出力エラーが発生したとき
    125         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
    126         */
    127        public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException {
    128                HttpServletRequest req = (HttpServletRequest)request ;
    129                req.setCharacterEncoding( "UTF-8" );    // 5.3.0.0 (2010/12/01)
    130
    131                if( isValidAccess( req ) ) {
    132                        String h_r = req.getParameter( REQ_KEY );
    133                        // ハッシュ化キーが存在する。
    134                        if( h_r != null ) {
    135                                HttpServletResponse resp = ((HttpServletResponse)response);
    136                                String qu = URLHashMap.getValue( h_r );
    137                                // キーに対する実アドレスが存在する。
    138                                if( qu != null ) {
    139                                        String requestURI = req.getRequestURI();                // /gf/jsp/index.jsp など
    140                                        String cntxPath   = req.getContextPath();               // /gf など
    141                                        // 自分自身のコンテキストと同じなので、forward できる。
    142                                        if( requestURI.startsWith( cntxPath ) ) {
    143                                                String url = requestURI.substring(cntxPath.length()) + "?" + qu ;
    144                                                RequestDispatcher rd = request.getRequestDispatcher( url );
    145                                                rd.forward( request,response );
    146                                        }
    147                                        // そうでない場合、リダイレクトする。
    148                                        else {
    149                                                String url = resp.encodeRedirectURL( requestURI + "?" + qu );
    150                                                resp.sendRedirect( url );
    151                                        }
    152                                }
    153                                // キーに対する実アドレスが存在しない。(行き先無しのケース)
    154                                else {
    155                                        String url = resp.encodeRedirectURL( initPage );
    156                                        resp.sendRedirect( url );
    157                                }
    158                        }
    159                        // ハッシュ化キーが存在しない。
    160                        else {
    161                                chain.doFilter(request, response);
    162                        }
    163                }
    164                else {
    165                        // アクセス拒否を示すメッセージファイルの内容を出力する。
    166                        response.setContentType( "text/html; charset=UTF-8" );
    167                        PrintWriter out = response.getWriter();
    168                        out.println( refuseMsg.getValue() );
    169                        out.flush();
    170                }
    171        }
    172
    173        /**
    174         * フィルターの初期処理メソッドです。
    175         *
    176         * フィルターに対してweb.xml で初期パラメータを設定します。
    177         *   ・filename   :停止時メッセージ表示ファイル名
    178         *   ・initPage   :最初にアクセスされる初期画面アドレス(初期値:/jsp/index.jsp)
    179         *   ・debug      :デバッグメッセージの表示(初期値:false)
    180         *
    181         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
    182         *
    183         * @param config FilterConfigオブジェクト
    184         */
    185        public void init( final FilterConfig config ) {
    186                initPage = StringUtil.nval( config.getInitParameter("initPage"), initPage );
    187                isDebug  = StringUtil.nval( config.getInitParameter("debug")   , isDebug  );
    188
    189                ServletContext context = config.getServletContext();
    190                String realPath = context.getRealPath( "" ) + File.separator;           // 5.7.3.2 (2014/02/28) Tomcat8 対応
    191
    192                // アクセス拒否を示すメッセージファイルの内容を管理する FileString オブジェクトを構築する。
    193                String filename  = realPath + config.getInitParameter("filename");
    194                refuseMsg = new FileString();
    195                refuseMsg.setFilename( filename );
    196                refuseMsg.setEncode( "UTF-8" );
    197        }
    198
    199        /**
    200         * フィルターの終了処理メソッドです。
    201         *
    202         */
    203        public void destroy() {
    204                // ここでは処理を行いません。
    205        }
    206
    207        /**
    208         * フィルターの内部状態をチェックするメソッドです。
    209         *
    210         * 判定条件は、URL_ACCESS_SECURITY_LEVEL 変数 に応じて異なります。
    211         *     レベル０：なにも制限はありません。
    212         *     レベル１：Referer チェックを行います。つまり、URLを直接入力しても動作しません。
    213         *     レベル２：URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。(チェックは、レベル１と同等)
    214         *     レベル３：URLのパラメータがハッシュ化/暗号化されている必要があります。
    215         *     それ以外：アクセスを停止します。
    216         *
    217         * @param request HttpServletRequestオブジェクト
    218         *
    219         * @return      (true:許可  false:拒否)
    220         */
    221        private boolean isValidAccess( final HttpServletRequest request ) {
    222                if( ACCS_LVL == 0 )      { return true;  }      // レベル０：無条件アクセス
    223
    224                String httpReferer = request.getHeader( "Referer" );
    225                String requestURI  = request.getRequestURI();
    226                String queryString = request.getQueryString();
    227                String hashVal     = request.getParameter( REQ_KEY );
    228
    229                if( isDebug ) {
    230                        System.out.println( "URLHashFilter#httpReferer = " + httpReferer );
    231                        System.out.println( "URLHashFilter#requestURI  = " + requestURI  );
    232                }
    233
    234                // 基準となる許可：パラメータなし or ハッシュありの場合
    235                boolean flag2 = queryString == null || hashVal != null ;
    236
    237                // レベル１,２：パラメータなし or ハッシュあり or Refererあり の場合、許可
    238                if( ACCS_LVL == 1 || ACCS_LVL == 2 ) {
    239                        return flag2 || httpReferer != null ;
    240                }
    241
    242                // レベル３：パラメータなし or ハッシュありの場合、許可
    243                if( ACCS_LVL == 3 ) {
    244                        String cntxPath = request.getContextPath();             // /gf など
    245                        // 特別処置
    246                        return flag2 ||
    247                                          requestURI.equalsIgnoreCase( initPage )            ||
    248                                          requestURI.startsWith( cntxPath + "/jsp/menu/"   ) ||
    249                                          requestURI.startsWith( cntxPath + "/jsp/custom/" ) ||
    250                                          requestURI.startsWith( cntxPath + "/jsp/common/" ) ;
    251                }
    252
    253                return false;   // それ以外：無条件拒否
    254        }
    255
    256        /**
    257         * 内部状態を文字列で返します。
    258         *
    259         * @return      このクラスの文字列表示
    260         */
    261        @Override
    262        public String toString() {
    263                StringBuilder sb = new StringBuilder()
    264                        .append( this.getClass().getCanonicalName() ).append( " : ")
    265                        .append( "initPage = [" ).append( initPage ).append( "] , ")
    266                        .append( "isDebug  = [" ).append( isDebug  ).append( "]");
    267                return sb.toString();
    268        }
    269}