Cookie中存取email地址和特殊值的问题解决方法。

分类:Java, Web UI & Ajax, 服务器端技术, 程序设计, 计算机与互联网 作者: 秋天 时间: 24-10-2009

标签分类 : , , , , , , , , , , , , ,

问题:在Cookie value中储存特殊字符并读取。

典型案例:email地址在cookie value中的存取。

解决方法:通过将Cookie value进行Base54编码后储存,读取,并解码。

环境:Java & Javascript

问题延伸:Javascript中读取Base64值错误的解决方法。

我个人认为使用email地址来作为用户帐户登录名称是一个比较不错的方法。首先email地址本身已经具备一定程度上的唯一性。通过一定的正则表达式判断或者直接使用现成的一些判断类和Service判断后可以使数据库中的该字段价值更高。其次在同一用户需要使用多个帐户的情况下,相比“用户名”来说,更加直接并且容易记忆。

使用传统的方式创建的cookie:

Cookie cookie = new Cookie(cookieName, cookieValue);
((BridgeExternalContext)context.getExternalContext()).addCookie(cookie); // icefaces中使用ExternalContext储存cookie

此时如果cookieValue的值是包含一些特定字符,比如空格,大括号,小括号,逗号,以及@等。用直接读取的方式将不能得到正确的值。

比如上面的代码中,String cookieValue = “sam@satech.com.au”

使用下面的代码直接读取:

Cookie[] cookies = ((HttpServletRequest)((BridgeExternalContext)FacesContext
	                .getCurrentInstance().getExternalContext()).getRequest()).getCookies();
for(int i=0;i<cookies.length;i++){
 if(cookies[i].getName().equals(loginCookieName)) {
 this.loginName = cookies[i].getValue();
}

你会发现,储存后在firefox的cookie查看器中能看到正常的值,而读取出来以后,值只剩下一个sam了。这显然不是我们所想要的。

最简单的解决方式,就是使用某种编码方式避开这些不能接受的字符,之后读取出来再进行解码。这里使用Base64编码来解决。其实使用任何编码方式都是可以的。

我的环境中有很多Base64类的继承类可以使用。比较常见的是

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

其他一些继承类中的加密和解码方法,可以直接作为静态方法调用,比如Icefaces自己也在utility包里面包含了。Base64类,需要的朋友可以尝试使用,原理是一样的。我这里列出使用基本的sun的misc包中的BASE64Encoder & Decoder的方法。

编码

if (log.isDebugEnabled()) {
 log.debug("Encoding string: " + this.loginName);
 }
 byte[] encodedBytes = this.loginName.getBytes(); // convert String into byte[]
 BASE64Encoder base64encoder = new BASE64Encoder(); // Create encoder
 this.base64LoginName = base64encoder.encode(encodedBytes);
 Cookie nameCookie = new Cookie(loginCookieName, this.base64LoginName);
 nameCookie.setMaxAge(SECONDS_PER_YEAR);
 nameCookie.setComment("This is the cookie to store username");
((BridgeExternalContext)context.getExternalContext()).addCookie(nameCookie); // save cookie to context

解码

Cookie[] cookies = ((HttpServletRequest)((BridgeExternalContext)FacesContext
 .getCurrentInstance().getExternalContext()).getRequest()).getCookies();
 for(int i=0;i<cookies.length;i++){
 if(cookies[i].getName().equals(loginCookieName)) {
 this.base64LoginName = cookies[i].getValue();
 BASE64Decoder decoder = new BASE64Decoder();
 try {
 byte[] decodedBytes = decoder.decodeBuffer(this.base64LoginName);
 this.loginName = new String(decodedBytes);
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

现在这个cookie value就能接受大部分的字符集了。但是,又发现一个问题。大部分的Base64都是以”=”结尾。在纯Java环境中读取和储存都没有问题,但是某些时候会需要在Javascript中储存和读取这个值。在Javascript中读取已储存的Base64值的时候,最后面的”=”会丢失,导致读取错误。

解决方案有2,第一,使用URLCodec urlCodec再次编码。第二,自己写一个判断函数来加上最后的”=” or “==”

方案一:

 public class CookieDecoder {

 private static final Log log = LogFactory.getLog(CookieDecoder.class);

 /**
 * @param cookieValue The value of the cookie to decode
 * @return Returns the decoded string
 */
 public String decode(String cookieValue) {
 if (cookieValue == null || "".equals(cookieValue)) {
 return null;
 }

 if (log.isDebugEnabled()) {
 log.debug("Decoding string: " + cookieValue);
 }
 URLCodec urlCodec = new URLCodec();
 String b64Str;
 try {
 b64Str = urlCodec.decode(cookieValue);
 }
 catch (DecoderException e) {
 log.error("Error decoding string: " + cookieValue);
 return null;
 }
 Base64 base64 = new Base64();
 byte[] encodedBytes = b64Str.getBytes();
 byte[] decodedBytes = base64.decode(encodedBytes);
 String result = new String(decodedBytes);
 if (log.isDebugEnabled()) {
 log.debug("Decoded string to: " + result);
 }
 return result;
 }
}

JS部分:

Encode:
var encodedValue = this.base64.encode(value); document.cookie = name + "=" + escape(encodedValue) + "; expires=" + this.expires.toGMTString() + "; path=" + this.path;

Decode:
var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0)==' ') { c = c.substring(1,c.length); } if (c.indexOf(nameEQ) == 0) { var encodedValue = c.substring(nameEQ.length,c.length); return this.base64.decode(unescape(encodedValue)); } } return null;

方案二:padString()来解决最后那个等号。

 private static final Log log = LogFactory.getLog(CookieDecoder.class);

/**
 * @param cookieValue The value of the cookie to decode
 * @return Returns the decoded string
 */
public String decode(String cookieValue) {
 if (cookieValue == null || "".equals(cookieValue)) {
 return null;
 }

 if (!cookieValue.endsWith("=")) {
 cookieValue = padString(cookieValue);
 }

 if (log.isDebugEnabled()) {
 log.debug("Decoding string: " + cookieValue);
 }

 Base64 base64 = new Base64();
 byte[] encodedBytes = cookieValue.getBytes();
 byte[] decodedBytes = base64.decode(encodedBytes);
 String result = new String(decodedBytes);
 if (log.isDebugEnabled()) {
 log.debug("Decoded string to: " + result);
 }
 return result;
}

private String padString(String value) {
 int mod = value.length() % 4;
 if (mod <= 0) {
 return value;
 }
 int numEqs = 4 - mod;
 if (log.isDebugEnabled()) {
 log.debug("Padding value with " + numEqs + " = signs");
 }
 for (int i = 0; i < numEqs; i++) {
 value += "=";
 }
 return value;
}

JS部分:现在在JS中就可以按照正常方法读取了

 var encodedValue = this.base64.encode(value);
document.cookie = name + "=" + encodedValue +
 "; expires=" + this.expires.toGMTString() +
 "; path=" + this.path;

至此,基本解决问题。

Leave a Reply


SenseArt 网站开发中