mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 17:22:13 +00:00
SEC-1229: Refactored authentication.concurrent in core, moving classes into core.session
This commit is contained in:
parent
2b89ebdfbb
commit
acf13c74ca
@ -24,8 +24,8 @@ import org.springframework.beans.factory.xml.ParserContext;
|
|||||||
import org.springframework.security.access.vote.AffirmativeBased;
|
import org.springframework.security.access.vote.AffirmativeBased;
|
||||||
import org.springframework.security.access.vote.AuthenticatedVoter;
|
import org.springframework.security.access.vote.AuthenticatedVoter;
|
||||||
import org.springframework.security.access.vote.RoleVoter;
|
import org.springframework.security.access.vote.RoleVoter;
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
|
||||||
import org.springframework.security.config.Elements;
|
import org.springframework.security.config.Elements;
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||||
@ -42,7 +42,7 @@ import org.springframework.security.web.authentication.concurrent.ConcurrentSess
|
|||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionControlStrategy;
|
import org.springframework.security.web.session.ConcurrentSessionControlStrategy;
|
||||||
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
|
import org.springframework.security.web.session.SessionFixationProtectionStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.util.AntUrlPathMatcher;
|
import org.springframework.security.web.util.AntUrlPathMatcher;
|
||||||
import org.springframework.security.web.util.UrlMatcher;
|
import org.springframework.security.web.util.UrlMatcher;
|
||||||
@ -248,7 +248,7 @@ class HttpConfigurationBuilder {
|
|||||||
}
|
}
|
||||||
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)
|
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)
|
||||||
|| StringUtils.hasText(sessionAuthStratRef)) {
|
|| StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionStrategy.class);
|
||||||
} else {
|
} else {
|
||||||
sfpf = null;
|
sfpf = null;
|
||||||
return;
|
return;
|
||||||
|
@ -27,11 +27,11 @@ import org.springframework.security.access.ConfigAttribute;
|
|||||||
import org.springframework.security.access.SecurityConfig;
|
import org.springframework.security.access.SecurityConfig;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.PostProcessedMockUserDetailsService;
|
import org.springframework.security.config.PostProcessedMockUserDetailsService;
|
||||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||||
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.openid.OpenID4JavaConsumer;
|
import org.springframework.security.openid.OpenID4JavaConsumer;
|
||||||
import org.springframework.security.openid.OpenIDAttribute;
|
import org.springframework.security.openid.OpenIDAttribute;
|
||||||
import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
|
import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* 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.authentication.concurrent;
|
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides two methods that can be called by an {@link
|
|
||||||
* org.springframework.security.authentication.AuthenticationManager} to integrate with the
|
|
||||||
* concurrent session handling infrastructure.
|
|
||||||
*
|
|
||||||
* @author Ben Alex
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
|
||||||
public interface ConcurrentSessionController {
|
|
||||||
//~ Methods ========================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by any class that wishes to know whether the current authentication request should be permitted.
|
|
||||||
* Generally callers will be <code>AuthenticationManager</code>s before they authenticate, but could equally
|
|
||||||
* include <code>Filter</code>s or other interceptors that wish to confirm the ongoing validity of a previously
|
|
||||||
* authenticated <code>Authentication</code>.<p>The implementation should throw a suitable exception if the
|
|
||||||
* user has exceeded their maximum allowed concurrent sessions.</p>
|
|
||||||
*
|
|
||||||
* @param request the authentication request (never <code>null</code>)
|
|
||||||
*
|
|
||||||
* @throws AuthenticationException if the user has exceeded their maximum allowed current sessions
|
|
||||||
*/
|
|
||||||
void checkAuthenticationAllowed(Authentication request)
|
|
||||||
throws AuthenticationException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by an <code>AuthenticationManager</code> when the authentication was successful. An
|
|
||||||
* implementation is expected to register the authenticated user in some sort of registry, for future concurrent
|
|
||||||
* tracking via the {@link #checkAuthenticationAllowed(Authentication)} method.
|
|
||||||
*
|
|
||||||
* @param authentication the successfully authenticated user (never <code>null</code>)
|
|
||||||
*/
|
|
||||||
void registerSuccessfulAuthentication(Authentication authentication);
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
Concurrent session control and registration classes.
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented by {@link org.springframework.security.core.Authentication#getDetails()}
|
* Implemented by {@link org.springframework.security.core.Authentication#getDetails()}
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -28,11 +28,10 @@ import java.util.Set;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of {@link org.springframework.security.authentication.concurrent.SessionRegistry}
|
* Base implementation of {@link org.springframework.security.core.session.SessionRegistry}
|
||||||
* which also listens for {@link org.springframework.security.web.session.HttpSessionDestroyedEvent}s
|
* which also listens for {@link org.springframework.security.web.session.HttpSessionDestroyedEvent}s
|
||||||
* published in the Spring application context.
|
* published in the Spring application context.
|
||||||
*
|
*
|
@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
Session registry and other related classes.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -13,13 +13,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
import org.springframework.security.core.session.SessionInformation;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.authentication.concurrent;
|
package org.springframework.security.core.session;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@ -24,6 +24,8 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||||
|
import org.springframework.security.core.session.SessionInformation;
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link SessionRegistryImpl}.
|
* Tests {@link SessionRegistryImpl}.
|
@ -298,18 +298,17 @@
|
|||||||
<section xml:id="nsa-session-mgmt">
|
<section xml:id="nsa-session-mgmt">
|
||||||
<title>The <literal><session-management></literal> Element</title>
|
<title>The <literal><session-management></literal> Element</title>
|
||||||
<para>Session-management related functionality is implemented by the addition of a
|
<para>Session-management related functionality is implemented by the addition of a
|
||||||
<classname>SessionManagementFilter</classname> to the filter stack.</para>
|
<classname>SessionManagementFilter</classname> to the filter stack.</para>
|
||||||
<section xml:id="session-fixation-protection">
|
<section xml:id="session-fixation-protection">
|
||||||
<title><literal>session-fixation-protection</literal></title>
|
<title><literal>session-fixation-protection</literal></title>
|
||||||
<para> Indicates whether an existing session should be invalidated when a user authenticates
|
<para> Indicates whether an existing session should be invalidated when a user authenticates
|
||||||
and a new session started. If set to "none" no change will be made. "newSession" will
|
and a new session started. If set to "none" no change will be made. "newSession" will
|
||||||
create a new empty session. "migrateSession" will create a new session and copy the
|
create a new empty session. "migrateSession" will create a new session and copy the
|
||||||
session attributes to the new session. Defaults to "migrateSession".</para>
|
session attributes to the new session. Defaults to "migrateSession".</para>
|
||||||
<para>
|
<para> If session fixation protection is enabled, the
|
||||||
If session fixation protection is enabled, the <classname>SessionManagementFilter</classname>
|
<classname>SessionManagementFilter</classname> is inected with a appropriately
|
||||||
is inected with a appropriately configured <classname>DefaultSessionAuthenticationStrategy</classname>.
|
configured <classname>DefaultSessionAuthenticationStrategy</classname>. See the Javadoc
|
||||||
See the Javadoc for this class for more details.
|
for this class for more details. </para>
|
||||||
</para>
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="nsa-concurrent-session-control">
|
<section xml:id="nsa-concurrent-session-control">
|
||||||
@ -317,15 +316,15 @@
|
|||||||
<para> Adds support for concurrent session control, allowing limits to be placed on the number
|
<para> Adds support for concurrent session control, allowing limits to be placed on the number
|
||||||
of active sessions a user can have. A <classname>ConcurrentSessionFilter</classname> will be
|
of active sessions a user can have. A <classname>ConcurrentSessionFilter</classname> will be
|
||||||
created, and a <classname>ConcurrentSessionControlStrategy</classname> will be used with the
|
created, and a <classname>ConcurrentSessionControlStrategy</classname> will be used with the
|
||||||
<classname>SessionManagementFilter</classname>. If a <literal>form-login</literal>
|
<classname>SessionManagementFilter</classname>. If a <literal>form-login</literal> element
|
||||||
element has been declared, the strategy object will also be injected into the created
|
has been declared, the strategy object will also be injected into the created authentication
|
||||||
authentication filter. An instance of <interfacename>SessionRegistry</interfacename> (a
|
filter. An instance of <interfacename>SessionRegistry</interfacename> (a
|
||||||
<classname>SessionRegistryImpl</classname> instance unless the user wishes to use a custom
|
<classname>SessionRegistryImpl</classname> instance unless the user wishes to use a custom
|
||||||
bean) will be created for use by the strategy.</para>
|
bean) will be created for use by the strategy.</para>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal>max-sessions</literal> attribute</title>
|
<title>The <literal>max-sessions</literal> attribute</title>
|
||||||
<para>Maps to the <literal>maximumSessions</literal> property of
|
<para>Maps to the <literal>maximumSessions</literal> property of
|
||||||
<classname>ConcurrentSessionControllerImpl</classname>.</para>
|
<classname>ConcurrentSessionControlStrategy</classname>.</para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal>expired-url</literal> attribute</title>
|
<title>The <literal>expired-url</literal> attribute</title>
|
||||||
|
@ -14,14 +14,13 @@
|
|||||||
<para>The <classname>SessionManagementFilter</classname> checks the contents of the
|
<para>The <classname>SessionManagementFilter</classname> checks the contents of the
|
||||||
<interfacename>SecurityContextRepository</interfacename> against the current contents of the
|
<interfacename>SecurityContextRepository</interfacename> against the current contents of the
|
||||||
<classname>SecurityContextHolder</classname> to deterine whether a user has been
|
<classname>SecurityContextHolder</classname> to deterine whether a user has been
|
||||||
authenticated during the current request, typically by a non-interactive authentication mechanism, such
|
authenticated during the current request, typically by a non-interactive authentication
|
||||||
as pre-authentication or remember-me <footnote><para>Authentication by mechanisms which perform a redirect
|
mechanism, such as pre-authentication or remember-me <footnote><para>Authentication by
|
||||||
after authenticating (such as form-login) will not be detected by <classname>SessionManagementFilter</classname>,
|
mechanisms which perform a redirect after authenticating (such as form-login) will not be
|
||||||
as the filter will not be invoked during the authenticating request. Session-management functionality has to be
|
detected by <classname>SessionManagementFilter</classname>, as the filter will not be
|
||||||
handled separately in these cases.
|
invoked during the authenticating request. Session-management functionality has to be
|
||||||
</para></footnote>.
|
handled separately in these cases. </para></footnote>. If the repository contains a
|
||||||
If the repository contains a security context, the
|
security context, the filter does nothing. If it doesn't, and the thread-local
|
||||||
filter does nothing. If it doesn't, and the thread-local
|
|
||||||
<interfacename>SecurityContext</interfacename> contains a (non-anonymous)
|
<interfacename>SecurityContext</interfacename> contains a (non-anonymous)
|
||||||
<interfacename>Authentication</interfacename> object, the filter assumes they have been
|
<interfacename>Authentication</interfacename> object, the filter assumes they have been
|
||||||
authenticated by a previous filter in the stack. It will then invoke the configured
|
authenticated by a previous filter in the stack. It will then invoke the configured
|
||||||
@ -34,10 +33,11 @@
|
|||||||
<section>
|
<section>
|
||||||
<title><interfacename>SessionAuthenticationStrategy</interfacename></title>
|
<title><interfacename>SessionAuthenticationStrategy</interfacename></title>
|
||||||
<para>
|
<para>
|
||||||
<interfacename>SessionAuthenticationStrategy</interfacename> is used by both <classname>SessionManagementFilter</classname>
|
<interfacename>SessionAuthenticationStrategy</interfacename> is used by both
|
||||||
and <classname>AbstractAutheticationProcessingFilter</classname>, so if you are using a customized form-login class, for example, you will need to inject
|
<classname>SessionManagementFilter</classname> and
|
||||||
it into both of these. In this case, a typical configuration, combining the namespace and custom beans might look like
|
<classname>AbstractAutheticationProcessingFilter</classname>, so if you are using a
|
||||||
<programlisting><![CDATA[
|
customized form-login class, for example, you will need to inject it into both of these. In
|
||||||
|
this case, a typical configuration, combining the namespace and custom beans might look like this:<programlisting><![CDATA[
|
||||||
<http>
|
<http>
|
||||||
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||||
<session-management session-authentication-strategy-ref="sas"/>
|
<session-management session-authentication-strategy-ref="sas"/>
|
||||||
@ -55,9 +55,7 @@
|
|||||||
<beans:property name="maximumSessions" value="1" />
|
<beans:property name="maximumSessions" value="1" />
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
]]>
|
]]>
|
||||||
</programlisting>
|
</programlisting></para>
|
||||||
|
|
||||||
</para>
|
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="concurrent-sessions">
|
<section xml:id="concurrent-sessions">
|
||||||
<title>Concurrency Control</title>
|
<title>Concurrency Control</title>
|
||||||
@ -70,15 +68,15 @@
|
|||||||
for the simplest configuration. Sometimes you need to customize things though. </para>
|
for the simplest configuration. Sometimes you need to customize things though. </para>
|
||||||
<para>The implementation uses a specialized version of
|
<para>The implementation uses a specialized version of
|
||||||
<interfacename>SessionAuthenticationStrategy</interfacename>, called
|
<interfacename>SessionAuthenticationStrategy</interfacename>, called
|
||||||
<classname>ConcurrentSessionControlStrategy</classname>.
|
<classname>ConcurrentSessionControlStrategy</classname>. <note><para>Previously the
|
||||||
<note><para>Previously the concurrent authentication check was made by the
|
concurrent authentication check was made by the <classname>ProviderManager</classname>,
|
||||||
<classname>ProviderManager</classname>, which could be injected with a
|
which could be injected with a <literal>ConcurrentSessionController</literal>. The latter
|
||||||
<literal>ConcurrentSessionController</literal> which would check if the user was
|
would check if the user was attempting to exceed the number of permitted sessions.
|
||||||
attempting to exceed the number of sessions permitted. However, this approach required that
|
However, this approach required that an HTTP session be created in advance, which is
|
||||||
an HTTP session be created in advance, which is undesirable. In Spring Security 3, the user
|
undesirable. In Spring Security 3, the user is first authenticated by the
|
||||||
is first authenticated by the <interfacename>AuthenticationManager</interfacename> and once
|
<interfacename>AuthenticationManager</interfacename> and once they are successfully
|
||||||
they are successfully authenticated, a session is created and the check is made whether they
|
authenticated, a session is created and the check is made whether they are allowed to have
|
||||||
are allowed to have another session open.</para></note></para>
|
another session open.</para></note></para>
|
||||||
<para>To use concurrent session support, you'll need to add the following to
|
<para>To use concurrent session support, you'll need to add the following to
|
||||||
<literal>web.xml</literal>: <programlisting><![CDATA[
|
<literal>web.xml</literal>: <programlisting><![CDATA[
|
||||||
<listener>
|
<listener>
|
||||||
@ -87,15 +85,13 @@
|
|||||||
</listener-class>
|
</listener-class>
|
||||||
</listener> ]]>
|
</listener> ]]>
|
||||||
</programlisting></para>
|
</programlisting></para>
|
||||||
<para>In addition, you will need to add the
|
<para>In addition, you will need to add the <literal>ConcurrentSessionFilter</literal> to your
|
||||||
<literal>org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter</literal>
|
<classname>FilterChainProxy</classname>. The <classname>ConcurrentSessionFilter</classname>
|
||||||
to your <classname>FilterChainProxy</classname>. The
|
requires two properties, <literal>sessionRegistry</literal>, which generally points to an
|
||||||
<classname>ConcurrentSessionFilter</classname> requires two properties,
|
instance of <literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which
|
||||||
<literal>sessionRegistry</literal>, which generally points to an instance of
|
points to the page to display when a session has expired. A configuration using the namespace
|
||||||
<literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which points to
|
to create the <classname>FilterChainProxy</classname> and other default beans might look like
|
||||||
the page to display when a session has expired. A configuration using the namespace to create the
|
this: <programlisting><![CDATA[
|
||||||
<classname>FilterChainProxy</classname> and other default beans might look like this:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
<http>
|
<http>
|
||||||
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
|
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
|
||||||
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||||
@ -104,7 +100,7 @@
|
|||||||
</http>
|
</http>
|
||||||
|
|
||||||
<beans:bean id="concurrencyFilter"
|
<beans:bean id="concurrencyFilter"
|
||||||
class="org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter">
|
class="org.springframework.security.web.session.ConcurrentSessionFilter">
|
||||||
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
||||||
<beans:property name="expiredUrl" value="/session-expired.htm" />
|
<beans:property name="expiredUrl" value="/session-expired.htm" />
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
@ -121,15 +117,15 @@
|
|||||||
<beans:property name="maximumSessions" value="1" />
|
<beans:property name="maximumSessions" value="1" />
|
||||||
</beans:bean>
|
</beans:bean>
|
||||||
|
|
||||||
<beans:bean id="sessionRegistry" class="org.springframework.security.authentication.concurrent.SessionRegistryImpl" />
|
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
|
||||||
]]>
|
]]>
|
||||||
</programlisting>
|
</programlisting></para>
|
||||||
</para>
|
<para>Adding the listener to <filename>web.xml</filename> causes an
|
||||||
<para>Adding the listener to <filename>web.xml</filename> causes an <literal>ApplicationEvent</literal> to
|
<literal>ApplicationEvent</literal> to be published to the Spring
|
||||||
be published to the Spring <literal>ApplicationContext</literal> every time a
|
<literal>ApplicationContext</literal> every time a <literal>HttpSession</literal> commences
|
||||||
<literal>HttpSession</literal> commences or terminates. This is critical, as it allows the
|
or terminates. This is critical, as it allows the <classname>SessionRegistryImpl</classname>
|
||||||
<classname>SessionRegistryImpl</classname> to be notified when a session ends. Without it, a user
|
to be notified when a session ends. Without it, a user will never be able to log back in again
|
||||||
will never be able to log back in again once they have exceeded their session allowance, even if they log out
|
once they have exceeded their session allowance, even if they log out of another session or it
|
||||||
of another session or it times out.</para>
|
times out.</para>
|
||||||
</section>
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
@ -5,8 +5,8 @@ import org.junit.runner.RunWith;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
@ -31,12 +31,12 @@
|
|||||||
<property name="authenticationManager" ref="authenticationManager"/>
|
<property name="authenticationManager" ref="authenticationManager"/>
|
||||||
<property name="accessDecisionManager" ref="accessDecisionManager"/>
|
<property name="accessDecisionManager" ref="accessDecisionManager"/>
|
||||||
<property name="securityMetadataSource"><value>
|
<property name="securityMetadataSource"><value>
|
||||||
org.springframework.security.authentication.concurrent.SessionRegistry.get*=ROLE_C
|
org.springframework.security.core.session.SessionRegistry.get*=ROLE_C
|
||||||
</value></property>
|
</value></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="httpRemoteService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
<bean id="httpRemoteService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||||
<property name="proxyInterfaces" value="org.springframework.security.authentication.concurrent.SessionRegistry"/>
|
<property name="proxyInterfaces" value="org.springframework.security.core.session.SessionRegistry"/>
|
||||||
<property name="interceptorNames">
|
<property name="interceptorNames">
|
||||||
<list>
|
<list>
|
||||||
<value>securityInterceptor</value>
|
<value>securityInterceptor</value>
|
||||||
|
@ -37,8 +37,8 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
|
||||||
import org.springframework.security.web.session.NullAuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.NullAuthenticatedSessionStrategy;
|
||||||
|
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||||
import org.springframework.security.web.util.UrlUtils;
|
import org.springframework.security.web.util.UrlUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.filter.GenericFilterBean;
|
import org.springframework.web.filter.GenericFilterBean;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.authentication;
|
package org.springframework.security.web.authentication;
|
||||||
|
|
||||||
import org.springframework.security.authentication.concurrent.SessionIdentifierAware;
|
import org.springframework.security.core.session.SessionIdentifierAware;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.session.SessionInformation;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||||
import org.springframework.security.web.RedirectStrategy;
|
import org.springframework.security.web.RedirectStrategy;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
@ -42,9 +42,9 @@ import org.springframework.web.filter.GenericFilterBean;
|
|||||||
* Filter required by concurrent session handling package.
|
* Filter required by concurrent session handling package.
|
||||||
* <p>
|
* <p>
|
||||||
* This filter performs two functions. First, it calls
|
* This filter performs two functions. First, it calls
|
||||||
* {@link org.springframework.security.authentication.concurrent.SessionRegistry#refreshLastRequest(String)} for each request
|
* {@link org.springframework.security.core.session.SessionRegistry#refreshLastRequest(String)} for each request
|
||||||
* so that registered sessions always have a correct "last update" date/time. Second, it retrieves a
|
* so that registered sessions always have a correct "last update" date/time. Second, it retrieves a
|
||||||
* {@link org.springframework.security.authentication.concurrent.SessionInformation} from the <code>SessionRegistry</code>
|
* {@link org.springframework.security.core.session.SessionInformation} from the <code>SessionRegistry</code>
|
||||||
* for each request and checks if the session has been marked as expired.
|
* for each request and checks if the session has been marked as expired.
|
||||||
* If it has been marked as expired, the configured logout handlers will be called (as happens with
|
* If it has been marked as expired, the configured logout handlers will be called (as happens with
|
||||||
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically to invalidate the session.
|
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically to invalidate the session.
|
||||||
|
@ -9,20 +9,36 @@ import javax.servlet.http.HttpSession;
|
|||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.MessageSourceAware;
|
import org.springframework.context.MessageSourceAware;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
|
import org.springframework.security.core.session.SessionInformation;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
|
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
|
||||||
|
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Strategy which handles concurrent session-control, in addition to the functionality provided by the base class.
|
||||||
|
*
|
||||||
|
* When invoked following an authentication, it will check whether the user in question should be allowed to proceed,
|
||||||
|
* by comparing the number of sessions they already have active with the configured <tt>maximumSessions</tt> value.
|
||||||
|
* The {@link SessionRegistry} is used as the source of data on authenticated users and session data.
|
||||||
|
* <p>
|
||||||
|
* If a user has reached the maximum number of permitted sessions, the behaviour depends on the
|
||||||
|
* <tt>exceptionIfMaxExceeded</tt> property. The default behaviour is to expired the least recently used session, which
|
||||||
|
* will be invalidated by the {@link ConcurrentSessionFilter} if accessed again. If <tt>exceptionIfMaxExceeded</tt> is
|
||||||
|
* set to <tt>true</tt>, however, the user will be prevented from starting a new authenticated session.
|
||||||
|
* <p>
|
||||||
|
* This strategy can be injected into both the {@link SessionManagementFilter} and instances of
|
||||||
|
* {@link AbstractAuthenticationProcessingFilter} (typically {@link UsernamePasswordAuthenticationProcessingFilter}).
|
||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticationStrategy
|
public class ConcurrentSessionControlStrategy extends SessionFixationProtectionStrategy
|
||||||
implements MessageSourceAware {
|
implements MessageSourceAware {
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
private final SessionRegistry sessionRegistry;
|
private final SessionRegistry sessionRegistry;
|
||||||
@ -106,7 +122,7 @@ public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticati
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
|
protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
|
||||||
SessionRegistry registry) {
|
SessionRegistry registry) throws SessionAuthenticationException {
|
||||||
if (exceptionIfMaximumExceeded || (sessions == null)) {
|
if (exceptionIfMaximumExceeded || (sessions == null)) {
|
||||||
throw new SessionAuthenticationException(messages.getMessage("ConcurrentSessionControllerImpl.exceededAllowed",
|
throw new SessionAuthenticationException(messages.getMessage("ConcurrentSessionControllerImpl.exceededAllowed",
|
||||||
new Object[] {new Integer(allowableSessions)},
|
new Object[] {new Integer(allowableSessions)},
|
||||||
@ -133,10 +149,22 @@ public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticati
|
|||||||
sessionRegistry.registerNewSession(newSession.getId(), auth.getPrincipal());
|
sessionRegistry.registerNewSession(newSession.getId(), auth.getPrincipal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>exceptionIfMaximumExceeded</tt> property, which determines whether the user should be prevented
|
||||||
|
* from opening more sessions than allowed. If set to <tt>true</tt>, a <tt>SessionAuthenticationException</tt>
|
||||||
|
* will be raised.
|
||||||
|
*
|
||||||
|
* @param exceptionIfMaximumExceeded defaults to <tt>false</tt>.
|
||||||
|
*/
|
||||||
public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {
|
public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {
|
||||||
this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
|
this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the <tt>maxSessions</tt> property. The default value is 1. Use -1 for unlimited sessions.
|
||||||
|
*
|
||||||
|
* @param maximumSessions the maximimum number of permitted sessions a user can have open simultaneously.
|
||||||
|
*/
|
||||||
public void setMaximumSessions(int maximumSessions) {
|
public void setMaximumSessions(int maximumSessions) {
|
||||||
Assert.isTrue(maximumSessions != 0,
|
Assert.isTrue(maximumSessions != 0,
|
||||||
"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
|
"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
|
||||||
|
@ -32,7 +32,7 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
|||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class DefaultSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
|
public class SessionFixationProtectionStrategy implements SessionAuthenticationStrategy {
|
||||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||||
|
|
||||||
/**
|
/**
|
@ -39,7 +39,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
|||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private final SecurityContextRepository securityContextRepository;
|
private final SecurityContextRepository securityContextRepository;
|
||||||
private SessionAuthenticationStrategy sessionStrategy = new DefaultSessionAuthenticationStrategy();
|
private SessionAuthenticationStrategy sessionStrategy = new SessionFixationProtectionStrategy();
|
||||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||||
private String invalidSessionUrl;
|
private String invalidSessionUrl;
|
||||||
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
|
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||||
@ -96,7 +96,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
|||||||
* Sets the strategy object which handles the session management behaviour when a
|
* Sets the strategy object which handles the session management behaviour when a
|
||||||
* user has been authenticated during the current request.
|
* user has been authenticated during the current request.
|
||||||
*
|
*
|
||||||
* @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used.
|
* @param sessionStrategy the strategy object. If not set, a {@link SessionFixationProtectionStrategy} is used.
|
||||||
*/
|
*/
|
||||||
public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||||
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
||||||
|
@ -13,15 +13,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.web.authentication;
|
package org.springframework.security.web.concurrent;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import org.springframework.mock.web.MockFilterConfig;
|
import org.springframework.mock.web.MockFilterConfig;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockHttpSession;
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
@ -21,7 +21,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception {
|
public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception {
|
||||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
||||||
@ -31,7 +31,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception {
|
public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception {
|
||||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
String sessionId = request.getSession().getId();
|
String sessionId = request.getSession().getId();
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
|||||||
// See SEC-1077
|
// See SEC-1077
|
||||||
@Test
|
@Test
|
||||||
public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception {
|
public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception {
|
||||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||||
strategy.setMigrateSessionAttributes(false);
|
strategy.setMigrateSessionAttributes(false);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
@ -58,7 +58,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception {
|
public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception {
|
||||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||||
strategy.setAlwaysCreateSession(true);
|
strategy.setAlwaysCreateSession(true);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user