123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="session-mgmt"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <info>
- <title>Session Management</title>
- </info>
- <para>HTTP session related functonality is handled by a combination of the
- <classname>SessionManagementFilter</classname> and the
- <interfacename>SessionAuthenticationStrategy</interfacename> interface, which the filter
- delegates to. Typical usage includes session-fixation protection attack prevention,
- detection of session timeouts and restrictions on how many sessions an authenticated user
- may have open concurrently.</para>
- <section>
- <title>SessionManagementFilter</title>
- <para>The <classname>SessionManagementFilter</classname> checks the contents of the
- <interfacename>SecurityContextRepository</interfacename> against the current contents of
- the <classname>SecurityContextHolder</classname> to determine whether a user has been
- authenticated during the current request, typically by a non-interactive authentication
- mechanism, such as pre-authentication or remember-me <footnote>
- <para>Authentication by mechanisms which perform a redirect after authenticating (such
- as form-login) will not be detected by
- <classname>SessionManagementFilter</classname>, as the filter will not be invoked
- during the authenticating request. Session-management functionality has to be
- handled separately in these cases. </para>
- </footnote>. If the repository contains a security context, the filter does nothing. If
- it doesn't, and the thread-local <interfacename>SecurityContext</interfacename> contains
- a (non-anonymous) <interfacename>Authentication</interfacename> object, the filter
- assumes they have been authenticated by a previous filter in the stack. It will then
- invoke the configured
- <interfacename>SessionAuthenticationStrategy</interfacename>.</para>
- <para>If the user is not currently authenticated, the filter will check whether an invalid
- session ID has been requested (because of a timeout, for example) and will invoke the configured
- <interfacename>InvalidSessionStrategy</interfacename>, if one is set. The most common behaviour
- is just to redirect to a fixed URL and this is encapsulated in the standard implementation
- <classname>SimpleRedirectInvalidSessionStrategy</classname>. The latter is also used
- when configuring an invalid session URL through the namespace,
- <link xlink:href="#ns-session-mgmt">as described earlier</link>.</para>
- </section>
- <section>
- <title><interfacename>SessionAuthenticationStrategy</interfacename></title>
- <para> <interfacename>SessionAuthenticationStrategy</interfacename> is used by both
- <classname>SessionManagementFilter</classname> and
- <classname>AbstractAuthenticationProcessingFilter</classname>, so if you are using a
- 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 language="xml"><![CDATA[
- <http>
- <custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
- <session-management session-authentication-strategy-ref="sas"/>
- </http>
- <beans:bean id="myAuthFilter" class=
- "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
- <beans:property name="sessionAuthenticationStrategy" ref="sas" />
- ...
- </beans:bean>
- <beans:bean id="sas" class=
- "org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
- ]]></programlisting>
- Note that the use of the default, <classname>SessionFixationProtectionStrategy</classname>
- may cause issues if you are storing beans in the session which implement
- <interfacename>HttpSessionBindingListener</interfacename>, including Spring session-scoped
- beans. See the Javadoc for this class for more information.
- </para>
- </section>
- <section xml:id="concurrent-sessions">
- <title>Concurrency Control</title>
- <para>Spring Security is able to prevent a principal from concurrently authenticating to the
- same application more than a specified number of times. Many ISVs take advantage of this
- to enforce licensing, whilst network administrators like this feature because it helps
- prevent people from sharing login names. You can, for example, stop user
- <quote>Batman</quote> from logging onto the web application from two different sessions.
- You can either expire their previous login or you can report an error when they try to
- log in again, preventing the second login. Note that if you are using the second
- approach, a user who has not explicitly logged out (but who has just closed their
- browser, for example) will not be able to log in again until their original session
- expires.</para>
- <para>Concurrency control is supported by the namespace, so please check the earlier
- namespace chapter for the simplest configuration. Sometimes you need to customize things
- though. </para>
- <para>The implementation uses a specialized version of
- <interfacename>SessionAuthenticationStrategy</interfacename>, called
- <classname>ConcurrentSessionControlAuthenticationStrategy</classname>. <note>
- <para>Previously the concurrent authentication check was made by the
- <classname>ProviderManager</classname>, which could be injected with a
- <literal>ConcurrentSessionController</literal>. The latter would check if the user
- was attempting to exceed the number of permitted sessions. However, this approach
- required that an HTTP session be created in advance, which is undesirable. In Spring
- Security 3, the user is first authenticated by the
- <interfacename>AuthenticationManager</interfacename> and once they are successfully
- authenticated, a session is created and the check is made whether they are allowed
- to have another session open.</para>
- </note></para>
- <para>To use concurrent session support, you'll need to add the following to
- <literal>web.xml</literal>: <programlisting language="xml"><![CDATA[
- <listener>
- <listener-class>
- org.springframework.security.web.session.HttpSessionEventPublisher
- </listener-class>
- </listener> ]]>
- </programlisting></para>
- <para>In addition, you will need to add the <literal>ConcurrentSessionFilter</literal> to
- your <classname>FilterChainProxy</classname>. The
- <classname>ConcurrentSessionFilter</classname> requires two properties,
- <literal>sessionRegistry</literal>, which generally points to an instance of
- <classname>SessionRegistryImpl</classname>, and <literal>expiredUrl</literal>, which
- points to the page to display when a session has expired. A configuration using the
- namespace to create the <classname>FilterChainProxy</classname> and other default beans
- might look like this: <programlisting language="xml"><![CDATA[
- <http>
- <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
- <custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
- <session-management session-authentication-strategy-ref="sas"/>
- </http>
- <beans:bean id="concurrencyFilter"
- class="org.springframework.security.web.session.ConcurrentSessionFilter">
- <beans:property name="sessionRegistry" ref="sessionRegistry" />
- <beans:property name="expiredUrl" value="/session-expired.htm" />
- </beans:bean>
- <beans:bean id="myAuthFilter" class=
- "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
- <beans:property name="sessionAuthenticationStrategy" ref="sas" />
- <beans:property name="authenticationManager" ref="authenticationManager" />
- </beans:bean>
- <beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
- <beans:constructor-arg>
- <beans:list>
- <beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
- <beans:constructor-arg ref="sessionRegistry"/>
- <beans:property name="maximumSessions" value="1" />
- <beans:property name="exceptionIfMaximumExceeded" value="true" />
- </beans:bean>
- <beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
- </beans:bean>
- <beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
- <beans:constructor-arg ref="sessionRegistry"/>
- </beans:bean>
- </beans:list>
- </beans:constructor-arg>
- </beans:bean>
- <beans:bean id="sessionRegistry"
- class="org.springframework.security.core.session.SessionRegistryImpl" />
- ]]>
- </programlisting></para>
- <para>Adding the listener to <filename>web.xml</filename> causes an
- <literal>ApplicationEvent</literal> to be published to the Spring
- <literal>ApplicationContext</literal> every time a <literal>HttpSession</literal>
- commences or terminates. This is critical, as it allows the
- <classname>SessionRegistryImpl</classname> to be notified when a session ends. Without
- it, a user will never be able to log back in again once they have exceeded their session
- allowance, even if they log out of another session or it times out.</para>
- <section xml:id="list-authenticated-principals">
- <title>Querying the <interfacename>SessionRegistry</interfacename> for currently authenticated
- users and their sessions</title>
- <para>
- Setting up concurrency-control, either through the namespace or using plain beans has the
- useful side effect of providing you with a reference to the <interfacename>SessionRegistry</interfacename>
- which you can use directly within your application, so even if you don't want to restrict the
- number of sessions a user may have, it may be worth setting up the infrastructure anyway. You can
- set the <literal>maximumSession</literal> property to -1 to allow unlimited sessions. If
- you're using the namespace, you can set an alias for the internally-created
- <interfacename>SessionRegistry</interfacename> using the <literal>session-registry-alias</literal>
- attribute, providing a reference which you can inject into your own beans.</para>
- <para>
- The <methodname>getAllPrincipals()</methodname>
- method supplies you with a list of the currently authenticated users. You can list a user's
- sessions by calling the <methodname>getAllSessions(Object principal, boolean includeExpiredSessions)</methodname> method,
- which returns a list of <classname>SessionInformation</classname> objects. You can also
- expire a user's session by calling <methodname>expireNow()</methodname> on a
- <methodname>SessionInformation</methodname> instance. When the user returns to the application, they
- will be prevented from proceeding. You may find these methods useful in an administration
- application, for example. Have a look at the Javadoc for more information.
- </para>
- </section>
- </section>
- </chapter>
|