001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.spring.web; 020 021import org.apache.shiro.config.Ini; 022import org.apache.shiro.mgt.SecurityManager; 023import org.apache.shiro.util.CollectionUtils; 024import org.apache.shiro.lang.util.Nameable; 025import org.apache.shiro.lang.util.StringUtils; 026import org.apache.shiro.web.config.IniFilterChainResolverFactory; 027import org.apache.shiro.web.config.ShiroFilterConfiguration; 028import org.apache.shiro.web.filter.AccessControlFilter; 029import org.apache.shiro.web.filter.InvalidRequestFilter; 030import org.apache.shiro.web.filter.authc.AuthenticationFilter; 031import org.apache.shiro.web.filter.authz.AuthorizationFilter; 032import org.apache.shiro.web.filter.mgt.DefaultFilter; 033import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager; 034import org.apache.shiro.web.filter.mgt.FilterChainManager; 035import org.apache.shiro.web.filter.mgt.FilterChainResolver; 036import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; 037import org.apache.shiro.web.mgt.WebSecurityManager; 038import org.apache.shiro.web.servlet.AbstractShiroFilter; 039import org.apache.shiro.web.servlet.OncePerRequestFilter; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042import org.springframework.beans.BeansException; 043import org.springframework.beans.factory.BeanInitializationException; 044import org.springframework.beans.factory.FactoryBean; 045import org.springframework.beans.factory.config.BeanPostProcessor; 046 047import javax.servlet.Filter; 048import java.util.ArrayList; 049import java.util.LinkedHashMap; 050import java.util.List; 051import java.util.Map; 052 053/** 054 * {@link org.springframework.beans.factory.FactoryBean FactoryBean} to be used in Spring-based web applications for 055 * defining the master Shiro Filter. 056 * <h4>Usage</h4> 057 * Declare a DelegatingFilterProxy in {@code web.xml}, matching the filter name to the bean id: 058 * <pre> 059 * <filter> 060 * <filter-name><b>shiroFilter</b></filter-name> 061 * <filter-class>org.springframework.web.filter.DelegatingFilterProxy<filter-class> 062 * <init-param> 063 * <param-name>targetFilterLifecycle</param-name> 064 * <param-value>true</param-value> 065 * </init-param> 066 * </filter> 067 * </pre> 068 * Then, in your spring XML file that defines your web ApplicationContext: 069 * <pre> 070 * <bean id="<b>shiroFilter</b>" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 071 * <property name="securityManager" ref="securityManager"/> 072 * <!-- other properties as necessary ... --> 073 * </bean> 074 * </pre> 075 * <h4>Filter Auto-Discovery</h4> 076 * While there is a {@link #setFilters(java.util.Map) filters} property that allows you to assign a filter beans 077 * to the 'pool' of filters available when defining {@link #setFilterChainDefinitions(String) filter chains}, it is 078 * optional. 079 * <p/> 080 * This implementation is also a {@link BeanPostProcessor} and will acquire 081 * any {@link javax.servlet.Filter Filter} beans defined independently in your Spring application context. Upon 082 * discovery, they will be automatically added to the {@link #setFilters(java.util.Map) map} keyed by the bean ID. 083 * That ID can then be used in the filter chain definitions, for example: 084 * 085 * <pre> 086 * <bean id="<b>myCustomFilter</b>" class="com.class.that.implements.javax.servlet.Filter"/> 087 * ... 088 * <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 089 * ... 090 * <property name="filterChainDefinitions"> 091 * <value> 092 * /some/path/** = authc, <b>myCustomFilter</b> 093 * </value> 094 * </property> 095 * </bean> 096 * </pre> 097 * <h4>Global Property Values</h4> 098 * Most Shiro servlet Filter implementations exist for defining custom Filter 099 * {@link #setFilterChainDefinitions(String) chain definitions}. Most implementations subclass one of the 100 * {@link AccessControlFilter}, {@link AuthenticationFilter}, {@link AuthorizationFilter} classes to simplify things, 101 * and each of these 3 classes has configurable properties that are application-specific. 102 * <p/> 103 * A dilemma arises where, if you want to for example set the application's 'loginUrl' for any Filter, you don't want 104 * to have to manually specify that value for <em>each</em> filter instance defined. 105 * <p/> 106 * To prevent configuration duplication, this implementation provides the following properties to allow you 107 * to set relevant values in only one place: 108 * <ul> 109 * <li>{@link #setLoginUrl(String)}</li> 110 * <li>{@link #setSuccessUrl(String)}</li> 111 * <li>{@link #setUnauthorizedUrl(String)}</li> 112 * </ul> 113 * <p> 114 * Then at startup, any values specified via these 3 properties will be applied to all configured 115 * Filter instances so you don't have to specify them individually on each filter instance. To ensure your own custom 116 * filters benefit from this convenience, your filter implementation should subclass one of the 3 mentioned 117 * earlier. 118 * 119 * @see org.springframework.web.filter.DelegatingFilterProxy DelegatingFilterProxy 120 * @since 1.0 121 */ 122public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor { 123 124 private static final Logger LOGGER = LoggerFactory.getLogger(ShiroFilterFactoryBean.class); 125 126 private SecurityManager securityManager; 127 128 private Map<String, Filter> filters; 129 130 private List<String> globalFilters; 131 132 //urlPathExpression_to_comma-delimited-filter-chain-definition 133 private Map<String, String> filterChainDefinitionMap; 134 135 private String loginUrl; 136 private String successUrl; 137 private String unauthorizedUrl; 138 139 private AbstractShiroFilter instance; 140 141 private ShiroFilterConfiguration filterConfiguration; 142 143 public ShiroFilterFactoryBean() { 144 this.filters = new LinkedHashMap<String, Filter>(); 145 this.globalFilters = new ArrayList<>(); 146 this.globalFilters.add(DefaultFilter.invalidRequest.name()); 147 //order matters! 148 this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); 149 this.filterConfiguration = new ShiroFilterConfiguration(); 150 } 151 152 /** 153 * Gets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. This is a 154 * required property - failure to set it will throw an initialization exception. 155 * 156 * @return the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. 157 */ 158 public SecurityManager getSecurityManager() { 159 return securityManager; 160 } 161 162 /** 163 * Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. This is a 164 * required property - failure to set it will throw an initialization exception. 165 * 166 * @param securityManager the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. 167 */ 168 public void setSecurityManager(SecurityManager securityManager) { 169 this.securityManager = securityManager; 170 } 171 172 /** 173 * Gets the application {@code ShiroFilterConfiguration} instance to be used by the constructed Shiro Filter. 174 * 175 * @return the application {@code ShiroFilterConfiguration} instance to be used by the constructed Shiro Filter. 176 */ 177 public ShiroFilterConfiguration getShiroFilterConfiguration() { 178 return filterConfiguration; 179 } 180 181 /** 182 * Sets the application {@code ShiroFilterConfiguration} instance to be used by the constructed Shiro Filter. 183 * 184 * @param filterConfiguration the application {@code SecurityManager} instance to be used by the constructed Shiro Filter. 185 */ 186 public void setShiroFilterConfiguration(ShiroFilterConfiguration filterConfiguration) { 187 this.filterConfiguration = filterConfiguration; 188 } 189 190 /** 191 * Returns the application's login URL to be assigned to all acquired Filters that subclass 192 * {@link AccessControlFilter} or {@code null} if no value should be assigned globally. The default value 193 * is {@code null}. 194 * 195 * @return the application's login URL to be assigned to all acquired Filters that subclass 196 * {@link AccessControlFilter} or {@code null} if no value should be assigned globally. 197 * @see #setLoginUrl 198 */ 199 public String getLoginUrl() { 200 return loginUrl; 201 } 202 203 /** 204 * Sets the application's login URL to be assigned to all acquired Filters that subclass 205 * {@link AccessControlFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, 206 * as well for any default ones ({@code authc}, {@code user}, etc.), this value will be passed on to each Filter 207 * via the {@link AccessControlFilter#setLoginUrl(String)} method<b>*</b>. This eliminates the need to 208 * configure the 'loginUrl' property manually on each filter instance, and instead that can be configured once 209 * via this attribute. 210 * <p/> 211 * <b>*</b>If a filter already has already been explicitly configured with a value, it will 212 * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. 213 * 214 * @param loginUrl the application's login URL to apply to as a convenience to all discovered 215 * {@link AccessControlFilter} instances. 216 * @see AccessControlFilter#setLoginUrl(String) 217 */ 218 public void setLoginUrl(String loginUrl) { 219 this.loginUrl = loginUrl; 220 } 221 222 /** 223 * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass 224 * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value 225 * is {@code null}. 226 * 227 * @return the application's after-login success URL to be assigned to all acquired Filters that subclass 228 * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. 229 * @see #setSuccessUrl 230 */ 231 public String getSuccessUrl() { 232 return successUrl; 233 } 234 235 /** 236 * Sets the application's after-login success URL to be assigned to all acquired Filters that subclass 237 * {@link AuthenticationFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, 238 * as well for any default ones ({@code authc}, {@code user}, etc.), this value will be passed on to each Filter 239 * via the {@link AuthenticationFilter#setSuccessUrl(String)} method<b>*</b>. This eliminates the need to 240 * configure the 'successUrl' property manually on each filter instance, and instead that can be configured once 241 * via this attribute. 242 * <p/> 243 * <b>*</b>If a filter already has already been explicitly configured with a value, it will 244 * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. 245 * 246 * @param successUrl the application's after-login success URL to apply to as a convenience to all discovered 247 * {@link AccessControlFilter} instances. 248 * @see AuthenticationFilter#setSuccessUrl(String) 249 */ 250 public void setSuccessUrl(String successUrl) { 251 this.successUrl = successUrl; 252 } 253 254 /** 255 * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass 256 * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value 257 * is {@code null}. 258 * 259 * @return the application's after-login success URL to be assigned to all acquired Filters that subclass 260 * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. 261 * @see #setSuccessUrl 262 */ 263 public String getUnauthorizedUrl() { 264 return unauthorizedUrl; 265 } 266 267 /** 268 * Sets the application's 'unauthorized' URL to be assigned to all acquired Filters that subclass 269 * {@link AuthorizationFilter}. This is a convenience mechanism: for all configured {@link #setFilters filters}, 270 * as well for any default ones ({@code roles}, {@code perms}, etc.), this value will be passed on to each Filter 271 * via the {@link AuthorizationFilter#setUnauthorizedUrl(String)} method<b>*</b>. This eliminates the need to 272 * configure the 'unauthorizedUrl' property manually on each filter instance, and instead that can be configured once 273 * via this attribute. 274 * <p/> 275 * <b>*</b>If a filter already has already been explicitly configured with a value, it will 276 * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property. 277 * 278 * @param unauthorizedUrl the application's 'unauthorized' URL to apply to as a convenience to all discovered 279 * {@link AuthorizationFilter} instances. 280 * @see AuthorizationFilter#setUnauthorizedUrl(String) 281 */ 282 public void setUnauthorizedUrl(String unauthorizedUrl) { 283 this.unauthorizedUrl = unauthorizedUrl; 284 } 285 286 /** 287 * Returns the filterName-to-Filter map of filters available for reference when defining filter chain definitions. 288 * All filter chain definitions will reference filters by the names in this map (i.e. the keys). 289 * 290 * @return the filterName-to-Filter map of filters available for reference when defining filter chain definitions. 291 */ 292 public Map<String, Filter> getFilters() { 293 return filters; 294 } 295 296 /** 297 * Sets the filterName-to-Filter map of filters available for reference when creating 298 * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}. 299 * <p/> 300 * <b>Note:</b> This property is optional: this {@code FactoryBean} implementation will discover all beans in the 301 * web application context that implement the {@link Filter} interface and automatically add them to this filter 302 * map under their bean name. 303 * <p/> 304 * For example, just defining this bean in a web Spring XML application context: 305 * <pre> 306 * <bean id="myFilter" class="com.class.that.implements.javax.servlet.Filter"> 307 * ... 308 * </bean></pre> 309 * Will automatically place that bean into this Filters map under the key '<b>myFilter</b>'. 310 * 311 * @param filters the optional filterName-to-Filter map of filters available for reference when creating 312 * {@link #setFilterChainDefinitionMap (java.util.Map) filter chain definitions}. 313 */ 314 public void setFilters(Map<String, Filter> filters) { 315 this.filters = filters; 316 } 317 318 /** 319 * Returns the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted 320 * by the Shiro Filter. Each map entry should conform to the format defined by the 321 * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL 322 * path expression) and the map value is the comma-delimited string chain definition. 323 * 324 * @return he chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted 325 * by the Shiro Filter. 326 */ 327 public Map<String, String> getFilterChainDefinitionMap() { 328 return filterChainDefinitionMap; 329 } 330 331 /** 332 * Sets the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted 333 * by the Shiro Filter. Each map entry should conform to the format defined by the 334 * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL 335 * path expression) and the map value is the comma-delimited string chain definition. 336 * 337 * @param filterChainDefinitionMap the chainName-to-chainDefinition map of chain definitions to use for creating 338 * filter chains intercepted by the Shiro Filter. 339 */ 340 public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) { 341 this.filterChainDefinitionMap = filterChainDefinitionMap; 342 } 343 344 /** 345 * A convenience method that sets the {@link #setFilterChainDefinitionMap(java.util.Map) filterChainDefinitionMap} 346 * property by accepting a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs). 347 * Each key/value pair must conform to the format defined by the 348 * {@link FilterChainManager#createChain(String, String)} JavaDoc - each property key is an ant URL 349 * path expression and the value is the comma-delimited chain definition. 350 * 351 * @param definitions a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs) 352 * where each key/value pair represents a single urlPathExpression-commaDelimitedChainDefinition. 353 */ 354 public void setFilterChainDefinitions(String definitions) { 355 Ini ini = new Ini(); 356 ini.load(definitions); 357 //did they explicitly state a 'urls' section? Not necessary, but just in case: 358 Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS); 359 if (CollectionUtils.isEmpty(section)) { 360 //no urls section. Since this _is_ a urls chain definition property, just assume the 361 //default section contains only the definitions: 362 section = ini.getSection(Ini.DEFAULT_SECTION_NAME); 363 } 364 setFilterChainDefinitionMap(section); 365 } 366 367 /** 368 * Sets the list of filters that will be executed against every request. 369 * Defaults to the {@link InvalidRequestFilter} which will block known invalid request attacks. 370 * 371 * @param globalFilters the list of filters to execute before specific path filters. 372 */ 373 public void setGlobalFilters(List<String> globalFilters) { 374 this.globalFilters = globalFilters; 375 } 376 377 /** 378 * Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the 379 * {@link #createInstance} method. 380 * 381 * @return the application's Shiro Filter instance used to filter incoming web requests. 382 * @throws Exception if there is a problem creating the {@code Filter} instance. 383 */ 384 public Object getObject() throws Exception { 385 if (instance == null) { 386 instance = createInstance(); 387 } 388 return instance; 389 } 390 391 /** 392 * Returns <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code> 393 * 394 * @return <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code> 395 */ 396 public Class getObjectType() { 397 return SpringShiroFilter.class; 398 } 399 400 /** 401 * Returns {@code true} always. There is almost always only ever 1 Shiro {@code Filter} per web application. 402 * 403 * @return {@code true} always. There is almost always only ever 1 Shiro {@code Filter} per web application. 404 */ 405 public boolean isSingleton() { 406 return true; 407 } 408 409 protected FilterChainManager createFilterChainManager() { 410 411 DefaultFilterChainManager manager = new DefaultFilterChainManager(); 412 Map<String, Filter> defaultFilters = manager.getFilters(); 413 //apply global settings if necessary: 414 for (Filter filter : defaultFilters.values()) { 415 applyGlobalPropertiesIfNecessary(filter); 416 } 417 418 //Apply the acquired and/or configured filters: 419 Map<String, Filter> filters = getFilters(); 420 if (!CollectionUtils.isEmpty(filters)) { 421 for (Map.Entry<String, Filter> entry : filters.entrySet()) { 422 String name = entry.getKey(); 423 Filter filter = entry.getValue(); 424 applyGlobalPropertiesIfNecessary(filter); 425 if (filter instanceof Nameable) { 426 ((Nameable) filter).setName(name); 427 } 428 //'init' argument is false, since Spring-configured filters should be initialized 429 //in Spring (i.e. 'init-method=blah') or implement InitializingBean: 430 manager.addFilter(name, filter, false); 431 } 432 } 433 434 // set the global filters 435 manager.setGlobalFilters(this.globalFilters); 436 437 //build up the chains: 438 Map<String, String> chains = getFilterChainDefinitionMap(); 439 if (!CollectionUtils.isEmpty(chains)) { 440 for (Map.Entry<String, String> entry : chains.entrySet()) { 441 String url = entry.getKey(); 442 String chainDefinition = entry.getValue(); 443 manager.createChain(url, chainDefinition); 444 } 445 } 446 447 // create the default chain, to match anything the path matching would have missed 448 // TODO this assumes ANT path matching, which might be OK here 449 manager.createDefaultChain("/**"); 450 451 return manager; 452 } 453 454 /** 455 * This implementation: 456 * <ol> 457 * <li>Ensures the required {@link #setSecurityManager(org.apache.shiro.mgt.SecurityManager) securityManager} 458 * property has been set</li> 459 * <li>{@link #createFilterChainManager() Creates} a {@link FilterChainManager} instance that reflects the 460 * configured {@link #setFilters(java.util.Map) filters} and 461 * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}</li> 462 * <li>Wraps the FilterChainManager with a suitable 463 * {@link org.apache.shiro.web.filter.mgt.FilterChainResolver FilterChainResolver} since the Shiro Filter 464 * implementations do not know of {@code FilterChainManager}s</li> 465 * <li>Sets both the {@code SecurityManager} and {@code FilterChainResolver} instances on a new Shiro Filter 466 * instance and returns that filter instance.</li> 467 * </ol> 468 * 469 * @return a new Shiro Filter reflecting any configured filters and filter chain definitions. 470 * @throws Exception if there is a problem creating the AbstractShiroFilter instance. 471 */ 472 protected AbstractShiroFilter createInstance() throws Exception { 473 474 LOGGER.debug("Creating Shiro Filter instance."); 475 476 SecurityManager securityManager = getSecurityManager(); 477 if (securityManager == null) { 478 String msg = "SecurityManager property must be set."; 479 throw new BeanInitializationException(msg); 480 } 481 482 if (!(securityManager instanceof WebSecurityManager)) { 483 String msg = "The security manager does not implement the WebSecurityManager interface."; 484 throw new BeanInitializationException(msg); 485 } 486 487 FilterChainManager manager = createFilterChainManager(); 488 489 //Expose the constructed FilterChainManager by first wrapping it in a 490 // FilterChainResolver implementation. The AbstractShiroFilter implementations 491 // do not know about FilterChainManagers - only resolvers: 492 PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); 493 chainResolver.setFilterChainManager(manager); 494 495 //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built 496 //FilterChainResolver. It doesn't matter that the instance is an anonymous inner class 497 //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts 498 //injection of the SecurityManager and FilterChainResolver: 499 return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver, getShiroFilterConfiguration()); 500 } 501 502 private void applyLoginUrlIfNecessary(Filter filter) { 503 String loginUrl = getLoginUrl(); 504 if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) { 505 AccessControlFilter acFilter = (AccessControlFilter) filter; 506 //only apply the login url if they haven't explicitly configured one already: 507 String existingLoginUrl = acFilter.getLoginUrl(); 508 if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) { 509 acFilter.setLoginUrl(loginUrl); 510 } 511 } 512 } 513 514 private void applySuccessUrlIfNecessary(Filter filter) { 515 String successUrl = getSuccessUrl(); 516 if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) { 517 AuthenticationFilter authcFilter = (AuthenticationFilter) filter; 518 //only apply the successUrl if they haven't explicitly configured one already: 519 String existingSuccessUrl = authcFilter.getSuccessUrl(); 520 if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) { 521 authcFilter.setSuccessUrl(successUrl); 522 } 523 } 524 } 525 526 private void applyUnauthorizedUrlIfNecessary(Filter filter) { 527 String unauthorizedUrl = getUnauthorizedUrl(); 528 if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) { 529 AuthorizationFilter authzFilter = (AuthorizationFilter) filter; 530 //only apply the unauthorizedUrl if they haven't explicitly configured one already: 531 String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl(); 532 if (existingUnauthorizedUrl == null) { 533 authzFilter.setUnauthorizedUrl(unauthorizedUrl); 534 } 535 } 536 } 537 538 private void applyGlobalPropertiesIfNecessary(Filter filter) { 539 applyLoginUrlIfNecessary(filter); 540 applySuccessUrlIfNecessary(filter); 541 applyUnauthorizedUrlIfNecessary(filter); 542 543 if (filter instanceof OncePerRequestFilter) { 544 ((OncePerRequestFilter) filter).setFilterOncePerRequest(filterConfiguration.isFilterOncePerRequest()); 545 } 546 } 547 548 /** 549 * Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter 550 * instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced 551 * later during filter chain construction. 552 */ 553 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 554 if (bean instanceof Filter) { 555 LOGGER.debug("Found filter chain candidate filter '{}'", beanName); 556 Filter filter = (Filter) bean; 557 applyGlobalPropertiesIfNecessary(filter); 558 getFilters().put(beanName, filter); 559 } else { 560 LOGGER.trace("Ignoring non-Filter bean '{}'", beanName); 561 } 562 return bean; 563 } 564 565 /** 566 * Does nothing - only exists to satisfy the BeanPostProcessor interface and immediately returns the 567 * {@code bean} argument. 568 */ 569 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 570 return bean; 571 } 572 573 /** 574 * Ordinarily the {@code AbstractShiroFilter} must be subclassed to additionally perform configuration 575 * and initialization behavior. Because this {@code FactoryBean} implementation manually builds the 576 * {@link AbstractShiroFilter}'s 577 * {@link AbstractShiroFilter#setSecurityManager(org.apache.shiro.web.mgt.WebSecurityManager) securityManager} and 578 * {@link AbstractShiroFilter#setFilterChainResolver(org.apache.shiro.web.filter.mgt.FilterChainResolver) filterChainResolver} 579 * properties, the only thing left to do is set those properties explicitly. We do that in a simple 580 * concrete subclass in the constructor. 581 */ 582 private static final class SpringShiroFilter extends AbstractShiroFilter { 583 584 protected SpringShiroFilter(WebSecurityManager webSecurityManager, 585 FilterChainResolver resolver, 586 ShiroFilterConfiguration filterConfiguration) { 587 super(); 588 if (webSecurityManager == null) { 589 throw new IllegalArgumentException("WebSecurityManager property cannot be null."); 590 } 591 setSecurityManager(webSecurityManager); 592 setShiroFilterConfiguration(filterConfiguration); 593 594 if (resolver != null) { 595 setFilterChainResolver(resolver); 596 } 597 } 598 } 599}