Coverage Report - org.apache.shiro.spring.web.ShiroFilterFactoryBean
 
Classes in this File Line Coverage Branch Coverage Complexity
ShiroFilterFactoryBean
75%
78/104
42%
17/40
1.962
ShiroFilterFactoryBean$SpringShiroFilter
85%
6/7
50%
2/4
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  
  * &lt;filter&gt;
 54  
  *   &lt;filter-name&gt;<b>shiroFilter</b>&lt;/filter-name&gt;
 55  
  *   &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;filter-class&gt;
 56  
  *   &lt;init-param&gt;
 57  
  *    &lt;param-name&gt;targetFilterLifecycle&lt;/param-name&gt;
 58  
  *     &lt;param-value&gt;true&lt;/param-value&gt;
 59  
  *   &lt;/init-param&gt;
 60  
  * &lt;/filter&gt;
 61  
  * </pre>
 62  
  * Then, in your spring XML file that defines your web ApplicationContext:
 63  
  * <pre>
 64  
  * &lt;bean id="<b>shiroFilter</b>" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
 65  
  *    &lt;property name="securityManager" ref="securityManager"/&gt;
 66  
  *    &lt;!-- other properties as necessary ... --&gt;
 67  
  * &lt;/bean&gt;
 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  
  * &lt;bean id="<b>myCustomFilter</b>" class="com.class.that.implements.javax.servlet.Filter"/&gt;
 81  
  * ...
 82  
  * &lt;bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
 83  
  *    ...
 84  
  *    &lt;property name="filterChainDefinitions"&gt;
 85  
  *        &lt;value&gt;
 86  
  *            /some/path/** = authc, <b>myCustomFilter</b>
 87  
  *        &lt;/value&gt;
 88  
  *    &lt;/property&gt;
 89  
  * &lt;/bean&gt;
 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  
      * &lt;bean id=&quot;myFilter&quot; class=&quot;com.class.that.implements.javax.servlet.Filter&quot;&gt;
 274  
      * ...
 275  
      * &lt;/bean&gt;</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  
 }