Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
PathMatchingFilter |
|
| 2.5;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 | } |