Coverage Report - org.apache.shiro.web.filter.PathMatchingFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
PathMatchingFilter
83%
30/36
61%
11/18
2.5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.web.filter;
 20  
 
 21  
 import org.apache.shiro.util.AntPathMatcher;
 22  
 import org.apache.shiro.util.PatternMatcher;
 23  
 import org.apache.shiro.util.StringUtils;
 24  
 import org.apache.shiro.web.servlet.AdviceFilter;
 25  
 import org.apache.shiro.web.util.WebUtils;
 26  
 import org.slf4j.Logger;
 27  
 import org.slf4j.LoggerFactory;
 28  
 
 29  
 import javax.servlet.Filter;
 30  
 import javax.servlet.ServletRequest;
 31  
 import javax.servlet.ServletResponse;
 32  
 import java.util.LinkedHashMap;
 33  
 import java.util.Map;
 34  
 
 35  
 import static org.apache.shiro.util.StringUtils.split;
 36  
 
 37  
 /**
 38  
  * <p>Base class for Filters that will process only specified paths and allow all others to pass through.</p>
 39  
  *
 40  
  * @since 0.9
 41  
  */
 42  990
 public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {
 43  
 
 44  
     /**
 45  
      * Log available to this class only
 46  
      */
 47  2
     private static final Logger log = LoggerFactory.getLogger(PathMatchingFilter.class);
 48  
 
 49  
     /**
 50  
      * PatternMatcher used in determining which paths to react to for a given request.
 51  
      */
 52  990
     protected PatternMatcher pathMatcher = new AntPathMatcher();
 53  
 
 54  
     /**
 55  
      * A collection of path-to-config entries where the key is a path which this filter should process and
 56  
      * the value is the (possibly null) configuration element specific to this Filter for that specific path.
 57  
      * <p/>
 58  
      * <p>To put it another way, the keys are the paths (urls) that this Filter will process.
 59  
      * <p>The values are filter-specific data that this Filter should use when processing the corresponding
 60  
      * key (path).  The values can be null if no Filter-specific config was specified for that url.
 61  
      */
 62  990
     protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();
 63  
 
 64  
     /**
 65  
      * Splits any comma-delmited values that might be found in the <code>config</code> argument and sets the resulting
 66  
      * <code>String[]</code> array on the <code>appliedPaths</code> internal Map.
 67  
      * <p/>
 68  
      * That is:
 69  
      * <pre><code>
 70  
      * String[] values = null;
 71  
      * if (config != null) {
 72  
      *     values = split(config);
 73  
      * }
 74  
      * <p/>
 75  
      * this.{@link #appliedPaths appliedPaths}.put(path, values);
 76  
      * </code></pre>
 77  
      *
 78  
      * @param path   the application context path to match for executing this filter.
 79  
      * @param config the specified for <em>this particular filter only</em> for the given <code>path</code>
 80  
      * @return this configured filter.
 81  
      */
 82  
     public Filter processPathConfig(String path, String config) {
 83  34
         String[] values = null;
 84  34
         if (config != null) {
 85  6
             values = split(config);
 86  
         }
 87  
 
 88  34
         this.appliedPaths.put(path, values);
 89  34
         return this;
 90  
     }
 91  
 
 92  
     /**
 93  
      * Returns the context path within the application based on the specified <code>request</code>.
 94  
      * <p/>
 95  
      * This implementation merely delegates to
 96  
      * {@link WebUtils#getPathWithinApplication(javax.servlet.http.HttpServletRequest) WebUtils.getPathWithinApplication(request)},
 97  
      * but can be overridden by subclasses for custom logic.
 98  
      *
 99  
      * @param request the incoming <code>ServletRequest</code>
 100  
      * @return the context path within the application.
 101  
      */
 102  
     protected String getPathWithinApplication(ServletRequest request) {
 103  6
         return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
 104  
     }
 105  
 
 106  
     /**
 107  
      * Returns <code>true</code> if the incoming <code>request</code> matches the specified <code>path</code> pattern,
 108  
      * <code>false</code> otherwise.
 109  
      * <p/>
 110  
      * The default implementation acquires the <code>request</code>'s path within the application and determines
 111  
      * if that matches:
 112  
      * <p/>
 113  
      * <code>String requestURI = {@link #getPathWithinApplication(javax.servlet.ServletRequest) getPathWithinApplication(request)};<br/>
 114  
      * return {@link #pathsMatch(String, String) pathsMatch(path,requestURI)}</code>
 115  
      *
 116  
      * @param path    the configured url pattern to check the incoming request against.
 117  
      * @param request the incoming ServletRequest
 118  
      * @return <code>true</code> if the incoming <code>request</code> matches the specified <code>path</code> pattern,
 119  
      *         <code>false</code> otherwise.
 120  
      */
 121  
     protected boolean pathsMatch(String path, ServletRequest request) {
 122  6
         String requestURI = getPathWithinApplication(request);
 123  6
         log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
 124  6
         return pathsMatch(path, requestURI);
 125  
     }
 126  
 
 127  
     /**
 128  
      * Returns <code>true</code> if the <code>path</code> matches the specified <code>pattern</code> string,
 129  
      * <code>false</code> otherwise.
 130  
      * <p/>
 131  
      * Simply delegates to
 132  
      * <b><code>this.pathMatcher.{@link PatternMatcher#matches(String, String) matches(pattern,path)}</code></b>,
 133  
      * but can be overridden by subclasses for custom matching behavior.
 134  
      *
 135  
      * @param pattern the pattern to match against
 136  
      * @param path    the value to match with the specified <code>pattern</code>
 137  
      * @return <code>true</code> if the <code>path</code> matches the specified <code>pattern</code> string,
 138  
      *         <code>false</code> otherwise.
 139  
      */
 140  
     protected boolean pathsMatch(String pattern, String path) {
 141  6
         return pathMatcher.matches(pattern, path);
 142  
     }
 143  
 
 144  
     /**
 145  
      * Implementation that handles path-matching behavior before a request is evaluated.  If the path matches and
 146  
      * the filter
 147  
      * {@link #isEnabled(javax.servlet.ServletRequest, javax.servlet.ServletResponse, String, Object) isEnabled} for
 148  
      * that path/config, the request will be allowed through via the result from
 149  
      * {@link #onPreHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) onPreHandle}.  If the
 150  
      * path does not match or the filter is not enabled for that path, this filter will allow passthrough immediately
 151  
      * to allow the {@code FilterChain} to continue executing.
 152  
      * <p/>
 153  
      * In order to retain path-matching functionality, subclasses should not override this method if at all
 154  
      * possible, and instead override
 155  
      * {@link #onPreHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) onPreHandle} instead.
 156  
      *
 157  
      * @param request  the incoming ServletRequest
 158  
      * @param response the outgoing ServletResponse
 159  
      * @return {@code true} if the filter chain is allowed to continue to execute, {@code false} if a subclass has
 160  
      *         handled the request explicitly.
 161  
      * @throws Exception if an error occurs
 162  
      */
 163  
     protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
 164  
 
 165  4
         if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
 166  0
             if (log.isTraceEnabled()) {
 167  0
                 log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
 168  
             }
 169  0
             return true;
 170  
         }
 171  
 
 172  4
         for (String path : this.appliedPaths.keySet()) {
 173  
             // If the path does match, then pass on to the subclass implementation for specific checks
 174  
             //(first match 'wins'):
 175  6
             if (pathsMatch(path, request)) {
 176  4
                 log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
 177  4
                 Object config = this.appliedPaths.get(path);
 178  4
                 return isFilterChainContinued(request, response, path, config);
 179  
             }
 180  2
         }
 181  
 
 182  
         //no path matched, allow the request to go through:
 183  0
         return true;
 184  
     }
 185  
 
 186  
     /**
 187  
      * Simple method to abstract out logic from the preHandle implementation - it was getting a bit unruly.
 188  
      *
 189  
      * @since 1.2
 190  
      */
 191  
     @SuppressWarnings({"JavaDoc"})
 192  
     private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
 193  
                                            String path, Object pathConfig) throws Exception {
 194  
 
 195  4
         if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
 196  2
             if (log.isTraceEnabled()) {
 197  4
                 log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}].  " +
 198  
                         "Delegating to subclass implementation for 'onPreHandle' check.",
 199  2
                         new Object[]{getName(), path, pathConfig});
 200  
             }
 201  
             //The filter is enabled for this specific request, so delegate to subclass implementations
 202  
             //so they can decide if the request should continue through the chain or not:
 203  2
             return onPreHandle(request, response, pathConfig);
 204  
         }
 205  
 
 206  2
         if (log.isTraceEnabled()) {
 207  4
             log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}].  " +
 208  
                     "The next element in the FilterChain will be called immediately.",
 209  2
                     new Object[]{getName(), path, pathConfig});
 210  
         }
 211  
         //This filter is disabled for this specific request,
 212  
         //return 'true' immediately to indicate that the filter will not process the request
 213  
         //and let the request/response to continue through the filter chain:
 214  2
         return true;
 215  
     }
 216  
 
 217  
     /**
 218  
      * This default implementation always returns {@code true} and should be overridden by subclasses for custom
 219  
      * logic if necessary.
 220  
      *
 221  
      * @param request     the incoming ServletRequest
 222  
      * @param response    the outgoing ServletResponse
 223  
      * @param mappedValue the filter-specific config value mapped to this filter in the URL rules mappings.
 224  
      * @return {@code true} if the request should be able to continue, {@code false} if the filter will
 225  
      *         handle the response directly.
 226  
      * @throws Exception if an error occurs
 227  
      * @see #isEnabled(javax.servlet.ServletRequest, javax.servlet.ServletResponse, String, Object)
 228  
      */
 229  
     protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
 230  0
         return true;
 231  
     }
 232  
 
 233  
     /**
 234  
      * Path-matching version of the parent class's
 235  
      * {@link #isEnabled(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} method, but additionally allows
 236  
      * for inspection of any path-specific configuration values corresponding to the specified request.  Subclasses
 237  
      * may wish to inspect this additional mapped configuration to determine if the filter is enabled or not.
 238  
      * <p/>
 239  
      * This method's default implementation ignores the {@code path} and {@code mappedValue} arguments and merely
 240  
      * returns the value from a call to {@link #isEnabled(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}.
 241  
      * It is expected that subclasses override this method if they need to perform enable/disable logic for a specific
 242  
      * request based on any path-specific config for the filter instance.
 243  
      *
 244  
      * @param request     the incoming servlet request
 245  
      * @param response    the outbound servlet response
 246  
      * @param path        the path matched for the incoming servlet request that has been configured with the given {@code mappedValue}.
 247  
      * @param mappedValue the filter-specific config value mapped to this filter in the URL rules mappings for the given {@code path}.
 248  
      * @return {@code true} if this filter should filter the specified request, {@code false} if it should let the
 249  
      *         request/response pass through immediately to the next element in the {@code FilterChain}.
 250  
      * @throws Exception in the case of any error
 251  
      * @since 1.2
 252  
      */
 253  
     @SuppressWarnings({"UnusedParameters"})
 254  
     protected boolean isEnabled(ServletRequest request, ServletResponse response, String path, Object mappedValue)
 255  
             throws Exception {
 256  0
         return isEnabled(request, response);
 257  
     }
 258  
 }