    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 java.io.File;                                                    // 5.7.3.2 (2014/02/28) Tomcat8 対応
    019import java.io.BufferedReader;
    020import java.io.FileInputStream;
    021import java.io.IOException;
    022import java.io.InputStreamReader;
    023import java.io.PrintWriter;
    024import java.io.UnsupportedEncodingException;
    025
    026import javax.servlet.Filter;
    027import javax.servlet.FilterChain;
    028import javax.servlet.FilterConfig;
    029import javax.servlet.ServletContext;
    030import javax.servlet.ServletException;
    031import javax.servlet.ServletRequest;
    032import javax.servlet.ServletResponse;
    033import javax.servlet.http.HttpServletRequest;
    034
    035import org.opengion.fukurou.security.HybsCryptography;
    036import org.opengion.fukurou.util.Closer;
    037import org.opengion.fukurou.util.StringUtil;
    038import org.opengion.hayabusa.common.HybsSystem;
    039
    040/**
    041 * URLCheckFilter は、Filter インターフェースを継承した URLチェッククラスです。
    042 * web.xml で filter 設定することにより、該当のリソースに対して、og:linkタグで、
    043 * useURLCheck="true"が指定されたリンクURL以外を拒否することができます。
    044 * また、og:linkタグを経由した場合でも、リンクの有効期限を設定することで、
    045 * リンクURLの漏洩に対しても、一定時間の経過を持って、アクセスを拒否することができます。
    046 * また、リンク時にユーザー情報も埋め込んでいますので(初期値は、ログインユーザー)、
    047 * リンクアドレスが他のユーザーに知られた場合でも、アクセスを拒否することができます。
    048 *
    049 * フィルターに対してweb.xml でパラメータを設定します。
    050 *   ・filename :停止時メッセージ表示ファイル名
    051 *
    052 * 【WEB-INF/web.xml】
    053 *     &lt;filter&gt;
    054 *         &lt;filter-name&gt;URLCheckFilter&lt;/filter-name&gt;
    055 *         &lt;filter-class&gt;org.opengion.hayabusa.filter.URLCheckFilter&lt;/filter-class&gt;
    056 *         &lt;init-param&gt;
    057 *             &lt;param-name&gt;filename&lt;/param-name&gt;
    058 *             &lt;param-value&gt;jsp/custom/refuseAccess.html&lt;/param-value&gt;
    059 *         &lt;/init-param&gt;
    060 *     &lt;/filter&gt;
    061 *
    062 *     &lt;filter-mapping&gt;
    063 *         &lt;filter-name&gt;URLCheckFilter&lt;/filter-name&gt;
    064 *         &lt;url-pattern&gt;/jsp/*&lt;/url-pattern&gt;
    065 *     &lt;/filter-mapping&gt;
    066 *
    067 * @og.group フィルター処理
    068 *
    069 * @version  4.0
    070 * @author   Hiroki Nakamura
    071 * @since    JDK5.0,
    072 */
    073public final class URLCheckFilter implements Filter {
    074
    075        private static final HybsCryptography HYBS_CRYPTOGRAPHY = new HybsCryptography(); // 4.3.7.0 (2009/06/01)
    076
    077        private String  filename  = null;                       // アクセス拒否時メッセージ表示ファイル名
    078        private boolean  isDebug         = false;
    079        private boolean  isDecode        = true;                // 5.4.5.0(2012/02/28) URIDecodeするかどうか
    080
    081        /**
    082         * フィルター処理本体のメソッドです。
    083         *
    084         * @param       request         ServletRequestオブジェクト
    085         * @param       response        ServletResponseオブジェクト
    086         * @param       chain           FilterChainオブジェクト
    087         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
    088         */
    089        public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException {
    090
    091                if( !isValidAccess( request ) ) {
    092                        BufferedReader in = null ;
    093                        try {
    094                                response.setContentType( "text/html; charset=UTF-8" );
    095                                PrintWriter out = response.getWriter();
    096                                in = new BufferedReader( new InputStreamReader(
    097                                                                new FileInputStream( filename ) ,"UTF-8" ) );
    098                                String str ;
    099                                while( (str = in.readLine()) != null ) {
    100                                        out.println( str );
    101                                }
    102                                out.flush();
    103                        }
    104                        catch( UnsupportedEncodingException ex ) {
    105                                String errMsg = "指定されたエンコーディングがサポートされていません。[UTF-8]" ;
    106                                throw new RuntimeException( errMsg,ex );
    107                        }
    108                        catch( IOException ex ) {
    109                                String errMsg = "ストリームがオープン出来ませんでした。[" + filename + "]" ;
    110                                throw new RuntimeException( errMsg,ex );
    111                        }
    112                        finally {
    113                                Closer.ioClose( in );
    114                        }
    115                        return;
    116                }
    117
    118                chain.doFilter(request, response);
    119        }
    120
    121        /**
    122         * フィルターの初期処理メソッドです。
    123         *
    124         * フィルターに対してweb.xml で初期パラメータを設定します。
    125         *   ・maxInterval:リンクの有効期限
    126         *   ・filename   :停止時メッセージ表示ファイル名
    127         *   ・decode     :URLデコードを行ってチェックするか(初期true)
    128         *
    129         * @og.rev 5.4.5.0 (2102/02/28)
    130         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
    131         *
    132         * @param filterConfig FilterConfigオブジェクト
    133         */
    134        public void init(final FilterConfig filterConfig) {
    135                ServletContext context = filterConfig.getServletContext();
    136                String realPath = context.getRealPath( "" ) + File.separator;           // 5.7.3.2 (2014/02/28) Tomcat8 対応
    137
    138                filename  = realPath + filterConfig.getInitParameter("filename");
    139                isDebug = StringUtil.nval( filterConfig.getInitParameter("debug"), false );
    140                isDecode = StringUtil.nval( filterConfig.getInitParameter("decode"), true ); // 5.4.5.0(2012/02/28)
    141        }
    142
    143        /**
    144         * フィルターの終了処理メソッドです。
    145         *
    146         */
    147        public void destroy() {
    148                // ここでは処理を行いません。
    149        }
    150
    151        /**
    152         * フィルターの内部状態をチェックするメソッドです。
    153         *
    154         * @og.rev 5.4.5.0 (2012/02/28) Decode
    155         *
    156         * @param request ServletRequestオブジェクト
    157         *
    158         * @return      (true:許可  false:拒否)
    159         */
    160        private boolean isValidAccess( final ServletRequest request ) {
    161                String checkKey = request.getParameter( HybsSystem.URL_CHECK_KEY );
    162                if( checkKey == null || checkKey.length() == 0 ) {
    163                        if( isDebug ) {
    164                                System.out.println( "  check NG [ No Check Key ]" );
    165                        }
    166                        return false;
    167                }
    168
    169                boolean rtn = false;
    170                try {
    171                        checkKey = HYBS_CRYPTOGRAPHY.decrypt( checkKey ).replace( "&amp;", "&" );
    172
    173                        if( isDebug ) {
    174                                System.out.println( "checkKey=" + checkKey );
    175                        }
    176
    177                        String url = checkKey.substring( 0 , checkKey.lastIndexOf( ",time=") );
    178                        long time = Long.parseLong( checkKey.substring( checkKey.lastIndexOf( ",time=") + 6, checkKey.lastIndexOf( ",userid=" ) ) );
    179                        String userid = checkKey.substring( checkKey.lastIndexOf( ",userid=") + 8 );
    180                        // 4.3.8.0 (2009/08/01)
    181                        String[] userArr = StringUtil.csv2Array( userid );
    182
    183                        if( isDebug ) {
    184                                System.out.println( " [url]    =" + url );
    185                                System.out.println( " [vtime]  =" + time );
    186                                System.out.println( " [userid] =" + userid );
    187                        }
    188
    189                        String reqStr =  ((HttpServletRequest)request).getRequestURL().toString() + "?" + ((HttpServletRequest)request).getQueryString();
    190                        // 5.4.5.0 (2012/02/28) URLDecodeを行う
    191                        if(isDecode){
    192                                if( isDebug ) {
    193                                        System.out.println( "[BeforeURIDecode]="+reqStr );
    194                                }
    195                                reqStr = StringUtil.urlDecode( reqStr );
    196                        }
    197                        reqStr = reqStr.substring( 0, reqStr.lastIndexOf( HybsSystem.URL_CHECK_KEY ) -1 );
    198                        //      String reqStr =  ((HttpServletRequest)request).getRequestURL().toString();
    199                        String reqUser = ((HttpServletRequest)request).getRemoteUser();
    200
    201                        if( isDebug ) {
    202                                System.out.println( " [reqURL] =" + reqStr );
    203                                System.out.println( " [ctime]  =" + System.currentTimeMillis() );
    204                                System.out.println( " [reqUser]=" + reqUser );
    205                        }
    206
    207                        if( reqStr.endsWith( url )
    208                                        && System.currentTimeMillis() - time < 0
    209                                        && userArr != null && userArr.length > 0 ) {
    210                                // 4.3.8.0 (2009/08/01)
    211                                for( int i=0; i<userArr.length; i++ ) {
    212                                        if( "*".equals( userArr[i] ) || reqUser.equals( userArr[i] ) ) {
    213                                                rtn = true;
    214                                                if( isDebug ) {
    215                                                        System.out.println( "  check OK" );
    216                                                }
    217                                                break;
    218                                        }
    219                                }
    220                        }
    221                }
    222                catch( RuntimeException ex ) {
    223                        if( isDebug ) {
    224                                String errMsg = "チェックエラー。 "
    225                                                        + " checkKey=" + checkKey
    226                                                        + " " + ex.getMessage();                        // 5.1.8.0 (2010/07/01) errMsg 修正
    227                                System.out.println( errMsg );
    228                                ex.printStackTrace();
    229                        }
    230                        rtn = false;
    231                }
    232                return rtn;
    233        }
    234
    235        /**
    236         * 内部状態を文字列で返します。
    237         *
    238         * @return      このクラスの文字列表示
    239         */
    240        @Override
    241        public String toString() {
    242                StringBuilder sb = new StringBuilder();
    243                sb.append( "UrlCheckFilter" );
    244                sb.append( "[" ).append( filename  ).append( "],");
    245                return sb.toString();
    246        }
    247}