remember-me-authentication.xml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="remember-me"
  2. xmlns:xlink="http://www.w3.org/1999/xlink">
  3. <info>
  4. <title>Remember-Me Authentication</title>
  5. </info>
  6. <section xml:id="remember-me-overview">
  7. <info>
  8. <title>Overview</title>
  9. </info>
  10. <para>Remember-me or persistent-login authentication refers to web sites being able to
  11. remember the identity of a principal between sessions. This is typically accomplished by
  12. sending a cookie to the browser, with the cookie being detected during future sessions
  13. and causing automated login to take place. Spring Security provides the necessary hooks
  14. for these operations to take place, and has two concrete remember-me implementations.
  15. One uses hashing to preserve the security of cookie-based tokens and the other uses a
  16. database or other persistent storage mechanism to store the generated tokens. </para>
  17. <para> Note that both implemementations require a
  18. <interfacename>UserDetailsService</interfacename>. If you are using an authentication
  19. provider which doesn't use a <interfacename>UserDetailsService</interfacename> (for
  20. example, the LDAP provider) then it won't work unless you also have a
  21. <interfacename>UserDetailsService</interfacename> bean in your application context.
  22. </para>
  23. </section>
  24. <section xml:id="remember-me-hash-token">
  25. <title>Simple Hash-Based Token Approach</title>
  26. <para>This approach uses hashing to achieve a useful remember-me strategy. In essence a
  27. cookie is sent to the browser upon successful interactive authentication, with the
  28. cookie being composed as follows:
  29. <programlisting language="txt">
  30. base64(username + ":" + expirationTime + ":" +
  31. md5Hex(username + ":" + expirationTime + ":" password + ":" + key))
  32. username: As identifiable to the <interfacename>UserDetailsService</interfacename>
  33. password: That matches the one in the retrieved UserDetails
  34. expirationTime: The date and time when the remember-me token expires,
  35. expressed in milliseconds
  36. key: A private key to prevent modification of the remember-me token
  37. </programlisting></para>
  38. <para>As such the remember-me token is valid only for the period specified, and provided
  39. that the username, password and key does not change. Notably, this has a potential
  40. security issue in that a captured remember-me token will be usable from any user agent
  41. until such time as the token expires. This is the same issue as with digest
  42. authentication. If a principal is aware a token has been captured, they can easily
  43. change their password and immediately invalidate all remember-me tokens on issue. If
  44. more significant security is needed you should use the approach described in the next
  45. section. Alternatively remember-me services should simply not be used at all.</para>
  46. <para>If you are familiar with the topics discussed in the chapter on <link
  47. linkend="ns-config">namespace configuration</link>, you can enable remember-me
  48. authentication just by adding the <literal>&lt;remember-me&gt;</literal> element: <programlisting language="xml"><![CDATA[
  49. <http>
  50. ...
  51. <remember-me key="myAppKey"/>
  52. </http>
  53. ]]>
  54. </programlisting> The <interfacename>UserDetailsService</interfacename> will
  55. normally be selected automatically. If you have more than one in your application
  56. context, you need to specify which one should be used with the
  57. <literal>user-service-ref</literal> attribute, where the value is the name of your
  58. <interfacename>UserDetailsService</interfacename> bean. </para>
  59. </section>
  60. <section xml:id="remember-me-persistent-token">
  61. <title>Persistent Token Approach</title>
  62. <para>This approach is based on the article <link
  63. xlink:href="http://jaspan.com/improved_persistent_login_cookie_best_practice"
  64. >http://jaspan.com/improved_persistent_login_cookie_best_practice</link> with some minor
  65. modifications <footnote>
  66. <para>Essentially, the username is not included in the cookie, to prevent exposing a
  67. valid login name unecessarily. There is a discussion on this in the comments section
  68. of this article.</para>
  69. </footnote>. To use the this approach with namespace configuration, you would supply a
  70. datasource reference: <programlisting language="xml"><![CDATA[
  71. <http>
  72. ...
  73. <remember-me data-source-ref="someDataSource"/>
  74. </http>
  75. ]]>
  76. </programlisting> The database should contain a
  77. <literal>persistent_logins</literal> table, created using the following SQL (or
  78. equivalent):
  79. <programlisting language="ddl">
  80. create table persistent_logins (username varchar(64) not null,
  81. series varchar(64) primary key,
  82. token varchar(64) not null,
  83. last_used timestamp not null)
  84. </programlisting></para>
  85. <!-- TODO: Add more info on the implementation and behaviour when tokens are stolen etc. Also some info for admins on invalidating tokens using key, or deleting info from db -->
  86. </section>
  87. <section xml:id="remember-me-impls">
  88. <info>
  89. <title>Remember-Me Interfaces and Implementations</title>
  90. </info>
  91. <para>Remember-me authentication is not used with basic authentication, given it is often
  92. not used with <literal>HttpSession</literal>s. Remember-me is used with
  93. <literal>UsernamePasswordAuthenticationFilter</literal>, and is implemented via hooks in
  94. the <literal>AbstractAuthenticationProcessingFilter</literal> superclass. The hooks will
  95. invoke a concrete <interfacename>RememberMeServices</interfacename> at the appropriate
  96. times. The interface looks like this:
  97. <programlisting language="java">
  98. Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
  99. void loginFail(HttpServletRequest request, HttpServletResponse response);
  100. void loginSuccess(HttpServletRequest request, HttpServletResponse response,
  101. Authentication successfulAuthentication);
  102. </programlisting>
  103. Please refer to the JavaDocs for a fuller discussion on what the methods do, although
  104. note at this stage that <literal>AbstractAuthenticationProcessingFilter</literal> only
  105. calls the <literal>loginFail()</literal> and <literal>loginSuccess()</literal> methods.
  106. The <literal>autoLogin()</literal> method is called by
  107. <classname>RememberMeAuthenticationFilter</classname> whenever the
  108. <classname>SecurityContextHolder</classname> does not contain an
  109. <interfacename>Authentication</interfacename>. This interface therefore provides the
  110. underlying remember-me implementation with sufficient notification of
  111. authentication-related events, and delegates to the implementation whenever a candidate
  112. web request might contain a cookie and wish to be remembered. This design allows any
  113. number of remember-me implementation strategies. We've seen above that Spring Security
  114. provides two implementations. We'll look at these in turn.</para>
  115. <section>
  116. <title>TokenBasedRememberMeServices</title>
  117. <para> This implementation supports the simpler approach described in <xref
  118. linkend="remember-me-hash-token"/>.
  119. <classname>TokenBasedRememberMeServices</classname> generates a
  120. <literal>RememberMeAuthenticationToken</literal>, which is processed by
  121. <literal>RememberMeAuthenticationProvider</literal>. A <literal>key</literal> is
  122. shared between this authentication provider and the
  123. <literal>TokenBasedRememberMeServices</literal>. In addition,
  124. <literal>TokenBasedRememberMeServices</literal> requires A UserDetailsService from
  125. which it can retrieve the username and password for signature comparison purposes,
  126. and generate the <literal>RememberMeAuthenticationToken</literal> to contain the
  127. correct <interfacename>GrantedAuthority</interfacename>s. Some sort of logout
  128. command should be provided by the application that invalidates the cookie if the
  129. user requests this. <classname>TokenBasedRememberMeServices</classname> also
  130. implements Spring Security's <interfacename>LogoutHandler</interfacename> interface
  131. so can be used with <classname>LogoutFilter</classname> to have the cookie cleared
  132. automatically. </para>
  133. <para>The beans required in an application context to enable remember-me services are as
  134. follows: <programlisting language="xml"><![CDATA[
  135. <bean id="rememberMeFilter" class=
  136. "org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
  137. <property name="rememberMeServices" ref="rememberMeServices"/>
  138. <property name="authenticationManager" ref="theAuthenticationManager" />
  139. </bean>
  140. <bean id="rememberMeServices" class=
  141. "org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
  142. <property name="userDetailsService" ref="myUserDetailsService"/>
  143. <property name="key" value="springRocks"/>
  144. </bean>
  145. <bean id="rememberMeAuthenticationProvider" class=
  146. "org.springframework.security.authentication.rememberme.RememberMeAuthenticationProvider">
  147. <property name="key" value="springRocks"/>
  148. </bean>
  149. ]]>
  150. </programlisting>Don't forget to add your
  151. <interfacename>RememberMeServices</interfacename> implementation to your
  152. <literal>UsernamePasswordAuthenticationFilter.setRememberMeServices()</literal>
  153. property, include the <literal>RememberMeAuthenticationProvider</literal> in your
  154. <literal>AuthenticationManager.setProviders()</literal> list, and add
  155. <classname>RememberMeAuthenticationFilter</classname> into your
  156. <classname>FilterChainProxy</classname> (typically immediately after your
  157. <literal>UsernamePasswordAuthenticationFilter</literal>).</para>
  158. </section>
  159. <section>
  160. <title>PersistentTokenBasedRememberMeServices</title>
  161. <para> This class can be used in the same way as
  162. <classname>TokenBasedRememberMeServices</classname>, but it additionally needs to be
  163. configured with a <interfacename>PersistentTokenRepository</interfacename> to store
  164. the tokens. There are two standard implementations. <itemizedlist>
  165. <listitem>
  166. <para><classname>InMemoryTokenRepositoryImpl</classname> which is intended for
  167. testing only.</para>
  168. </listitem>
  169. <listitem>
  170. <para><classname>JdbcTokenRepositoryImpl</classname> which stores the tokens in
  171. a database. </para>
  172. </listitem>
  173. </itemizedlist> The database schema is described above in <xref
  174. linkend="remember-me-persistent-token"/>. </para>
  175. </section>
  176. </section>
  177. </chapter>