Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ShiroFilterFactoryBean |
|
| 1.9615384615384615;1.962 | ||||
ShiroFilterFactoryBean$SpringShiroFilter |
|
| 1.9615384615384615;1.962 |
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.spring.web; | |
20 | ||
21 | import org.apache.shiro.config.Ini; | |
22 | import org.apache.shiro.mgt.SecurityManager; | |
23 | import org.apache.shiro.util.CollectionUtils; | |
24 | import org.apache.shiro.util.Nameable; | |
25 | import org.apache.shiro.util.StringUtils; | |
26 | import org.apache.shiro.web.config.IniFilterChainResolverFactory; | |
27 | import org.apache.shiro.web.filter.AccessControlFilter; | |
28 | import org.apache.shiro.web.filter.authc.AuthenticationFilter; | |
29 | import org.apache.shiro.web.filter.authz.AuthorizationFilter; | |
30 | import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager; | |
31 | import org.apache.shiro.web.filter.mgt.FilterChainManager; | |
32 | import org.apache.shiro.web.filter.mgt.FilterChainResolver; | |
33 | import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; | |
34 | import org.apache.shiro.web.mgt.WebSecurityManager; | |
35 | import org.apache.shiro.web.servlet.AbstractShiroFilter; | |
36 | import org.slf4j.Logger; | |
37 | import org.slf4j.LoggerFactory; | |
38 | import org.springframework.beans.BeansException; | |
39 | import org.springframework.beans.factory.BeanInitializationException; | |
40 | import org.springframework.beans.factory.FactoryBean; | |
41 | import org.springframework.beans.factory.config.BeanPostProcessor; | |
42 | ||
43 | import javax.servlet.Filter; | |
44 | import java.util.LinkedHashMap; | |
45 | import java.util.Map; | |
46 | ||
47 | /** | |
48 | * {@link org.springframework.beans.factory.FactoryBean FactoryBean} to be used in Spring-based web applications for | |
49 | * defining the master Shiro Filter. | |
50 | * <h4>Usage</h4> | |
51 | * Declare a DelegatingFilterProxy in {@code web.xml}, matching the filter name to the bean id: | |
52 | * <pre> | |
53 | * <filter> | |
54 | * <filter-name><b>shiroFilter</b></filter-name> | |
55 | * <filter-class>org.springframework.web.filter.DelegatingFilterProxy<filter-class> | |
56 | * <init-param> | |
57 | * <param-name>targetFilterLifecycle</param-name> | |
58 | * <param-value>true</param-value> | |
59 | * </init-param> | |
60 | * </filter> | |
61 | * </pre> | |
62 | * Then, in your spring XML file that defines your web ApplicationContext: | |
63 | * <pre> | |
64 | * <bean id="<b>shiroFilter</b>" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> | |
65 | * <property name="securityManager" ref="securityManager"/> | |
66 | * <!-- other properties as necessary ... --> | |
67 | * </bean> | |
68 | * </pre> | |
69 | * <h4>Filter Auto-Discovery</h4> | |
70 | * While there is a {@link #setFilters(java.util.Map) filters} property that allows you to assign a filter beans | |
71 | * to the 'pool' of filters available when defining {@link #setFilterChainDefinitions(String) filter chains}, it is | |
72 | * optional. | |
73 | * <p/> | |
74 | * This implementation is also a {@link BeanPostProcessor} and will acquire | |
75 | * any {@link javax.servlet.Filter Filter} beans defined independently in your Spring application context. Upon | |
76 | * discovery, they will be automatically added to the {@link #setFilters(java.util.Map) map} keyed by the bean ID. | |
77 | * That ID can then be used in the filter chain definitions, for example: | |
78 | * | |
79 | * <pre> | |
80 | * <bean id="<b>myCustomFilter</b>" class="com.class.that.implements.javax.servlet.Filter"/> | |
81 | * ... | |
82 | * <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> | |
83 | * ... | |
84 | * <property name="filterChainDefinitions"> | |
85 | * <value> | |
86 | * /some/path/** = authc, <b>myCustomFilter</b> | |
87 | * </value> | |
88 | * </property> | |
89 | * </bean> | |
90 | * </pre> | |
91 | * <h4>Global Property Values</h4> | |
92 | * Most Shiro servlet Filter implementations exist for defining custom Filter | |
93 | * {@link #setFilterChainDefinitions(String) chain definitions}. Most implementations subclass one of the | |
94 | * {@link AccessControlFilter}, {@link AuthenticationFilter}, {@link AuthorizationFilter} classes to simplify things, | |
95 | * and each of these 3 classes has configurable properties that are application-specific. | |
96 | * <p/> | |
97 | * A dilemma arises where, if you want to for example set the application's 'loginUrl' for any Filter, you don't want | |
98 | * to have to manually specify that value for <em>each</em> filter instance definied. | |
99 | * <p/> | |
100 | * To prevent configuration duplication, this implementation provides the following properties to allow you | |
101 | * to set relevant values in only one place: | |
102 | * <ul> | |
103 | * <li>{@link #setLoginUrl(String)}</li> | |
104 | * <li>{@link #setSuccessUrl(String)}</li> | |
105 | * <li>{@link #setUnauthorizedUrl(String)}</li> | |
106 | * </ul> | |
107 | * | |
108 | * Then at startup, any values specified via these 3 properties will be applied to all configured | |
109 | * Filter instances so you don't have to specify them individually on each filter instance. To ensure your own custom | |
110 | * filters benefit from this convenience, your filter implementation should subclass one of the 3 mentioned | |
111 | * earlier. | |
112 | * | |
113 | * @see org.springframework.web.filter.DelegatingFilterProxy DelegatingFilterProxy | |
114 | * @since 1.0 | |
115 | */ | |
116 | public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor { | |
117 | ||
118 | 1 | private static transient final Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class); |
119 | ||
120 | private SecurityManager securityManager; | |
121 | ||
122 | private Map<String, Filter> filters; | |
123 | ||
124 | private Map<String, String> filterChainDefinitionMap; //urlPathExpression_to_comma-delimited-filter-chain-definition | |
125 | ||
126 | private String loginUrl; | |
127 | private String successUrl; | |
128 | private String unauthorizedUrl; | |
129 | ||
130 | private AbstractShiroFilter instance; | |
131 | ||
132 | 2 | public ShiroFilterFactoryBean() { |
133 | 2 | this.filters = new LinkedHashMap<String, Filter>(); |
134 | 2 | this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); //order matters! |
135 | 2 | } |
136 | ||
137 | /** | |
138 | * Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. This is a | |
139 | * required property - failure to set it will throw an initialization exception. | |
140 | * | |
141 | * @return the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. | |
142 | */ | |
143 | public SecurityManager getSecurityManager() { | |
144 | 2 | return securityManager; |
145 | } | |
146 | ||
147 | /** | |
148 | * Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. This is a | |
149 | * required property - failure to set it will throw an initialization exception. | |
150 | * | |
151 | * @param securityManager the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. | |
152 | */ | |
153 | public void setSecurityManager(SecurityManager securityManager) { | |
154 | 2 | this.securityManager = securityManager; |
155 | 2 | } |
156 | ||
157 | /** | |
158 | * Returns the application's login URL to be assigned to all acquired Filters that subclass | |
159 | * {@link AccessControlFilter} or {@code null} if no value should be assigned globally. The default value | |
160 | * is {@code null}. | |
161 | * | |
162 | * @return the application's login URL to be assigned to all acquired Filters that subclass | |
163 | * {@link AccessControlFilter} or {@code null} if no value should be assigned globally. | |
164 | * @see #setLoginUrl | |
165 | */ | |
166 | public String getLoginUrl() { | |
167 | 26 | return loginUrl; |
168 | } | |
169 | ||
170 | /** | |
171 | * Sets the application's login URL to be assigned to all acquired Filters that subclass | |
172 | * {@link AccessControlFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, | |
173 | * as well for any default ones ({@code authc}, {@code user}, etc), this value will be passed on to each Filter | |
174 | * via the {@link AccessControlFilter#setLoginUrl(String)} method<b>*</b>. This eliminates the need to | |
175 | * configure the 'loginUrl' property manually on each filter instance, and instead that can be configured once | |
176 | * via this attribute. | |
177 | * <p/> | |
178 | * <b>*</b>If a filter already has already been explicitly configured with a value, it will | |
179 | * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. | |
180 | * | |
181 | * @param loginUrl the application's login URL to apply to as a convenience to all discovered | |
182 | * {@link AccessControlFilter} instances. | |
183 | * @see AccessControlFilter#setLoginUrl(String) | |
184 | */ | |
185 | public void setLoginUrl(String loginUrl) { | |
186 | 0 | this.loginUrl = loginUrl; |
187 | 0 | } |
188 | ||
189 | /** | |
190 | * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass | |
191 | * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value | |
192 | * is {@code null}. | |
193 | * | |
194 | * @return the application's after-login success URL to be assigned to all acquired Filters that subclass | |
195 | * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. | |
196 | * @see #setSuccessUrl | |
197 | */ | |
198 | public String getSuccessUrl() { | |
199 | 26 | return successUrl; |
200 | } | |
201 | ||
202 | /** | |
203 | * Sets the application's after-login success URL to be assigned to all acquired Filters that subclass | |
204 | * {@link AuthenticationFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, | |
205 | * as well for any default ones ({@code authc}, {@code user}, etc), this value will be passed on to each Filter | |
206 | * via the {@link AuthenticationFilter#setSuccessUrl(String)} method<b>*</b>. This eliminates the need to | |
207 | * configure the 'successUrl' property manually on each filter instance, and instead that can be configured once | |
208 | * via this attribute. | |
209 | * <p/> | |
210 | * <b>*</b>If a filter already has already been explicitly configured with a value, it will | |
211 | * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. | |
212 | * | |
213 | * @param successUrl the application's after-login success URL to apply to as a convenience to all discovered | |
214 | * {@link AccessControlFilter} instances. | |
215 | * @see AuthenticationFilter#setSuccessUrl(String) | |
216 | */ | |
217 | public void setSuccessUrl(String successUrl) { | |
218 | 0 | this.successUrl = successUrl; |
219 | 0 | } |
220 | ||
221 | /** | |
222 | * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass | |
223 | * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value | |
224 | * is {@code null}. | |
225 | * | |
226 | * @return the application's after-login success URL to be assigned to all acquired Filters that subclass | |
227 | * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. | |
228 | * @see #setSuccessUrl | |
229 | */ | |
230 | public String getUnauthorizedUrl() { | |
231 | 26 | return unauthorizedUrl; |
232 | } | |
233 | ||
234 | /** | |
235 | * Sets the application's 'unauthorized' URL to be assigned to all acquired Filters that subclass | |
236 | * {@link AuthorizationFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, | |
237 | * as well for any default ones ({@code roles}, {@code perms}, etc), this value will be passed on to each Filter | |
238 | * via the {@link AuthorizationFilter#setUnauthorizedUrl(String)} method<b>*</b>. This eliminates the need to | |
239 | * configure the 'unauthorizedUrl' property manually on each filter instance, and instead that can be configured once | |
240 | * via this attribute. | |
241 | * <p/> | |
242 | * <b>*</b>If a filter already has already been explicitly configured with a value, it will | |
243 | * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. | |
244 | * | |
245 | * @param unauthorizedUrl the application's 'unauthorized' URL to apply to as a convenience to all discovered | |
246 | * {@link AuthorizationFilter} instances. | |
247 | * @see AuthorizationFilter#setUnauthorizedUrl(String) | |
248 | */ | |
249 | public void setUnauthorizedUrl(String unauthorizedUrl) { | |
250 | 0 | this.unauthorizedUrl = unauthorizedUrl; |
251 | 0 | } |
252 | ||
253 | /** | |
254 | * Returns the filterName-to-Filter map of filters available for reference when defining filter chain definitions. | |
255 | * All filter chain definitions will reference filters by the names in this map (i.e. the keys). | |
256 | * | |
257 | * @return the filterName-to-Filter map of filters available for reference when defining filter chain definitions. | |
258 | */ | |
259 | public Map<String, Filter> getFilters() { | |
260 | 4 | return filters; |
261 | } | |
262 | ||
263 | /** | |
264 | * Sets the filterName-to-Filter map of filters available for reference when creating | |
265 | * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}. | |
266 | * <p/> | |
267 | * <b>Note:</b> This property is optional: this {@code FactoryBean} implementation will discover all beans in the | |
268 | * web application context that implement the {@link Filter} interface and automatically add them to this filter | |
269 | * map under their bean name. | |
270 | * <p/> | |
271 | * For example, just defining this bean in a web Spring XML application context: | |
272 | * <pre> | |
273 | * <bean id="myFilter" class="com.class.that.implements.javax.servlet.Filter"> | |
274 | * ... | |
275 | * </bean></pre> | |
276 | * Will automatically place that bean into this Filters map under the key '<b>myFilter</b>'. | |
277 | * | |
278 | * @param filters the optional filterName-to-Filter map of filters available for reference when creating | |
279 | * {@link #setFilterChainDefinitionMap (java.util.Map) filter chain definitions}. | |
280 | */ | |
281 | public void setFilters(Map<String, Filter> filters) { | |
282 | 0 | this.filters = filters; |
283 | 0 | } |
284 | ||
285 | /** | |
286 | * Returns the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted | |
287 | * by the Shiro Filter. Each map entry should conform to the format defined by the | |
288 | * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL | |
289 | * path expression) and the map value is the comma-delimited string chain definition. | |
290 | * | |
291 | * @return he chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted | |
292 | * by the Shiro Filter. | |
293 | */ | |
294 | public Map<String, String> getFilterChainDefinitionMap() { | |
295 | 2 | return filterChainDefinitionMap; |
296 | } | |
297 | ||
298 | /** | |
299 | * Sets the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted | |
300 | * by the Shiro Filter. Each map entry should conform to the format defined by the | |
301 | * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL | |
302 | * path expression) and the map value is the comma-delimited string chain definition. | |
303 | * | |
304 | * @param filterChainDefinitionMap the chainName-to-chainDefinition map of chain definitions to use for creating | |
305 | * filter chains intercepted by the Shiro Filter. | |
306 | */ | |
307 | public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) { | |
308 | 2 | this.filterChainDefinitionMap = filterChainDefinitionMap; |
309 | 2 | } |
310 | ||
311 | /** | |
312 | * A convenience method that sets the {@link #setFilterChainDefinitionMap(java.util.Map) filterChainDefinitionMap} | |
313 | * property by accepting a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs). | |
314 | * Each key/value pair must conform to the format defined by the | |
315 | * {@link FilterChainManager#createChain(String,String)} JavaDoc - each property key is an ant URL | |
316 | * path expression and the value is the comma-delimited chain definition. | |
317 | * | |
318 | * @param definitions a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs) | |
319 | * where each key/value pair represents a single urlPathExpression-commaDelimitedChainDefinition. | |
320 | */ | |
321 | public void setFilterChainDefinitions(String definitions) { | |
322 | 2 | Ini ini = new Ini(); |
323 | 2 | ini.load(definitions); |
324 | //did they explicitly state a 'urls' section? Not necessary, but just in case: | |
325 | 2 | Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS); |
326 | 2 | if (CollectionUtils.isEmpty(section)) { |
327 | //no urls section. Since this _is_ a urls chain definition property, just assume the | |
328 | //default section contains only the definitions: | |
329 | 2 | section = ini.getSection(Ini.DEFAULT_SECTION_NAME); |
330 | } | |
331 | 2 | setFilterChainDefinitionMap(section); |
332 | 2 | } |
333 | ||
334 | /** | |
335 | * Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the | |
336 | * {@link #createInstance} method. | |
337 | * | |
338 | * @return the application's Shiro Filter instance used to filter incoming web requests. | |
339 | * @throws Exception if there is a problem creating the {@code Filter} instance. | |
340 | */ | |
341 | public Object getObject() throws Exception { | |
342 | 2 | if (instance == null) { |
343 | 2 | instance = createInstance(); |
344 | } | |
345 | 2 | return instance; |
346 | } | |
347 | ||
348 | /** | |
349 | * Returns <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code> | |
350 | * | |
351 | * @return <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code> | |
352 | */ | |
353 | public Class getObjectType() { | |
354 | 4 | return SpringShiroFilter.class; |
355 | } | |
356 | ||
357 | /** | |
358 | * Returns {@code true} always. There is almost always only ever 1 Shiro {@code Filter} per web application. | |
359 | * | |
360 | * @return {@code true} always. There is almost always only ever 1 Shiro {@code Filter} per web application. | |
361 | */ | |
362 | public boolean isSingleton() { | |
363 | 4 | return true; |
364 | } | |
365 | ||
366 | protected FilterChainManager createFilterChainManager() { | |
367 | ||
368 | 2 | DefaultFilterChainManager manager = new DefaultFilterChainManager(); |
369 | 2 | Map<String, Filter> defaultFilters = manager.getFilters(); |
370 | //apply global settings if necessary: | |
371 | 2 | for (Filter filter : defaultFilters.values()) { |
372 | 22 | applyGlobalPropertiesIfNecessary(filter); |
373 | 22 | } |
374 | ||
375 | //Apply the acquired and/or configured filters: | |
376 | 2 | Map<String, Filter> filters = getFilters(); |
377 | 2 | if (!CollectionUtils.isEmpty(filters)) { |
378 | 2 | for (Map.Entry<String, Filter> entry : filters.entrySet()) { |
379 | 2 | String name = entry.getKey(); |
380 | 2 | Filter filter = entry.getValue(); |
381 | 2 | applyGlobalPropertiesIfNecessary(filter); |
382 | 2 | if (filter instanceof Nameable) { |
383 | 0 | ((Nameable) filter).setName(name); |
384 | } | |
385 | //'init' argument is false, since Spring-configured filters should be initialized | |
386 | //in Spring (i.e. 'init-method=blah') or implement InitializingBean: | |
387 | 2 | manager.addFilter(name, filter, false); |
388 | 2 | } |
389 | } | |
390 | ||
391 | //build up the chains: | |
392 | 2 | Map<String, String> chains = getFilterChainDefinitionMap(); |
393 | 2 | if (!CollectionUtils.isEmpty(chains)) { |
394 | 2 | for (Map.Entry<String, String> entry : chains.entrySet()) { |
395 | 2 | String url = entry.getKey(); |
396 | 2 | String chainDefinition = entry.getValue(); |
397 | 2 | manager.createChain(url, chainDefinition); |
398 | 2 | } |
399 | } | |
400 | ||
401 | 2 | return manager; |
402 | } | |
403 | ||
404 | /** | |
405 | * This implementation: | |
406 | * <ol> | |
407 | * <li>Ensures the required {@link #setSecurityManager(org.apache.shiro.mgt.SecurityManager) securityManager} | |
408 | * property has been set</li> | |
409 | * <li>{@link #createFilterChainManager() Creates} a {@link FilterChainManager} instance that reflects the | |
410 | * configured {@link #setFilters(java.util.Map) filters} and | |
411 | * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}</li> | |
412 | * <li>Wraps the FilterChainManager with a suitable | |
413 | * {@link org.apache.shiro.web.filter.mgt.FilterChainResolver FilterChainResolver} since the Shiro Filter | |
414 | * implementations do not know of {@code FilterChainManager}s</li> | |
415 | * <li>Sets both the {@code SecurityManager} and {@code FilterChainResolver} instances on a new Shiro Filter | |
416 | * instance and returns that filter instance.</li> | |
417 | * </ol> | |
418 | * | |
419 | * @return a new Shiro Filter reflecting any configured filters and filter chain definitions. | |
420 | * @throws Exception if there is a problem creating the AbstractShiroFilter instance. | |
421 | */ | |
422 | protected AbstractShiroFilter createInstance() throws Exception { | |
423 | ||
424 | 2 | log.debug("Creating Shiro Filter instance."); |
425 | ||
426 | 2 | SecurityManager securityManager = getSecurityManager(); |
427 | 2 | if (securityManager == null) { |
428 | 0 | String msg = "SecurityManager property must be set."; |
429 | 0 | throw new BeanInitializationException(msg); |
430 | } | |
431 | ||
432 | 2 | if (!(securityManager instanceof WebSecurityManager)) { |
433 | 0 | String msg = "The security manager does not implement the WebSecurityManager interface."; |
434 | 0 | throw new BeanInitializationException(msg); |
435 | } | |
436 | ||
437 | 2 | FilterChainManager manager = createFilterChainManager(); |
438 | ||
439 | //Expose the constructed FilterChainManager by first wrapping it in a | |
440 | // FilterChainResolver implementation. The AbstractShiroFilter implementations | |
441 | // do not know about FilterChainManagers - only resolvers: | |
442 | 2 | PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); |
443 | 2 | chainResolver.setFilterChainManager(manager); |
444 | ||
445 | //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built | |
446 | //FilterChainResolver. It doesn't matter that the instance is an anonymous inner class | |
447 | //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts | |
448 | //injection of the SecurityManager and FilterChainResolver: | |
449 | 2 | return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver); |
450 | } | |
451 | ||
452 | private void applyLoginUrlIfNecessary(Filter filter) { | |
453 | 26 | String loginUrl = getLoginUrl(); |
454 | 26 | if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) { |
455 | 0 | AccessControlFilter acFilter = (AccessControlFilter) filter; |
456 | //only apply the login url if they haven't explicitly configured one already: | |
457 | 0 | String existingLoginUrl = acFilter.getLoginUrl(); |
458 | 0 | if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) { |
459 | 0 | acFilter.setLoginUrl(loginUrl); |
460 | } | |
461 | } | |
462 | 26 | } |
463 | ||
464 | private void applySuccessUrlIfNecessary(Filter filter) { | |
465 | 26 | String successUrl = getSuccessUrl(); |
466 | 26 | if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) { |
467 | 0 | AuthenticationFilter authcFilter = (AuthenticationFilter) filter; |
468 | //only apply the successUrl if they haven't explicitly configured one already: | |
469 | 0 | String existingSuccessUrl = authcFilter.getSuccessUrl(); |
470 | 0 | if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) { |
471 | 0 | authcFilter.setSuccessUrl(successUrl); |
472 | } | |
473 | } | |
474 | 26 | } |
475 | ||
476 | private void applyUnauthorizedUrlIfNecessary(Filter filter) { | |
477 | 26 | String unauthorizedUrl = getUnauthorizedUrl(); |
478 | 26 | if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) { |
479 | 0 | AuthorizationFilter authzFilter = (AuthorizationFilter) filter; |
480 | //only apply the unauthorizedUrl if they haven't explicitly configured one already: | |
481 | 0 | String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl(); |
482 | 0 | if (existingUnauthorizedUrl == null) { |
483 | 0 | authzFilter.setUnauthorizedUrl(unauthorizedUrl); |
484 | } | |
485 | } | |
486 | 26 | } |
487 | ||
488 | private void applyGlobalPropertiesIfNecessary(Filter filter) { | |
489 | 26 | applyLoginUrlIfNecessary(filter); |
490 | 26 | applySuccessUrlIfNecessary(filter); |
491 | 26 | applyUnauthorizedUrlIfNecessary(filter); |
492 | 26 | } |
493 | ||
494 | /** | |
495 | * Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter | |
496 | * instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced | |
497 | * later during filter chain construction. | |
498 | */ | |
499 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { | |
500 | 2 | if (bean instanceof Filter) { |
501 | 2 | log.debug("Found filter chain candidate filter '{}'", beanName); |
502 | 2 | Filter filter = (Filter) bean; |
503 | 2 | applyGlobalPropertiesIfNecessary(filter); |
504 | 2 | getFilters().put(beanName, filter); |
505 | 2 | } else { |
506 | 0 | log.trace("Ignoring non-Filter bean '{}'", beanName); |
507 | } | |
508 | 2 | return bean; |
509 | } | |
510 | ||
511 | /** | |
512 | * Does nothing - only exists to satisfy the BeanPostProcessor interface and immediately returns the | |
513 | * {@code bean} argument. | |
514 | */ | |
515 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | |
516 | 4 | return bean; |
517 | } | |
518 | ||
519 | /** | |
520 | * Ordinarily the {@code AbstractShiroFilter} must be subclassed to additionally perform configuration | |
521 | * and initialization behavior. Because this {@code FactoryBean} implementation manually builds the | |
522 | * {@link AbstractShiroFilter}'s | |
523 | * {@link AbstractShiroFilter#setSecurityManager(org.apache.shiro.web.mgt.WebSecurityManager) securityManager} and | |
524 | * {@link AbstractShiroFilter#setFilterChainResolver(org.apache.shiro.web.filter.mgt.FilterChainResolver) filterChainResolver} | |
525 | * properties, the only thing left to do is set those properties explicitly. We do that in a simple | |
526 | * concrete subclass in the constructor. | |
527 | */ | |
528 | private static final class SpringShiroFilter extends AbstractShiroFilter { | |
529 | ||
530 | protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { | |
531 | 2 | super(); |
532 | 2 | if (webSecurityManager == null) { |
533 | 0 | throw new IllegalArgumentException("WebSecurityManager property cannot be null."); |
534 | } | |
535 | 2 | setSecurityManager(webSecurityManager); |
536 | 2 | if (resolver != null) { |
537 | 2 | setFilterChainResolver(resolver); |
538 | } | |
539 | 2 | } |
540 | } | |
541 | } |