SEC-1734: AbstractRememberMeServices will now default to using a secure cookie if the connection is secure. The behaviour can be overridden by setting the useSecureCookie property in which case the cookie will either always be secure (true) or never (false).

This commit is contained in:
Luke Taylor 2011-05-09 13:36:23 +01:00
parent 396eced291
commit 6e91786f92
5 changed files with 43 additions and 18 deletions

View File

@ -537,7 +537,7 @@ remember-me.attlist &=
attribute services-alias {xsd:token}?
remember-me.attlist &=
## Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false.
## Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.
attribute use-secure-cookie {xsd:boolean}?
remember-me.attlist &=

View File

@ -1172,7 +1172,7 @@
</xs:attribute>
<xs:attribute name="use-secure-cookie" type="xs:boolean">
<xs:annotation>
<xs:documentation>Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false.</xs:documentation>
<xs:documentation>Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="token-validity-seconds" type="xs:integer">

View File

@ -72,9 +72,9 @@
</section>
<section xml:id="nsa-jaas-api-provision">
<title><literal>jaas-api-provision</literal></title>
<para>If available, runs the request as the <literal>Subject</literal> acquired from
the <classname>JaasAuthenticationToken</classname> which is implemented by
adding a <classname>JaasApiIntegrationFilter</classname> bean to the stack.
<para>If available, runs the request as the <literal>Subject</literal> acquired from
the <classname>JaasAuthenticationToken</classname> which is implemented by
adding a <classname>JaasApiIntegrationFilter</classname> bean to the stack.
Defaults to "false".</para>
</section>
<section xml:id="nsa-path-type">
@ -334,7 +334,7 @@
<para> Allows complete control of the
<interfacename>RememberMeServices</interfacename> implementation that will be
used by the filter. The value should be the <literal>id</literal> of a bean in the application
context which implements this interface. Should also implement
context which implements this interface. Should also implement
<interfacename>LogoutHandler</interfacename> if a logout filter is in use.</para>
</section>
<section>
@ -369,6 +369,17 @@
and used automatically by the namespace configuration. If there are multiple
instances, you can specify a bean <literal>id</literal> explicitly using this attribute. </para>
</section>
<section>
<title><literal>use-secure-cookie</literal></title>
<para>It is recommended that remember-me cookies are only submitted over HTTPS and thus should
be flagged as <quote>secure</quote>. By default, a secure cookie will be used if the
connection over which the login request is made is secure (as it should be).
If you set this property to <literal>false</literal>, secure cookies will not be used.
Setting it to <literal>true</literal> will always set the secure flag on the cookie.
This attribute maps to the <literal>useSecureCookie</literal> property of
<classname>AbstractRememberMeServices</classname>.
</para>
</section>
<section>
<title><literal>authentication-success-handler-ref</literal></title>
<para>Sets the <code>authenticationSuccessHandler</code> property on the

View File

@ -56,7 +56,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
private boolean alwaysRemember;
private String key;
private int tokenValiditySeconds = TWO_WEEKS_S;
private boolean useSecureCookie = false;
private Boolean useSecureCookie = null;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
public void afterPropertiesSet() throws Exception {
@ -296,9 +296,6 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
/**
* Sets a "cancel cookie" (with maxAge = 0) on the response to disable persistent logins.
*
* @param request
* @param response
*/
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
logger.debug("Cancelling cookie");
@ -310,7 +307,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
}
/**
* Sets the cookie on the response
* Sets the cookie on the response.
*
* By default a secure cookie will be used if the connection is secure. You can set the {@code useSecureCookie}
* property to {@code false} to override this. If you set it to {@code true}, the cookie will always be flagged
* as secure.
*
* @param tokens the tokens which will be encoded to make the cookie value.
* @param maxAge the value passed to {@link Cookie#setMaxAge(int)}
@ -322,7 +323,13 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
Cookie cookie = new Cookie(cookieName, cookieValue);
cookie.setMaxAge(maxAge);
cookie.setPath(getCookiePath(request));
cookie.setSecure(useSecureCookie);
if (useSecureCookie == null) {
cookie.setSecure(request.isSecure());
} else {
cookie.setSecure(useSecureCookie);
}
response.addCookie(cookie);
}
@ -332,7 +339,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
}
/**
* Implementation of <tt>LogoutHandler</tt>. Default behaviour is to call <tt>cancelCookie()</tt>.
* Implementation of {@code LogoutHandler}. Default behaviour is to call {@code cancelCookie()}.
*/
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
if (logger.isDebugEnabled()) {
@ -395,6 +402,15 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
return tokenValiditySeconds;
}
/**
* Whether the cookie should be flagged as secure or not. Secure cookies can only be sent over an HTTPS connection
* and this cannot be accidentally submitted over HTTP where they could be intercepted.
* <p>
* By default the cookie will be secure if the request is secure. If you only want to use remember-me over
* HTTPS (recommended) you should set this property to {@code true}.
*
* @param useSecureCookie set to {@code true} to always user secure cookies, {@code false} to disable their use.
*/
public void setUseSecureCookie(boolean useSecureCookie) {
this.useSecureCookie = useSecureCookie;
}

View File

@ -106,7 +106,7 @@ public class AbstractRememberMeServicesTests {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
// set non-login cookie
request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")});
request.setCookies(new Cookie("mycookie", "cookie"));
assertNull(services.autoLogin(request, response));
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY));
}
@ -134,8 +134,7 @@ public class AbstractRememberMeServicesTests {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setCookies(new Cookie[] {
new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ")});
request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "ZZZ"));
Authentication result = services.autoLogin(request, response);
assertNull(result);
assertCookieCancelled(response);
@ -147,8 +146,7 @@ public class AbstractRememberMeServicesTests {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setCookies(new Cookie[] {
new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, "")});
request.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, ""));
Authentication result = services.autoLogin(request, response);
assertNull(result);
assertCookieCancelled(response);