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 * レベル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 = "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 public void destroy() { 212 // ここでは処理を行いません。 213 } 214 215 /** 216 * アクセス拒否を示すメッセージ内容。 217 * 218 * @og.rev 6.3.8.3 (2015/10/03) アクセス拒否を示すメッセージファイルの内容を取り出します。 219 * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 220 * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。 221 * 222 * @return アクセス拒否を示すメッセージファイルの内容 223 */ 224 private String refuseMsg() { 225 // アクセス拒否を示すメッセージファイルの内容を管理する FileString オブジェクトを構築する。 226 227 // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 228 return FileUtil.getValue( filename , HybsConst.UTF_8 ); // 6.4.5.2 (2016/05/06) 229 } 230 231 /** 232 * フィルターの内部状態をチェックするメソッドです。 233 * 234 * 判定条件は、URL_ACCESS_SECURITY_LEVEL 変数 に応じて異なります。 235 * レベル0:なにも制限はありません。 236 * レベル1:Referer チェックを行います。つまり、URLを直接入力しても動作しません。 237 * レベル2:URLのハッシュ化/暗号化処理を、外部URLに対してのみ行います。(チェックは、レベル1と同等) 238 * レベル3:URLのパラメータがハッシュ化/暗号化されている必要があります。 239 * それ以外:アクセスを停止します。 240 * 241 * @param request HttpServletRequestオブジェクト 242 * 243 * @return (true:許可 false:拒否) 244 */ 245 private boolean isValidAccess( final HttpServletRequest request ) { 246 if( ACCS_LVL == 0 ) { return true; } // レベル0:無条件アクセス 247 248 final String httpReferer = request.getHeader( "Referer" ); 249 final String requestURI = request.getRequestURI(); 250 final String queryString = request.getQueryString(); 251 final String hashVal = request.getParameter( REQ_KEY ); 252 253 if( isDebug ) { 254 System.out.println( "URLHashFilter#httpReferer = " + httpReferer ); 255 System.out.println( "URLHashFilter#requestURI = " + requestURI ); 256 } 257 258 // 基準となる許可:パラメータなし or ハッシュありの場合 259 final boolean flag2 = queryString == null || hashVal != null ; 260 261 // レベル1,2:パラメータなし or ハッシュあり or Refererあり の場合、許可 262 if( ACCS_LVL == 1 || ACCS_LVL == 2 ) { 263 return flag2 || httpReferer != null ; 264 } 265 266 // レベル3:パラメータなし or ハッシュありの場合、許可 267 if( ACCS_LVL == 3 ) { 268 final String cntxPath = request.getContextPath(); // /gf など 269 // 特別処置 270 return flag2 || 271 requestURI.equalsIgnoreCase( initPage ) || 272 requestURI.startsWith( cntxPath + "/jsp/menu/" ) || 273 requestURI.startsWith( cntxPath + "/jsp/custom/" ) || 274 requestURI.startsWith( cntxPath + "/jsp/common/" ) ; 275 } 276 277 return false; // それ以外:無条件拒否 278 } 279 280 /** 281 * 内部状態を文字列で返します。 282 * 283 * @return このクラスの文字列表示 284 * @og.rtnNotNull 285 */ 286 @Override 287 public String toString() { 288 final StringBuilder sb = new StringBuilder( BUFFER_MIDDLE ) 289 .append( this.getClass().getCanonicalName() ).append( " : ") 290 .append( "initPage = [" ).append( initPage ).append( "] , ") 291 .append( "isDebug = [" ).append( isDebug ).append( "]"); 292 return sb.toString(); 293 } 294}