/*
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package juzu.plugin.shiro.impl.common;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import juzu.impl.bridge.spi.servlet.ServletWebBridge;
import juzu.impl.request.Request;
import juzu.request.HttpContext;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.io.DefaultSerializer;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
/**
* @author <a href="mailto:haithanh0809@gmail.com">Nguyen Thanh Hai</a>
* @version $Id$
*
*/
public class RememberMeUtil {
/**
* The default name of the underlying rememberMe cookie which is
* {@code rememberMe}.
*/
private final static String DEFAULT_REMEMBER_ME_COOKIE_NAME = "rememberMe";
/**
* The value of deleted cookie (with the maxAge 0).
*/
private final static String DELETED_COOKIE_VALUE = "deleteMe";
/**
* The number of seconds in one year (= 60 * 60 * 24 * 365).
*/
private final static int ONE_YEAR = 60 * 60 * 24 * 365;
/** . */
private final static long DAY_MILLIS = 86400000; // 1 day = 86,400,000
// milliseconds
/** . */
private final static String GMT_TIME_ZONE_ID = "GMT";
/** . */
private final static String COOKIE_DATE_FORMAT_STRING = "EEE, dd-MMM-yyyy HH:mm:ss z";
/** . */
private final static String NAME_VALUE_DELIMITER = "=";
/** . */
private final static String ATTRIBUTE_DELIMITER = "; ";
/** . */
private final static String COOKIE_HEADER_NAME = "Set-Cookie";
/** . */
private final static String PATH_ATTRIBUTE_NAME = "Path";
/** . */
private final static String EXPIRES_ATTRIBUTE_NAME = "Expires";
/** . */
private final static String MAXAGE_ATTRIBUTE_NAME = "Max-Age";
/** . */
private final static String DOMAIN_ATTRIBUTE_NAME = "Domain";
public static void rememberSerialized() {
HttpContext context = Request.getCurrent().getHttpContext();
if (context instanceof ServletWebBridge) {
ServletWebBridge bridge = (ServletWebBridge) context;
// base 64 encode it and store as a cookie:
DefaultSerializer<PrincipalCollection> serializer = new DefaultSerializer<PrincipalCollection>();
byte[] serialized = serializer.serialize(SecurityUtils.getSubject().getPrincipals());
serialized = encrypt(serialized);
String base64 = Base64.encodeToString(serialized);
String name = DEFAULT_REMEMBER_ME_COOKIE_NAME;
String value = base64;
String domain = context.getServerName();
String path = context.getContextPath();
int maxAge = ONE_YEAR; // always zero for deletion
final String headerValue = buildHeaderValue(name, value, domain.trim(), path.trim(), maxAge);
bridge.getResponse().setHeader(COOKIE_HEADER_NAME, headerValue);
}
}
public static void forgetIdentity() {
HttpContext context = Request.getCurrent().getHttpContext();
if (context instanceof ServletWebBridge) {
ServletWebBridge bridge = (ServletWebBridge) context;
String name = DEFAULT_REMEMBER_ME_COOKIE_NAME;
String value = DELETED_COOKIE_VALUE;
String domain = context.getServerName();
String path = context.getContextPath();
int maxAge = 0; // always zero for deletion
final String headerValue = buildHeaderValue(name, value, domain.trim(), path.trim(), maxAge);
bridge.getResponse().setHeader(COOKIE_HEADER_NAME, headerValue);
}
}
private static String buildHeaderValue(String name, String value, String domain, String path, int maxAge) {
StringBuilder sb = new StringBuilder(name).append(NAME_VALUE_DELIMITER);
if (value != null && !value.isEmpty()) {
sb.append(value);
}
if (domain != null && !domain.isEmpty()) {
sb.append(ATTRIBUTE_DELIMITER);
sb.append(DOMAIN_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(domain);
}
if (path != null && !path.isEmpty()) {
sb.append(ATTRIBUTE_DELIMITER);
sb.append(PATH_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(path);
}
if (maxAge >= 0) {
sb.append(ATTRIBUTE_DELIMITER);
sb.append(MAXAGE_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(maxAge);
sb.append(ATTRIBUTE_DELIMITER);
Date expires;
if (maxAge == 0) {
// delete the cookie by specifying a time in the past (1 day ago):
expires = new Date(System.currentTimeMillis() - DAY_MILLIS);
} else {
// Value is in seconds. So take 'now' and add that many seconds, and
// that's our expiration date:
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, maxAge);
expires = cal.getTime();
}
String formatted = toCookieDate(expires);
sb.append(EXPIRES_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(formatted);
}
return sb.toString();
}
/**
* Formats a date into a cookie date compatible string (Netscape's
* specification).
*
* @param date
* the date to format
* @return an HTTP 1.0/1.1 Cookie compatible date string (GMT-based).
*/
private static String toCookieDate(Date date) {
TimeZone tz = TimeZone.getTimeZone(GMT_TIME_ZONE_ID);
DateFormat fmt = new SimpleDateFormat(COOKIE_DATE_FORMAT_STRING, Locale.US);
fmt.setTimeZone(tz);
return fmt.format(date);
}
private static byte[] encrypt(byte[] serialized) {
byte[] value = serialized;
CipherService cipherService = new AesCipherService();
if (cipherService != null) {
ByteSource byteSource = cipherService.encrypt(serialized, Base64.decode("kPH+bIxk5D2deZiIxcaaaA=="));
value = byteSource.getBytes();
}
return value;
}
}