首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

运用CookieHandler管理Cookie数据

2012-10-24 
使用CookieHandler管理Cookie数据前言:因为只学过J2SE部分,对JAVA网络编程也不甚了解,所以学习在JAVA操作H

使用CookieHandler管理Cookie数据

前言:因为只学过J2SE部分,对JAVA网络编程也不甚了解,所以学习在JAVA操作HTTP协议时碰到很多问题.翻译这篇文章只是为了加深理解,如有不当,还望指出.

原文地址:http://java.sun.com/developer/JDCTechTips/2005/tt0913.html

?

???? 在JAVA平台,访问URL资源是通过一系列协议处理器(protocol handler)来实现的.URL的起始部分指定了URL使用的协议.比如某个URL是以file:开头的,这表明这个URL资源是保存在本地文件系统的.J2SE5.0定义了几个必须实现的协议:http,https,file,jar.
???? 作为http协议处理器实现的一部分,J2SE5.0增加了一个CookieHandler.这个类提供了一些用于管理cookies的接口.Cookie是保存在浏览器缓存中的一小块数据.当你访问一个网站然后再次访问的时候,这个cookie数据用于鉴别你的身份.Cookies能够用于保存信息,譬如一个在线商店用于保存以购商品信息.Cookie可以是短期的,为一个单独的web事务保存数据,直到关闭浏览器;也可以是长期的,保存数据一个星期或一年.
在J2SE5中并没有设置默认的CookieHandler.不过你可以注册一个Handler以便程序能够保存cookies并且在http连接的时候得到这些cookies.
???? 回到CookieHandler这个类,这是个具有两组相关联方法的抽象类.第一组方法让你能得到当前已经设置的Handler或设置你自己的Handler:
??????? * getDefault()
??????? * setDefault(CookieHandler)

??? 对于安装了安全管理器的应用来说,得到或设置handler需要特别的权限.通过设置handler为null可以清除当前设置的handler.正如之前提到的,没有设置默认的handler.
第二组方法允许你从一个你维持的cookie缓存得到cookies,或将cookies保存到这个cookie缓存.
??????? * get(URI uri,Map<String,List<String>>requestHeaders)
??????? * put(URI uri,Map<String,List<String>>responseHeaders)

??? get()方法从cookie缓存中的到之前保存的cookie并保存到requestHeaders中.put()方法从response headers 中提取cookies并保存到cookie缓存.
??? 这看起来很简单,事实创建一个handler确实如此.但定义cookie缓存需要做更多的事情.作为示范,我们写一个自己的CookieHandler,cookie缓存以及一个测试程序.这里是测试程序的雏形:[注 1]
??

java 代码
  1. ?? import?java.io.*;?????import?java.net.*;??
  2. ???import?java.util.*;????
  3. ???public?class?Fetch?{???????public?static?void?main(String?args[])?throws?Exception?{??
  4. ???????if?(args.length?==?0)?{???????????System.err.println("URL?missing");??
  5. ?????????System.exit(-1);?????????}??
  6. ???????String?urlString?=?args[0];?????????CookieHandler.setDefault(new?ListCookieHandler());??
  7. ???????URL?url?=?new?URL(urlString);?????????URLConnection?connection?=?url.openConnection();??
  8. ???????Object?obj?=?connection.getContent();?????????url?=?new?URL(urlString);??
  9. ???????connection?=?url.openConnection();?????????obj?=?connection.getContent();??
  10. ?????}?????}??


??? 这个程序首先建立并安装了一个ListCookieHandler类(这个类的定义将在后面给出).然后打开了一个到URL(由命令行参数给出)的连接,并且读取其内容.再次打开另一个到URL的连接,并读取相同的内容.当第一次读取连接内容时,响应中包含的cookies将会被保存,第二次连接请求中就会包含这些保存的cookies.
下面我们来看这些是如何通过URLConnection类实现的.在建立了一个表示网络资源的URL后,我们可以用URLConnection来得到与该网站通信的输入流与输出流.
?? String urlString = ...;
?? URL url = new URL(urlString);
?? URLConnection connection = url.openConnection();
?? InputStream is = connection.getInputStream();
?? // .. read content from stream

??? 这个连接中的信息可能有一部分是属于报文头,这与所用的协议有关.我们可以通过URLConnection来得到这些报文头消息,这个类提供了一些能提取报文头信息的方法,包括:
???? *? getHeaderFields() - Gets a Map of available fields.
???? *? getHeaderField(String name) - Gets header fields by name.
???? *? getHeaderFieldDate(String name, long default) - Gets the header field as a date.
???? *? getHeaderFieldInt(String name, int default) - Gets the header field as a number.
???? *? getHeaderFieldKey(int n) or getHeaderField(int n) - Gets the header field by position.


??? 作为一个示例,下面的程序将指定URL的所有报文头消息列出:
??java 代码
  1. ?? import?java.net.*;?????import?java.util.*;??
  2. ?????public?class?ListHeaders?{??
  3. ?????public?static?void?main(String?args[])?throws?Exception?{?????????if?(args.length?==?0)?{??
  4. ?????????System.err.println("URL?missing");?????????}??
  5. ???????String?urlString?=?args[0];?????????URL?url?=?new?URL(urlString);??
  6. ???????URLConnection?connection?=?url.openConnection();?????????Map<String,List<String>>?headerFields?=??
  7. ?????????connection.getHeaderFields();?????????Set<String>?set?=?headerFields.keySet();??
  8. ???????Iterator?itor?=?set.iterator();?????????while?(itor.hasNext())?{??
  9. ?????????String?key?=?itor.next();???????????System.out.println("Key:?"?+?key?+?"?/?"?+??
  10. ???????????headerFields.get(key));?????????}??
  11. ?????}?????}??


??? 这个程序用一个URL作为参数(比如:http://java.sun.com),然后将从该网站返回的所有报文头消息列出,每一个报头用如下格式显示:
???? Key: <key> / [<value>]
??? 如果你输入:
???? >> java ListHeaders http://java.sun.com
??? 你将会看到与下面类似的输出:
?? Key: Set-Cookie / [SUN_ID=192.168.0.1:269421125489956; EXPIRES=Wednesday, 31- Dec-2025 23:59:59 GMT; DOMAIN=.sun.com; PATH=/]
?? Key: Set-cookie / [JSESSIONID=688047FA45065E07D8792CF650B8F0EA;Path=/]
?? Key: null / [HTTP/1.1 200 OK]
?? Key: Transfer-encoding / [chunked]
?? Key: Date / [Wed, 31 Aug 2005 12:05:56 GMT]
?? Key: Server / [Sun-ONE-Web-Server/6.1]
?? Key: Content-type / [text/html;charset=ISO-8859-1]

????? 这些输出只包含URL的报头,并没有包括这个URL指向的HTML页面.你可能注意到这些输出信息里面包含了这个URL站点所用的web服务器以及其日期时间.同意可以看到里面包含了两行Set-Cookie,这就是报头里面携带的cookies.这些cookie能够保存下来,然后在下一次请求的时候被发送.
??? 下面我们来建立一个CookieHandler,我们得实现CookieHandler的两个抽象方法:get()与put():
???????? *? public void put( URI uri,? Map<String, List<String>> responseHeaders) throws IOException
?   *? public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException


其中put()方法将所有报头中的cookies保存到一个缓存中.为了实现put()方法,首先要从responseHeaders中得到"Set-Cookie"对应的List.
???? List<String> setCookieList =
??????? responseHeaders.get("Set-Cookie");
当你得到cookies对应的List,将List中所有的值保存下来.如果这个cookie已经存在,就将已保存的替换掉:
java 代码
  1. ??? if?(setCookieList?!=?null)?{????????for?(String?item?:?setCookieList)?{??
  2. ????????Cookie?cookie?=?new?Cookie(uri,?item);??????????//?Remove?cookie?if?it?already?exists?in?cache??
  3. ????????//?New?one?will?replace?it??????????for?(Cookie?existingCookie?:?cache)?{??
  4. ??????????...??????????}??
  5. ????????System.out.println("Adding?to?cache:?"?+?cookie);??????????cache.add(cookie);??
  6. ??????}??????}??

??? 这里的"cache"可以是一个数据库或者是一个Collections Framework中的List.其中的Cookie类将在下面定义.从本质上说,这些就是put()方法所要做的事:对于响应报头中每一个cookie,这个方法将cookie保存到缓存中.
??? 而get()方法做的是相反的事情:将缓存中所有与URI匹配cookie添加到请求报头中,如果存在多个cookie,则建立一个用','分隔的列表.方法get()返回一个Map,而且用一个包含已有报文头的map作为参数,你应该将cookie缓存与之相匹配的cookie添加这个map里面去,但是这个Map是只读的,所以你应该首先新建另一个map,并将参数map中的内容复制过去,然后再将cookie添加进去,最后返回一个只读的map.[注 2]
??? 为了实现get()方法,首先要从cookie缓存中查找与URI相匹配的cookie,然后删除那些已经过期的cookie:java 代码
  1. ??? //?Retrieve?all?the?cookies?for?matching?URI??????//?Put?in?comma-separated?list??
  2. ????StringBuilder?cookies?=?new?StringBuilder();??????for?(Cookie?cookie?:?cache)?{??
  3. ??????//?Remove?cookies?that?have?expired????????if?(cookie.hasExpired())?{??
  4. ????????cache.remove(cookie);????????}?else?if?(cookie.matches(uri))?{??
  5. ????????if?(cookies.length() > 0)?{????????????cookies.append(",?");??
  6. ????????}??????????cookies.append(cookie.toString());??
  7. ??????}??????}??

?? ?
??? 这里简单说明一下Cookie类,上面代码中用到了Coookie类的两个方法:hasExpired()和matches().hasExpired()方法用于表明这个cookie是否已经过期;而matches()方法用于检验这个cookie与某个URI是否匹配.
??? get()方法余下部分将上面的StringBuilder中的文本添加到一个Map中,与之对应的key为"Cookie"java 代码
  1. //?Map?to?return??Map<String,?List<String>>?cookieMap?=??
  2. ??new?HashMap<String,?List<String>>(requestHeaders);????
  3. //?Convert?StringBuilder?to?List,?store?in?map??if?(cookies.length()?>?0)?{??
  4. ??List<String>?list?=??????Collections.singletonList(cookies.toString());??
  5. ??cookieMap.put("Cookie",?list);??}??
  6. return?Collections.unmodifiableMap(cookieMap);??

?? ?
??? 下面是CookieHandler的完整实现,里面添加了一些输出语句用于观察运行时刻的信息:java 代码
  1. ? import?java.io.*;????import?java.net.*;??
  2. ??import?java.util.*;????
  3. ??public?class?ListCookieHandler?extends?CookieHandler?{????
  4. ????//?"Long"?term?storage?for?cookies,?not?serialized?so?only??????//?for?current?JVM?instance??
  5. ????private?List<Cookie>?cache?=?new?LinkedList<Cookie>();????
  6. ????/**??????*?Saves?all?applicable?cookies?present?in?the?response?
  7. ?????*?headers?into?cache.??????*?@param?uri?URI?source?of?cookies?
  8. ?????*?@param?responseHeaders?Immutable?map?from?field?names?to??????*?lists?of?field?
  9. ?????*???values?representing?the?response?header?fields?returned??????*/??
  10. ??????public?void?put(??
  11. ????????URI?uri,??????????Map<String,?List<String>>?responseHeaders)??
  12. ??????????throws?IOException?{????
  13. ??????System.out.println("Cache:?"?+?cache);????????List<String>?setCookieList?=??
  14. ????????responseHeaders.get("Set-Cookie");????????if?(setCookieList?!=?null)?{??
  15. ????????for?(String?item?:?setCookieList)?{????????????Cookie?cookie?=?new?Cookie(uri,?item);??
  16. ??????????//?Remove?cookie?if?it?already?exists????????????//?New?one?will?replace??
  17. ??????????for?(Cookie?existingCookie?:?cache)?{??????????????if((cookie.getURI().equals(??
  18. ??????????????existingCookie.getURI()))?&&?????????????????(cookie.getName().equals(??
  19. ?????????????????existingCookie.getName())))?{???????????????cache.remove(existingCookie);??
  20. ?????????????break;?????????????}??
  21. ?????????}???????????System.out.println("Adding?to?cache:?"?+?cookie);??
  22. ?????????cache.add(cookie);?????????}??
  23. ?????}?????}??
  24. ?????/**?
  25. ????*?Gets?all?the?applicable?cookies?from?a?cookie?cache?for?????*?the?specified?uri?in?the?request?header.?
  26. ????*?????*?@param?uri?URI?to?send?cookies?to?in?a?request?
  27. ????*?@param?requestHeaders?Map?from?request?header?field?names?????*?to?lists?of?field?values?representing?the?current?request?
  28. ????*?headers?????*?@return?Immutable?map,?with?field?name?"Cookie"?to?a?list?
  29. ????*?of?cookies?????*/??
  30. ?????public?Map<String,?List<String>>?get(??
  31. ???????URI?uri,?????????Map<String,?List<String>>?requestHeaders)??
  32. ?????????throws?IOException?{????
  33. ?????//?Retrieve?all?the?cookies?for?matching?URI???????//?Put?in?comma-separated?list??
  34. ?????StringBuilder?cookies?=?new?StringBuilder();???????for?(Cookie?cookie?:?cache)?{??
  35. ???????//?Remove?cookies?that?have?expired?????????if?(cookie.hasExpired())?{??
  36. ?????????cache.remove(cookie);?????????}?else?if?(cookie.matches(uri))?{??
  37. ?????????if?(cookies.length()?>?0)?{?????????????cookies.append(",?");??
  38. ?????????}???????????cookies.append(cookie.toString());??
  39. ???????}???????}??
  40. ???????//?Map?to?return??
  41. ?????Map<String,?List<String>>?cookieMap?=?????????new?HashMap<String,?List<String>>(requestHeaders);??
  42. ???????//?Convert?StringBuilder?to?List,?store?in?map??
  43. ?????if?(cookies.length()?>?0)?{?????????List<String>?list?=??
  44. ?????????Collections.singletonList(cookies.toString());?????????cookieMap.put("Cookie",?list);??
  45. ?????}?????????System.out.println("Cookies:?"?+?cookieMap);??
  46. ???return?Collections.unmodifiableMap(cookieMap);?????}??
  47. ?}??

?
????? 到这里,我们的工作只剩下Cookie类的实现了.这个工作的重头戏在其构造函数部分,你需要从URI以及报文头里面解析出所需要的信息.其中的cookie有效日期信息的格式是确定的,但其它信息对不同的网站有不同的格式.不过这也没有什么困难的,只需要把cookie路径,有效日期,域名这些信息保存下来就是了.java 代码
  1. ?public?Cookie(URI?uri,?String?header)?{????String?attributes[]?=?header.split(";");??
  2. ??String?nameValue?=?attributes[0].trim();????this.uri?=?uri;??
  3. ??this.name?=?nameValue.substring(0,?nameValue.indexOf('='));????this.value?=?nameValue.substring(nameValue.indexOf('=')+1);??
  4. ??this.path?=?"/";????this.domain?=?uri.getHost();??
  5. ????for?(int?i=1;?i?<?attributes.length;?i++)?{??
  6. ????nameValue?=?attributes[i].trim();??????int?equals?=?nameValue.indexOf('=');??
  7. ????if?(equals?==?-1)?{????????continue;??
  8. ????}??????String?name?=?nameValue.substring(0,?equals);??
  9. ????String?value?=?nameValue.substring(equals+1);??????if?(name.equalsIgnoreCase("domain"))?{??
  10. ??????String?uriDomain?=?uri.getHost();????????if?(uriDomain.equals(value))?{??
  11. ????????this.domain?=?value;????????}?else?{??
  12. ????????if?(!value.startsWith("."))?{????????????value?=?"."?+?value;??
  13. ????????}??????????uriDomain?=??
  14. ??????????uriDomain.substring(uriDomain.indexOf('.'));??????????if?(!uriDomain.equals(value))?{??
  15. ??????????throw?new?IllegalArgumentException(??????????????"Trying?to?set?foreign?cookie");??
  16. ????????}??????????this.domain?=?value;??
  17. ??????}??????}?else?if?(name.equalsIgnoreCase("path"))?{??
  18. ??????this.path?=?value;??????}?else?if?(name.equalsIgnoreCase("expires"))?{??
  19. ??????try?{??????????this.expires?=?expiresFormat1.parse(value);??
  20. ??????}?catch?(ParseException?e)?{??????????try?{??
  21. ??????????this.expires?=?expiresFormat2.parse(value);??????????}?catch?(ParseException?e2)?{??
  22. ??????????throw?new?IllegalArgumentException(??????????????"Bad?date?format?in?header:?"?+?value);??
  23. ????????}????????}??
  24. ????}????}??

?
? Cookie类的其它方法只需要返回这些信息,或者检查有效日期就OK了:java 代码
  1. public?boolean?hasExpired()?{????if?(expires?==?null)?{??
  2. ????return?false;????}??
  3. ??Date?now?=?new?Date();????return?now.after(expires);??
  4. }????
  5. public?String?toString()?{????StringBuilder?result?=?new?StringBuilder(name);??
  6. ??result.append("=");????result.append(value);??
  7. ??return?result.toString();??}??

?? 对于一个已经过期的cookie,其matchs方法总是返回false:

??? public boolean matches(URI uri) {

???? if (hasExpired()) {
?????? return false;
???? }

???? String path = uri.getPath();
???? if (path == null) {
?????? path = "/";
???? }

???? return path.startsWith(this.path);
?? }
?? 注意:Cookie规范中要求同时检查域名以及路径,为了简单起见,我们这里只检查了路径.
?? 这里是Cookie的完整定义:

java 代码?
  1. ?? import?java.net.*;?????import?java.text.*;??
  2. ???import?java.util.*;????
  3. ???public?class?Cookie?{????
  4. ?????String?name;???????String?value;??
  5. ?????URI?uri;???????String?domain;??
  6. ?????Date?expires;???????String?path;??
  7. ???????private?static?DateFormat?expiresFormat1??
  8. ????????=?new?SimpleDateFormat("E,?dd?MMM?yyyy?k:m:s?'GMT'",?Locale.US);????
  9. ?????private?static?DateFormat?expiresFormat2??????????=?new?SimpleDateFormat("E,?dd-MMM-yyyy?k:m:s?'GMT'",?Locale.US);??
  10. ????????????
  11. ?????/**???????*?Construct?a?cookie?from?the?URI?and?header?fields?
  12. ??????*???????*?@param?uri?URI?for?cookie?
  13. ??????*?@param?header?Set?of?attributes?in?header???????*/??
  14. ?????public?Cookie(URI?uri,?String?header)?{?????????String?attributes[]?=?header.split(";");??
  15. ???????String?nameValue?=?attributes[0].trim();?????????this.uri?=?uri;??
  16. ???????this.name?=????????????nameValue.substring(0,?nameValue.indexOf('='));??
  17. ???????this.value?=????????????nameValue.substring(nameValue.indexOf('=')+1);??
  18. ???????this.path?=?"/";?????????this.domain?=?uri.getHost();??
  19. ?????????for?(int?i=1;?i?<?attributes.length;?i++)?{??
  20. ?????????nameValue?=?attributes[i].trim();???????????int?equals?=?nameValue.indexOf('=');??
  21. ?????????if?(equals?==?-1)?{?????????????continue;??
  22. ?????????}???????????String?name?=?nameValue.substring(0,?equals);??
  23. ?????????String?value?=?nameValue.substring(equals+1);???????????if?(name.equalsIgnoreCase("domain"))?{??
  24. ???????????String?uriDomain?=?uri.getHost();?????????????if?(uriDomain.equals(value))?{??
  25. ?????????????this.domain?=?value;?????????????}?else?{??
  26. ?????????????if?(!value.startsWith("."))?{?????????????????value?=?"."?+?value;??
  27. ?????????????}???????????????uriDomain?=?uriDomain.substring(??
  28. ???????????????uriDomain.indexOf('.'));???????????????if?(!uriDomain.equals(value))?{??
  29. ???????????????throw?new?IllegalArgumentException(???????????????????"Trying?to?set?foreign?cookie");??
  30. ?????????????}??

热点排行