remember-me-authentication.xml 11 KB

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