|
@@ -15,28 +15,16 @@
|
|
|
|
|
|
package org.springframework.security.ui.rememberme;
|
|
|
|
|
|
-import java.util.Date;
|
|
|
-
|
|
|
-import javax.servlet.http.Cookie;
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
|
-import javax.servlet.http.HttpServletResponse;
|
|
|
-
|
|
|
import org.springframework.security.Authentication;
|
|
|
-import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
|
|
-import org.springframework.security.ui.AuthenticationDetailsSource;
|
|
|
-import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
|
|
-import org.springframework.security.ui.logout.LogoutHandler;
|
|
|
import org.springframework.security.userdetails.UserDetails;
|
|
|
-import org.springframework.security.userdetails.UserDetailsService;
|
|
|
-import org.springframework.security.userdetails.UsernameNotFoundException;
|
|
|
-import org.apache.commons.codec.binary.Base64;
|
|
|
-import org.apache.commons.codec.digest.DigestUtils;
|
|
|
-import org.apache.commons.logging.Log;
|
|
|
-import org.apache.commons.logging.LogFactory;
|
|
|
-import org.springframework.beans.factory.InitializingBean;
|
|
|
-import org.springframework.util.Assert;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
-import org.springframework.web.bind.RequestUtils;
|
|
|
+
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Date;
|
|
|
|
|
|
/**
|
|
|
* Identifies previously remembered users by a Base-64 encoded cookie.
|
|
@@ -44,8 +32,7 @@ import org.springframework.web.bind.RequestUtils;
|
|
|
* <p>
|
|
|
* This implementation does not rely on an external database, so is attractive
|
|
|
* for simple applications. The cookie will be valid for a specific period from
|
|
|
- * the date of the last
|
|
|
- * {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
|
|
|
+ * the date of the last {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
|
|
|
* As per the interface contract, this method will only be called when the
|
|
|
* principal completes a successful interactive authentication. As such the time
|
|
|
* period commences from the last authentication attempt where they furnished
|
|
@@ -72,7 +59,7 @@ import org.springframework.web.bind.RequestUtils;
|
|
|
*
|
|
|
* </p>
|
|
|
* <p>
|
|
|
- * As such, if the user changes their password any remember-me token will be
|
|
|
+ * As such, if the user changes their password, any remember-me token will be
|
|
|
* invalidated. Equally, the system administrator may invalidate every
|
|
|
* remember-me token on issue by changing the key. This provides some reasonable
|
|
|
* approaches to recovering from a remember-me token being left on a public
|
|
@@ -86,366 +73,123 @@ import org.springframework.web.bind.RequestUtils;
|
|
|
* <p>
|
|
|
* This is a basic remember-me implementation which is suitable for many
|
|
|
* applications. However, we recommend a database-based implementation if you
|
|
|
- * require a more secure remember-me approach.
|
|
|
+ * require a more secure remember-me approach (see {@link PersistentTokenBasedRememberMeServices}).
|
|
|
* </p>
|
|
|
* <p>
|
|
|
* By default the tokens will be valid for 14 days from the last successful
|
|
|
* authentication attempt. This can be changed using
|
|
|
- * {@link #setTokenValiditySeconds(long)}.
|
|
|
+ * {@link #setTokenValiditySeconds(int)}.
|
|
|
* </p>
|
|
|
*
|
|
|
* @author Ben Alex
|
|
|
* @version $Id$
|
|
|
*/
|
|
|
-public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
|
|
- //~ Static fields/initializers =====================================================================================
|
|
|
-
|
|
|
- public static final String SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE";
|
|
|
-
|
|
|
- public static final String DEFAULT_PARAMETER = "_spring_security_remember_me";
|
|
|
-
|
|
|
- protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
|
|
|
-
|
|
|
- //~ Instance fields ================================================================================================
|
|
|
-
|
|
|
- protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
|
|
-
|
|
|
- private String key;
|
|
|
-
|
|
|
- private String parameter = DEFAULT_PARAMETER;
|
|
|
-
|
|
|
- private UserDetailsService userDetailsService;
|
|
|
-
|
|
|
- protected long tokenValiditySeconds = 1209600; // 14 days
|
|
|
-
|
|
|
- private boolean alwaysRemember = false;
|
|
|
-
|
|
|
- private String cookieName = SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
|
|
|
+public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
|
|
|
|
|
|
//~ Methods ========================================================================================================
|
|
|
|
|
|
- public void afterPropertiesSet() throws Exception {
|
|
|
- Assert.hasLength(key);
|
|
|
- Assert.hasLength(parameter);
|
|
|
- Assert.hasLength(cookieName);
|
|
|
- Assert.notNull(userDetailsService);
|
|
|
- }
|
|
|
-
|
|
|
- public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
|
|
- Cookie[] cookies = request.getCookies();
|
|
|
-
|
|
|
- if ((cookies == null) || (cookies.length == 0)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < cookies.length; i++) {
|
|
|
- if (!cookieName.equals(cookies[i].getName())) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // We have the spring security cookie
|
|
|
- String cookieValue = cookies[i].getValue();
|
|
|
-
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Remember-me cookie detected");
|
|
|
- }
|
|
|
-
|
|
|
- for (int j = 0; j < cookieValue.length() % 4; j++) {
|
|
|
- cookieValue = cookieValue + "=";
|
|
|
- }
|
|
|
-
|
|
|
- if (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
|
|
|
- cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // Decode token from Base64
|
|
|
- // format of token is:
|
|
|
- // username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
|
|
- String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
|
|
|
- String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
|
|
-
|
|
|
- if (cookieTokens.length != 3) {
|
|
|
- cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
|
|
|
- + cookieAsPlainText + "'");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- long tokenExpiryTime;
|
|
|
-
|
|
|
- try {
|
|
|
- tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
|
|
- }
|
|
|
- catch (NumberFormatException nfe) {
|
|
|
- cancelCookie(request, response,
|
|
|
- "Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (isTokenExpired(tokenExpiryTime)) {
|
|
|
- cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
|
|
|
- + new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
+ public UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
|
|
+ HttpServletResponse response) {
|
|
|
|
|
|
- // Check the user exists
|
|
|
- // Defer lookup until after expiry time checked, to
|
|
|
- // possibly avoid expensive lookup
|
|
|
- UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
|
|
|
-
|
|
|
- if (userDetails == null) {
|
|
|
- cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
|
- + "' but was not found");
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // Check signature of token matches remaining details
|
|
|
- // Must do this after user lookup, as we need the
|
|
|
- // DAO-derived password
|
|
|
- // If efficiency was a major issue, just add in a
|
|
|
- // UserCache implementation,
|
|
|
- // but recall this method is usually only called one per
|
|
|
- // HttpSession
|
|
|
- // (as if the token is valid, it will cause
|
|
|
- // SecurityContextHolder population, whilst
|
|
|
- // if invalid, will cause the cookie to be cancelled)
|
|
|
- String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);
|
|
|
-
|
|
|
- if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
|
|
- cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
|
|
|
- + "' but expected '" + expectedTokenSignature + "'");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // By this stage we have a valid token
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Remember-me cookie accepted");
|
|
|
- }
|
|
|
+ if (cookieTokens.length != 3) {
|
|
|
+ throw new InvalidCookieException("Cookie token did not contain " + 2 +
|
|
|
+ " tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
|
|
|
+ }
|
|
|
|
|
|
- RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
|
|
|
- userDetails.getAuthorities());
|
|
|
- auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
|
|
+ long tokenExpiryTime;
|
|
|
|
|
|
- return auth;
|
|
|
+ try {
|
|
|
+ tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
|
|
+ }
|
|
|
+ catch (NumberFormatException nfe) {
|
|
|
+ throw new InvalidCookieException("Cookie token[1] did not contain a valid number (contained '" +
|
|
|
+ cookieTokens[1] + "')");
|
|
|
}
|
|
|
|
|
|
+ if (isTokenExpired(tokenExpiryTime)) {
|
|
|
+ throw new InvalidCookieException("Cookie token[1] has expired (expired on '"
|
|
|
+ + new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
|
|
|
+ }
|
|
|
|
|
|
- return null;
|
|
|
- }
|
|
|
+ // Check the user exists.
|
|
|
+ // Defer lookup until after expiry time checked, to possibly avoid expensive database call.
|
|
|
|
|
|
- /**
|
|
|
- * @param tokenExpiryTime
|
|
|
- * @param userDetails
|
|
|
- * @return
|
|
|
- */
|
|
|
- protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetails) {
|
|
|
- String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":" + tokenExpiryTime + ":"
|
|
|
- + userDetails.getPassword() + ":" + this.key);
|
|
|
- return expectedTokenSignature;
|
|
|
- }
|
|
|
+ UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);
|
|
|
|
|
|
- protected boolean isValidUserDetails(HttpServletRequest request, HttpServletResponse response,
|
|
|
- UserDetails userDetails, String[] cookieTokens) {
|
|
|
- // Immediately reject if the user is not allowed to
|
|
|
- // login
|
|
|
- if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired() || !userDetails.isEnabled()) {
|
|
|
- cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
|
- + "' but account has expired, credentials have expired, or user is disabled");
|
|
|
+ validateUserDetails(userDetails);
|
|
|
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
+ // Check signature of token matches remaining details.
|
|
|
+ // Must do this after user lookup, as we need the DAO-derived password.
|
|
|
+ // If efficiency was a major issue, just add in a UserCache implementation,
|
|
|
+ // but recall that this method is usually only called once per HttpSession - if the token is valid,
|
|
|
+ // it will cause SecurityContextHolder population, whilst if invalid, will cause the cookie to be cancelled.
|
|
|
+ String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),
|
|
|
+ userDetails.getPassword());
|
|
|
|
|
|
- protected UserDetails loadUserDetails(HttpServletRequest request, HttpServletResponse response,
|
|
|
- String[] cookieTokens) {
|
|
|
- UserDetails userDetails = null;
|
|
|
+ if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
|
|
+ throw new InvalidCookieException("Cookie token[2] contained signature '" + cookieTokens[2]
|
|
|
+ + "' but expected '" + expectedTokenSignature + "'");
|
|
|
+ }
|
|
|
|
|
|
- try {
|
|
|
- userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
|
|
|
- }
|
|
|
- catch (UsernameNotFoundException notFound) {
|
|
|
- cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
|
- + "' but was not found");
|
|
|
+ return userDetails;
|
|
|
+ }
|
|
|
|
|
|
- return null;
|
|
|
- }
|
|
|
- return userDetails;
|
|
|
+ /**
|
|
|
+ * Calculates the digital signature to be put in the cookie. Default value is
|
|
|
+ * MD5 ("username:tokenExpiryTime:password:key")
|
|
|
+ */
|
|
|
+ protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
|
|
|
+ return DigestUtils.md5Hex(username + ":" + tokenExpiryTime + ":" + password + ":" + getKey());
|
|
|
}
|
|
|
|
|
|
protected boolean isTokenExpired(long tokenExpiryTime) {
|
|
|
- // Check it has not expired
|
|
|
- if (tokenExpiryTime < System.currentTimeMillis()) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- protected void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
|
|
|
- if ((reasonForLog != null) && logger.isDebugEnabled()) {
|
|
|
- logger.debug("Cancelling cookie for reason: " + reasonForLog);
|
|
|
- }
|
|
|
-
|
|
|
- response.addCookie(makeCancelCookie(request));
|
|
|
- }
|
|
|
-
|
|
|
- public String getKey() {
|
|
|
- return key;
|
|
|
- }
|
|
|
-
|
|
|
- public String getParameter() {
|
|
|
- return parameter;
|
|
|
- }
|
|
|
-
|
|
|
- public long getTokenValiditySeconds() {
|
|
|
- return tokenValiditySeconds;
|
|
|
+ return tokenExpiryTime < System.currentTimeMillis();
|
|
|
}
|
|
|
|
|
|
- public UserDetailsService getUserDetailsService() {
|
|
|
- return userDetailsService;
|
|
|
- }
|
|
|
-
|
|
|
- public void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
|
|
- cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
|
|
|
- }
|
|
|
-
|
|
|
- protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
|
|
|
- if (alwaysRemember) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return RequestUtils.getBooleanParameter(request, parameter, false);
|
|
|
- }
|
|
|
-
|
|
|
- public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
|
|
+ public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
|
|
|
Authentication successfulAuthentication) {
|
|
|
- // Exit if the principal hasn't asked to be remembered
|
|
|
- if (!rememberMeRequested(request, parameter)) {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
|
|
|
- + "')");
|
|
|
- }
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Determine username and password, ensuring empty strings
|
|
|
- Assert.notNull(successfulAuthentication.getPrincipal());
|
|
|
- Assert.notNull(successfulAuthentication.getCredentials());
|
|
|
|
|
|
String username = retrieveUserName(successfulAuthentication);
|
|
|
String password = retrievePassword(successfulAuthentication);
|
|
|
|
|
|
- // If unable to find a username and password, just abort as
|
|
|
- // TokenBasedRememberMeServices unable to construct a valid token in
|
|
|
- // this case
|
|
|
+ // If unable to find a username and password, just abort as TokenBasedRememberMeServices is
|
|
|
+ // unable to construct a valid token in this case.
|
|
|
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
|
|
|
+ long expiryTime = System.currentTimeMillis() + getTokenValiditySeconds() * 1000;
|
|
|
+
|
|
|
+ String signatureValue = makeTokenSignature(expiryTime, username, password);
|
|
|
+ String cookieValue = encodeCookie(new String[] {username, Long.toString(expiryTime), signatureValue});
|
|
|
|
|
|
- // construct token to put in cookie; format is:
|
|
|
- // username + ":" + expiryTime + ":" + Md5Hex(username + ":" +
|
|
|
- // expiryTime + ":" + password + ":" + key)
|
|
|
- String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
|
|
|
- String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
|
|
|
- String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
|
|
|
- response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
|
|
|
+ response.addCookie(makeValidCookie(cookieValue, request, getTokenValiditySeconds()));
|
|
|
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Added remember-me cookie for user '"
|
|
|
- + username + "', expiry: '" + new Date(expiryTime) + "'");
|
|
|
+ logger.debug("Added remember-me cookie for user '" + username + "', expiry: '"
|
|
|
+ + new Date(expiryTime) + "'");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
|
|
- cancelCookie(request, response, "Logout of user "
|
|
|
- + (authentication == null ? "Unknown" : authentication.getName()));
|
|
|
- }
|
|
|
-
|
|
|
- protected String retrieveUserName(Authentication successfulAuthentication) {
|
|
|
- if (isInstanceOfUserDetails(successfulAuthentication)) {
|
|
|
- return ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
|
|
|
+ protected String retrieveUserName(Authentication authentication) {
|
|
|
+ if (isInstanceOfUserDetails(authentication)) {
|
|
|
+ return ((UserDetails) authentication.getPrincipal()).getUsername();
|
|
|
}
|
|
|
else {
|
|
|
- return successfulAuthentication.getPrincipal().toString();
|
|
|
+ return authentication.getPrincipal().toString();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- protected String retrievePassword(Authentication successfulAuthentication) {
|
|
|
- if (isInstanceOfUserDetails(successfulAuthentication)) {
|
|
|
- return ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
|
|
|
+ protected String retrievePassword(Authentication authentication) {
|
|
|
+ if (isInstanceOfUserDetails(authentication)) {
|
|
|
+ return ((UserDetails) authentication.getPrincipal()).getPassword();
|
|
|
}
|
|
|
else {
|
|
|
- return successfulAuthentication.getCredentials().toString();
|
|
|
+ return authentication.getCredentials().toString();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private boolean isInstanceOfUserDetails(Authentication authentication) {
|
|
|
return authentication.getPrincipal() instanceof UserDetails;
|
|
|
}
|
|
|
-
|
|
|
- protected Cookie makeCancelCookie(HttpServletRequest request) {
|
|
|
- Cookie cookie = new Cookie(cookieName, null);
|
|
|
- cookie.setMaxAge(0);
|
|
|
- cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
|
|
-
|
|
|
- return cookie;
|
|
|
- }
|
|
|
-
|
|
|
- protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
|
|
|
- Cookie cookie = new Cookie(cookieName, tokenValueBase64);
|
|
|
- cookie.setMaxAge(new Long(maxAge).intValue());
|
|
|
- cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
|
|
-
|
|
|
- return cookie;
|
|
|
- }
|
|
|
-
|
|
|
- public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
|
|
- Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
|
|
- this.authenticationDetailsSource = authenticationDetailsSource;
|
|
|
- }
|
|
|
-
|
|
|
- public void setKey(String key) {
|
|
|
- this.key = key;
|
|
|
- }
|
|
|
-
|
|
|
- public void setParameter(String parameter) {
|
|
|
- this.parameter = parameter;
|
|
|
- }
|
|
|
-
|
|
|
- public void setCookieName(String cookieName) {
|
|
|
- this.cookieName = cookieName;
|
|
|
- }
|
|
|
-
|
|
|
- public void setTokenValiditySeconds(long tokenValiditySeconds) {
|
|
|
- this.tokenValiditySeconds = tokenValiditySeconds;
|
|
|
- }
|
|
|
-
|
|
|
- public void setUserDetailsService(UserDetailsService userDetailsService) {
|
|
|
- this.userDetailsService = userDetailsService;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isAlwaysRemember() {
|
|
|
- return alwaysRemember;
|
|
|
- }
|
|
|
-
|
|
|
- public void setAlwaysRemember(boolean alwaysRemember) {
|
|
|
- this.alwaysRemember = alwaysRemember;
|
|
|
- }
|
|
|
-
|
|
|
- public String getCookieName() {
|
|
|
- return cookieName;
|
|
|
- }
|
|
|
-
|
|
|
}
|