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