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 *   レベル0:なにも制限はありません。
054 *   レベル1:Referer チェックを行います。つまり、URLを直接入力しても動作しません。
055 *             ただし、Refererが付いてさえいれば、アクセス許可を与えます。
056 *             Referer 無しの場合でも、URLにパラメータが存在しない、または、
057 *             アドレスがハッシュ化/暗号化されている場合は、アクセスを許可します。
058 *             レベル1の場合、ハッシュ戻し/複合化処理は行います。あくまで、ハッシュ化
059 *             暗号化されていない場合でも、Refererさえあれば、許可するということです。
060 *             (パラメータなし or ハッシュあり or Refererあり の場合、許可)
061 *   レベル2:フィルター処理としては、レベル1と同じです。
062 *             異なるのは、URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。
063 *             (パラメータなし or ハッシュあり or Refererあり の場合、許可)
064 *   レベル3:URLのパラメータがハッシュ化/暗号化されている必要があります。
065 *             レベル1同様、URLにパラメータが存在しない場合は、アクセスを許可します。
066 *             レベル1と異なるのは、パラメータは必ずハッシュ化か、暗号化されている
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 *     <filter>
078 *         <filter-name>URLHashFilter</filter-name>
079 *         <filter-class>org.opengion.hayabusa.filter.URLHashFilter</filter-class>
080 *         <init-param>
081 *             <param-name>filename</param-name>
082 *             <param-value>/jsp/custom/refuseAccess.html</param-value>
083 *         </init-param>
084 *          <init-param>
085 *              <param-name>initPage</param-name>
086 *              <param-value>/jsp/index.jsp</param-value>
087 *          </init-param>
088 *          <init-param>
089 *              <param-name>debug</param-name>
090 *              <param-value>false</param-value>
091 *          </init-param>
092 *     </filter>
093 *
094 *     <filter-mapping>
095 *         <filter-name>URLHashFilter</filter-name>
096 *         <url-pattern>*.jsp</url-pattern>
097 *     </filter-mapping>
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         *     レベル0:なにも制限はありません。
214         *     レベル1:Referer チェックを行います。つまり、URLを直接入力しても動作しません。
215         *     レベル2:URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。(チェックは、レベル1と同等)
216         *     レベル3: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;  }      // レベル0:無条件アクセス
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                // レベル1,2:パラメータなし or ハッシュあり or Refererあり の場合、許可
240                if( ACCS_LVL == 1 || ACCS_LVL == 2 ) {
241                        return ( flag2 || httpReferer != null );
242                }
243
244                // レベル3:パラメータなし 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}