mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-28 23:02:15 +00:00
SEC-1850: Namespace adds all LogoutHandlers to ConcurrentSessionFilter
Previously the namespace configuration only populated ConcurrentSessionFilter with SecurityContextLogoutHandler. This means that there was an inconsistency with LogoutFilter. Now the namespace will configure the same LogoutHandlers as it would for LogoutFilter (i.e. RememberMeServices, SecurityContextLogoutHandler, and CookieClearingLogoutHandler.
This commit is contained in:
parent
06638db289
commit
3ce06333c5
@ -42,6 +42,7 @@ import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
|||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;
|
||||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
||||||
@ -116,6 +117,8 @@ final class AuthenticationConfigBuilder {
|
|||||||
private RootBeanDefinition preAuthEntryPoint;
|
private RootBeanDefinition preAuthEntryPoint;
|
||||||
|
|
||||||
private BeanDefinition logoutFilter;
|
private BeanDefinition logoutFilter;
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private ManagedList logoutHandlers;
|
||||||
private BeanDefinition loginPageGenerationFilter;
|
private BeanDefinition loginPageGenerationFilter;
|
||||||
private BeanDefinition etf;
|
private BeanDefinition etf;
|
||||||
private final BeanReference requestCache;
|
private final BeanReference requestCache;
|
||||||
@ -479,10 +482,23 @@ final class AuthenticationConfigBuilder {
|
|||||||
void createLogoutFilter() {
|
void createLogoutFilter() {
|
||||||
Element logoutElt = DomUtils.getChildElementByTagName(httpElt, Elements.LOGOUT);
|
Element logoutElt = DomUtils.getChildElementByTagName(httpElt, Elements.LOGOUT);
|
||||||
if (logoutElt != null || autoConfig) {
|
if (logoutElt != null || autoConfig) {
|
||||||
logoutFilter = new LogoutBeanDefinitionParser(rememberMeServicesId).parse(logoutElt, pc);
|
LogoutBeanDefinitionParser logoutParser = new LogoutBeanDefinitionParser(rememberMeServicesId);
|
||||||
|
logoutFilter = logoutParser.parse(logoutElt, pc);
|
||||||
|
logoutHandlers = logoutParser.getLogoutHandlers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
ManagedList getLogoutHandlers() {
|
||||||
|
if(logoutHandlers == null && rememberMeProviderRef != null) {
|
||||||
|
logoutHandlers = new ManagedList();
|
||||||
|
logoutHandlers.add(new RuntimeBeanReference(rememberMeServicesId));
|
||||||
|
logoutHandlers.add(new RootBeanDefinition(SecurityContextLogoutHandler.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return logoutHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
void createAnonymousFilter() {
|
void createAnonymousFilter() {
|
||||||
Element anonymousElt = DomUtils.getChildElementByTagName(httpElt, Elements.ANONYMOUS);
|
Element anonymousElt = DomUtils.getChildElementByTagName(httpElt, Elements.ANONYMOUS);
|
||||||
|
|
||||||
|
@ -147,6 +147,13 @@ class HttpConfigurationBuilder {
|
|||||||
createFilterSecurityInterceptor(authenticationManager);
|
createFilterSecurityInterceptor(authenticationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
void setLogoutHandlers(ManagedList logoutHandlers) {
|
||||||
|
if(logoutHandlers != null && concurrentSessionFilter != null) {
|
||||||
|
concurrentSessionFilter.getPropertyValues().add("logoutHandlers", logoutHandlers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Needed to account for placeholders
|
// Needed to account for placeholders
|
||||||
static String createPath(String path, boolean lowerCase) {
|
static String createPath(String path, boolean lowerCase) {
|
||||||
return lowerCase ? path.toLowerCase() : path;
|
return lowerCase ? path.toLowerCase() : path;
|
||||||
|
@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.springframework.security.config.http;
|
package org.springframework.security.config.http;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
@ -123,6 +138,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
|
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
|
||||||
httpBldr.getSessionStrategy(), portMapper, portResolver);
|
httpBldr.getSessionStrategy(), portMapper, portResolver);
|
||||||
|
|
||||||
|
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
|
||||||
|
|
||||||
authenticationProviders.addAll(authBldr.getProviders());
|
authenticationProviders.addAll(authBldr.getProviders());
|
||||||
|
|
||||||
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
|
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
|
||||||
|
@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.springframework.security.config.http;
|
package org.springframework.security.config.http;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
@ -29,6 +44,7 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
static final String ATT_DELETE_COOKIES = "delete-cookies";
|
static final String ATT_DELETE_COOKIES = "delete-cookies";
|
||||||
|
|
||||||
final String rememberMeServices;
|
final String rememberMeServices;
|
||||||
|
private ManagedList logoutHandlers = new ManagedList();
|
||||||
|
|
||||||
public LogoutBeanDefinitionParser(String rememberMeServices) {
|
public LogoutBeanDefinitionParser(String rememberMeServices) {
|
||||||
this.rememberMeServices = rememberMeServices;
|
this.rememberMeServices = rememberMeServices;
|
||||||
@ -75,24 +91,27 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
builder.addConstructorArgValue(logoutSuccessUrl);
|
builder.addConstructorArgValue(logoutSuccessUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagedList handlers = new ManagedList();
|
|
||||||
BeanDefinition sclh = new RootBeanDefinition(SecurityContextLogoutHandler.class);
|
BeanDefinition sclh = new RootBeanDefinition(SecurityContextLogoutHandler.class);
|
||||||
sclh.getPropertyValues().addPropertyValue("invalidateHttpSession", !"false".equals(invalidateSession));
|
sclh.getPropertyValues().addPropertyValue("invalidateHttpSession", !"false".equals(invalidateSession));
|
||||||
handlers.add(sclh);
|
logoutHandlers.add(sclh);
|
||||||
|
|
||||||
if (rememberMeServices != null) {
|
if (rememberMeServices != null) {
|
||||||
handlers.add(new RuntimeBeanReference(rememberMeServices));
|
logoutHandlers.add(new RuntimeBeanReference(rememberMeServices));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(deleteCookies)) {
|
if (StringUtils.hasText(deleteCookies)) {
|
||||||
BeanDefinition cookieDeleter = new RootBeanDefinition(CookieClearingLogoutHandler.class);
|
BeanDefinition cookieDeleter = new RootBeanDefinition(CookieClearingLogoutHandler.class);
|
||||||
String[] names = StringUtils.tokenizeToStringArray(deleteCookies, ",");
|
String[] names = StringUtils.tokenizeToStringArray(deleteCookies, ",");
|
||||||
cookieDeleter.getConstructorArgumentValues().addGenericArgumentValue(names);
|
cookieDeleter.getConstructorArgumentValues().addGenericArgumentValue(names);
|
||||||
handlers.add(cookieDeleter);
|
logoutHandlers.add(cookieDeleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.addConstructorArgValue(handlers);
|
builder.addConstructorArgValue(logoutHandlers);
|
||||||
|
|
||||||
return builder.getBeanDefinition();
|
return builder.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ManagedList getLogoutHandlers() {
|
||||||
|
return logoutHandlers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,12 @@ import org.springframework.security.core.context.SecurityContext
|
|||||||
import org.springframework.security.core.context.SecurityContextHolder
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
import org.springframework.security.core.session.SessionRegistryImpl
|
import org.springframework.security.core.session.SessionRegistryImpl
|
||||||
import org.springframework.security.util.FieldUtils
|
import org.springframework.security.util.FieldUtils
|
||||||
|
import org.springframework.security.web.authentication.RememberMeServices
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||||
|
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
|
||||||
|
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
|
||||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
|
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
|
||||||
import org.springframework.security.web.context.NullSecurityContextRepository
|
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||||
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper
|
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper
|
||||||
@ -93,6 +98,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def concurrentSessionSupportAddsFilterAndExpectedBeans() {
|
def concurrentSessionSupportAddsFilterAndExpectedBeans() {
|
||||||
|
when:
|
||||||
httpAutoConfig {
|
httpAutoConfig {
|
||||||
'session-management'() {
|
'session-management'() {
|
||||||
'concurrency-control'('session-registry-alias':'sr', 'expired-url': '/expired')
|
'concurrency-control'('session-registry-alias':'sr', 'expired-url': '/expired')
|
||||||
@ -100,13 +106,89 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
|
|||||||
}
|
}
|
||||||
createAppContext();
|
createAppContext();
|
||||||
List filters = getFilters("/someurl");
|
List filters = getFilters("/someurl");
|
||||||
|
def concurrentSessionFilter = filters.get(0)
|
||||||
|
|
||||||
expect:
|
then:
|
||||||
filters.get(0) instanceof ConcurrentSessionFilter
|
concurrentSessionFilter instanceof ConcurrentSessionFilter
|
||||||
filters.get(0).expiredUrl == '/expired'
|
concurrentSessionFilter.expiredUrl == '/expired'
|
||||||
appContext.getBean("sr") != null
|
appContext.getBean("sr") != null
|
||||||
getFilter(SessionManagementFilter.class) != null
|
getFilter(SessionManagementFilter.class) != null
|
||||||
sessionRegistryIsValid();
|
sessionRegistryIsValid();
|
||||||
|
|
||||||
|
concurrentSessionFilter.handlers.size() == 1
|
||||||
|
def logoutHandler = concurrentSessionFilter.handlers[0]
|
||||||
|
logoutHandler instanceof SecurityContextLogoutHandler
|
||||||
|
logoutHandler.invalidateHttpSession
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def 'concurrency-control adds custom logout handlers'() {
|
||||||
|
when: 'Custom logout and remember-me'
|
||||||
|
httpAutoConfig {
|
||||||
|
'session-management'() {
|
||||||
|
'concurrency-control'()
|
||||||
|
}
|
||||||
|
'logout'('invalidate-session': false, 'delete-cookies': 'testCookie')
|
||||||
|
'remember-me'()
|
||||||
|
}
|
||||||
|
createAppContext();
|
||||||
|
|
||||||
|
List filters = getFilters("/someurl")
|
||||||
|
ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
|
||||||
|
def logoutHandlers = concurrentSessionFilter.handlers
|
||||||
|
|
||||||
|
then: 'ConcurrentSessionFilter contains the customized LogoutHandlers'
|
||||||
|
logoutHandlers.size() == 3
|
||||||
|
def securityCtxlogoutHandler = logoutHandlers.find { it instanceof SecurityContextLogoutHandler }
|
||||||
|
securityCtxlogoutHandler.invalidateHttpSession == false
|
||||||
|
def cookieClearingLogoutHandler = logoutHandlers.find { it instanceof CookieClearingLogoutHandler }
|
||||||
|
cookieClearingLogoutHandler.cookiesToClear == ['testCookie']
|
||||||
|
def remembermeLogoutHandler = logoutHandlers.find { it instanceof RememberMeServices }
|
||||||
|
remembermeLogoutHandler == getFilter(RememberMeAuthenticationFilter.class).rememberMeServices
|
||||||
|
}
|
||||||
|
|
||||||
|
def 'concurrency-control with remember-me and no LogoutFilter contains SecurityContextLogoutHandler and RememberMeServices as LogoutHandlers'() {
|
||||||
|
when: 'RememberMe and No LogoutFilter'
|
||||||
|
xml.http(['entry-point-ref': 'entryPoint'], {
|
||||||
|
'session-management'() {
|
||||||
|
'concurrency-control'()
|
||||||
|
}
|
||||||
|
'remember-me'()
|
||||||
|
})
|
||||||
|
bean('entryPoint', 'org.springframework.security.web.authentication.Http403ForbiddenEntryPoint')
|
||||||
|
createAppContext()
|
||||||
|
|
||||||
|
List filters = getFilters("/someurl")
|
||||||
|
ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
|
||||||
|
def logoutHandlers = concurrentSessionFilter.handlers
|
||||||
|
|
||||||
|
then: 'SecurityContextLogoutHandler and RememberMeServices are in ConcurrentSessionFilter logoutHandlers'
|
||||||
|
!filters.find { it instanceof LogoutFilter }
|
||||||
|
logoutHandlers.size() == 2
|
||||||
|
def securityCtxlogoutHandler = logoutHandlers.find { it instanceof SecurityContextLogoutHandler }
|
||||||
|
securityCtxlogoutHandler.invalidateHttpSession == true
|
||||||
|
logoutHandlers.find { it instanceof RememberMeServices } == getFilter(RememberMeAuthenticationFilter).rememberMeServices
|
||||||
|
}
|
||||||
|
|
||||||
|
def 'concurrency-control with no remember-me or LogoutFilter contains SecurityContextLogoutHandler as LogoutHandlers'() {
|
||||||
|
when: 'No Logout Filter or RememberMe'
|
||||||
|
xml.http(['entry-point-ref': 'entryPoint'], {
|
||||||
|
'session-management'() {
|
||||||
|
'concurrency-control'()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
bean('entryPoint', 'org.springframework.security.web.authentication.Http403ForbiddenEntryPoint')
|
||||||
|
createAppContext()
|
||||||
|
|
||||||
|
List filters = getFilters("/someurl")
|
||||||
|
ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
|
||||||
|
def logoutHandlers = concurrentSessionFilter.handlers
|
||||||
|
|
||||||
|
then: 'Only SecurityContextLogoutHandler is found in ConcurrentSessionFilter logoutHandlers'
|
||||||
|
!filters.find { it instanceof LogoutFilter }
|
||||||
|
logoutHandlers.size() == 1
|
||||||
|
def securityCtxlogoutHandler = logoutHandlers.find { it instanceof SecurityContextLogoutHandler }
|
||||||
|
securityCtxlogoutHandler.invalidateHttpSession == true
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'concurrency-control handles default expired-url as null'() {
|
def 'concurrency-control handles default expired-url as null'() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user