@ -16,8 +16,21 @@ import org.springframework.beans.factory.ListableBeanFactory;
@@ -16,8 +16,21 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor ;
import org.springframework.core.OrderComparator ;
import org.springframework.core.Ordered ;
import org.springframework.security.ConfigAttributeDefinition ;
import org.springframework.security.config.ConfigUtils.FilterChainList ;
import org.springframework.security.context.HttpSessionContextIntegrationFilter ;
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource ;
import org.springframework.security.intercept.web.FilterSecurityInterceptor ;
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken ;
import org.springframework.security.providers.anonymous.AnonymousProcessingFilter ;
import org.springframework.security.ui.ExceptionTranslationFilter ;
import org.springframework.security.ui.SessionFixationProtectionFilter ;
import org.springframework.security.ui.basicauth.BasicProcessingFilter ;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter ;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint ;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter ;
import org.springframework.security.util.FilterChainProxy ;
import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter ;
/ * *
*
@ -59,23 +72,114 @@ public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFac
@@ -59,23 +72,114 @@ public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFac
}
logger . info ( "Filter chain..." ) ;
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
// Remove the ordered wrapper from the filter and put it back in the chain at the same position.
Filter filter = unwrapFilter ( filters . get ( i ) ) ;
logger . info ( "[" + i + "] - " + filter ) ;
filters . set ( i , filter ) ;
}
checkFilterStack ( filters ) ;
// Note that this returns a copy
Map filterMap = filterChainProxy . getFilterChainMap ( ) ;
filterMap . put ( filterChainProxy . getMatcher ( ) . getUniversalMatchPattern ( ) , filters ) ;
filterChainProxy . setFilterChainMap ( filterMap ) ;
logger . info ( "FilterChainProxy: " + filterChainProxy ) ;
checkLoginPageIsntProtected ( filterChainProxy ) ;
logger . info ( "FilterChainProxy: " + filterChainProxy ) ;
return bean ;
}
/ * *
* Checks the filter list for possible errors and logs them
* /
private void checkFilterStack ( List filters ) {
checkForDuplicates ( HttpSessionContextIntegrationFilter . class , filters ) ;
checkForDuplicates ( AuthenticationProcessingFilter . class , filters ) ;
checkForDuplicates ( SessionFixationProtectionFilter . class , filters ) ;
checkForDuplicates ( BasicProcessingFilter . class , filters ) ;
checkForDuplicates ( SecurityContextHolderAwareRequestFilter . class , filters ) ;
checkForDuplicates ( ExceptionTranslationFilter . class , filters ) ;
checkForDuplicates ( FilterSecurityInterceptor . class , filters ) ;
}
private void checkForDuplicates ( Class clazz , List filters ) {
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
Filter f1 = ( Filter ) filters . get ( i ) ;
if ( clazz . isAssignableFrom ( f1 . getClass ( ) ) ) {
// Found the first one, check remaining for another
for ( int j = i + 1 ; j < filters . size ( ) ; j + + ) {
Filter f2 = ( Filter ) filters . get ( j ) ;
if ( clazz . isAssignableFrom ( f2 . getClass ( ) ) ) {
logger . warn ( "Possible error: Filters at position " + i + " and " + j + " are both " +
"instances of " + clazz . getName ( ) ) ;
return ;
}
}
}
}
}
/* Checks for the common error of having a login page URL protected by the security interceptor */
private void checkLoginPageIsntProtected ( FilterChainProxy fcp ) {
ExceptionTranslationFilter etf = ( ExceptionTranslationFilter ) beanFactory . getBean ( BeanIds . EXCEPTION_TRANSLATION_FILTER ) ;
if ( etf . getAuthenticationEntryPoint ( ) instanceof AuthenticationProcessingFilterEntryPoint ) {
String loginPage =
( ( AuthenticationProcessingFilterEntryPoint ) etf . getAuthenticationEntryPoint ( ) ) . getLoginFormUrl ( ) ;
List filters = fcp . getFilters ( loginPage ) ;
logger . info ( "Checking whether login URL '" + loginPage + "' is accessible with your configuration" ) ;
if ( filters = = null | | filters . isEmpty ( ) ) {
logger . debug ( "Filter chain is empty for the login page" ) ;
return ;
}
if ( loginPage . equals ( DefaultLoginPageGeneratingFilter . DEFAULT_LOGIN_PAGE_URL ) & &
beanFactory . containsBean ( BeanIds . DEFAULT_LOGIN_PAGE_GENERATING_FILTER ) ) {
logger . debug ( "Default generated login page is in use" ) ;
return ;
}
FilterSecurityInterceptor fsi =
( ( FilterSecurityInterceptor ) beanFactory . getBean ( BeanIds . FILTER_SECURITY_INTERCEPTOR ) ) ;
DefaultFilterInvocationDefinitionSource fids =
( DefaultFilterInvocationDefinitionSource ) fsi . getObjectDefinitionSource ( ) ;
ConfigAttributeDefinition cad = fids . lookupAttributes ( loginPage , "POST" ) ;
if ( cad = = null ) {
logger . debug ( "No access attributes defined for login page URL" ) ;
if ( fsi . isRejectPublicInvocations ( ) ) {
logger . warn ( "FilterSecurityInterceptor is configured to reject public invocations." +
" Your login page may not be accessible." ) ;
}
return ;
}
if ( ! beanFactory . containsBean ( BeanIds . ANONYMOUS_PROCESSING_FILTER ) ) {
logger . warn ( "The login page is being protected by the filter chain, but you don't appear to have" +
" anonymous authentication enabled. This is almost certainly an error." ) ;
return ;
}
// Simulate an anonymous access with the supplied attributes.
AnonymousProcessingFilter anonPF = ( AnonymousProcessingFilter ) beanFactory . getBean ( BeanIds . ANONYMOUS_PROCESSING_FILTER ) ;
AnonymousAuthenticationToken token =
new AnonymousAuthenticationToken ( "key" , anonPF . getUserAttribute ( ) . getPassword ( ) ,
anonPF . getUserAttribute ( ) . getAuthorities ( ) ) ;
try {
fsi . getAccessDecisionManager ( ) . decide ( token , new Object ( ) , cad ) ;
} catch ( Exception e ) {
logger . warn ( "Anonymous access to the login page doesn't appear to be enabled. This is almost certainly " +
"an error. Please check your configuration allows unauthenticated access to the configured " +
"login page. (Simulated access was rejected: " + e + ")" ) ;
}
}
}
/ * *
* Returns the delegate filter of a wrapper , or the unchanged filter if it isn ' t wrapped .
* /