Files
spring-security/src/docbkx/remember-me-authentication.xml
2008-04-05 11:57:29 +00:00

118 lines
6.9 KiB
XML

<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="remember-me"><info><title>Remember-Me Authentication</title></info>
<section xml:id="remember-me-overview">
<info><title>Overview</title></info>
<para>Remember-me authentication refers to web sites being able to
remember the identity of a principal between sessions. This is
typically accomplished by sending a cookie to the browser, with the
cookie being detected during future sessions and causing automated
login to take place. Spring Security provides the necessary hooks so
that such operations can take place, along with providing a concrete
implementation that uses hashing to preserve the security of
cookie-based tokens.</para>
</section>
<section xml:id="remember-me-config"><info><title>Configuration</title></info>
<para>Remember-me authentication is not used with basic
authentication, given it is often not used with
<literal>HttpSession</literal>s. Remember-me is used with
<literal>AuthenticationProcessingFilter</literal>, and is implemented
via hooks in the <literal>AbstractProcessingFilter</literal>
superclass. The hooks will invoke a concrete
<literal>RememberMeServices</literal> at the appropriate times. The
interface looks like this:</para>
<para><programlisting>public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
public void loginFail(HttpServletRequest request, HttpServletResponse response);
public void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication);</programlisting></para>
<para>Please refer to JavaDocs for a fuller discussion on what the
methods do, although note at this stage
<literal>AbstractProcessingFilter</literal> only calls the
<literal>loginFail()</literal> and <literal>loginSuccess()</literal>
methods. The <literal>autoLogin()</literal> method is called by
<literal>RememberMeProcessingFilter</literal> whenever the
<literal>SecurityContextHolder</literal> does not contain an
<literal>Authentication</literal>. This interface therefore provides
the underlaying remember-me implementation with sufficient
notification of authentication-related events, and delegates to the
implementation whenever a candidate web request might contain a cookie
and wish to be remembered.</para>
<para>This design allows any number of remember-me implementation
strategies. In the interests of simplicity and avoiding the need for
DAO implementations that specify write and create methods, Acegi
Security's only concrete implementation,
<literal>TokenBasedRememberMeServices</literal>, uses hashing to
achieve a useful remember-me strategy. In essence a cookie is sent to
the browser upon successful interactive authentication, with that
cookie being composed as follows:</para>
<para><programlisting>base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" password + ":" + key))
username: As identifiable to TokenBasedRememberMeServices.getUserDetailsService()
password: That matches the relevant UserDetails retrieved from TokenBasedRememberMeServices.getUserDetailsService()
expirationTime: The date and time when the remember-me token expires, expressed in milliseconds
key: A private key to prevent modification of the remember-me token
</programlisting></para>
<para>As such the remember-me token is valid only for the period
specified, and provided that the username, password and key does not
change. Notably, this has a potential security issue in that a
captured remember-me token will be usable from any user agent until
such time as the token expires. This is the same issue as with digest
authentication. If a principal is aware a token has been captured,
they can easily change their password and immediately invalidate all
remember-me tokens on issue. However, if more significant security is
needed a rolling token approach should be used (this would require a
database) or remember-me services should simply not be used.</para>
<para><literal>TokenBasedRememberMeServices</literal> generates a
<literal>RememberMeAuthenticationToken</literal>, which is processed
by <literal>RememberMeAuthenticationProvider</literal>. A
<literal>key</literal> is shared between this authentication provider
and the <literal>TokenBasedRememberMeServices</literal>. In addition,
<literal>TokenBasedRememberMeServices</literal> requires A
UserDetailsService from which it can retrieve the username and
password for signature comparison purposes, and generate the
<literal>RememberMeAuthenticationToken</literal> to contain the
correct <literal>GrantedAuthority</literal>[]s. Some sort of logout
command should be provided by the application (typically via a JSP)
that invalidates the cookie upon user request. See the Contacts Sample
application's <literal>logout.jsp</literal> for an example.</para>
<para>The beans required in an application context to enable
remember-me services are as follows:</para>
<para><programlisting>
&lt;bean id="rememberMeProcessingFilter"
class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter"&gt;
&lt;property name="rememberMeServices"&gt;&lt;ref local="rememberMeServices"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices"&gt;
&lt;property name="userDetailsService"&gt;&lt;ref local="jdbcDaoImpl"/&gt;&lt;/property&gt;
&lt;property name="key"&gt;&lt;value&gt;springRocks&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider"&gt;
&lt;property name="key"&gt;&lt;value&gt;springRocks&lt;/value&gt;&lt;/property&gt;
&lt;/bean&gt;
</programlisting>Don't forget to add your
<literal>RememberMeServices</literal> implementation to your
<literal>AuthenticationProcessingFilter.setRememberMeServices()</literal>
property, include the
<literal>RememberMeAuthenticationProvider</literal> in your
<literal>AuthenticationManager.setProviders()</literal> list, and add
a call to <literal>RememberMeProcessingFilter</literal> into your
<literal>FilterChainProxy</literal> (typically immediately after your
<literal>AuthenticationProcessingFilter</literal>)</para>
</section>
</chapter>