1.状态管理
状态管理是你对同一个或不同页的多个请求维护状态和页信息的过程
状态管理的作用:
指示用户信息,关联浏览器实例
使得页与页之间,请求与请求之间能够共享信息
更为快速的数据存储与读取
2.Cookie
A.Cookie概述:
Cookie为Web应用程序保存用户相关信息提供了一种有用的方法
Cookie是小段保存在客户端的数据,用户访问网站时,网站会给用户一个包含过期时间的Cookie,浏览器收到Cookie后就存放在客户端的文件夹下,以 后用户每次访问网站页面的时候,浏览器会根据网站的URL在本地Cookie文件夹中查找是否存在当前网站关联的Cookie,如果有的话就连同页面请求一 起发送到服务器
注意:Cookie只是一段字符串,并不能执行
除了Cookie外,几乎没有其他的方法在客户端的机器上写入数据
现在的大多数网站都利用Cookie来保存一些数据(比如你的ID),以便下一次访问网站时能直接“继续”以前的配置,所有不要轻易关闭Cookie
不要在Cookie中保存保密信息,如用户名、密码、信用号等,在Cookie中不要保存不应该由用户掌握的内容,也不要保存可能被其他窃取Cookie 的人控制的内容
我们可以了解到Cookie可以保存单个值也可以保存多个值,其中,HttpCookie类型表示一个Cookie,Expiers属性用于修改Cookie的 过期时间,在添加完值以后,务必记得使用Response对象把Cookie对象返回给浏览器,我们的服务器不能直接在客户端机器上写Cookie,而是由浏览器完成这一工作 获取单值Cookie时只需要知道该Cookie保存时的名称,获取多值Cookie时也是根据保存名称获取,我们可以通过遍历 如果直接读取多值Cookie的Value,它会把所有子键和子键值使用key=value方法显示,多个子键使用“&”连接
我们始终要记住,服务器不能直接删除Cookie,删除Cookie的操作是浏览器进行的,说是删除,其实是把它的过期时间设置为过去的时间,让Cookie过期
C.总结:
存储的物理位置:客户端的Cookies文件夹内
存储的类型限制:字符串
状态使用的范围:当前请求上下文的上下文都能访问到Cookie,Cookie对每个用户来说都是独立的
存储的大小限制:每个Cookie不超过4K数据,每个网站不超过20个Cookie,所有网站的Cookie总和不超过300个
生命周期:每个Cookie都有自己的过期时间,超过了过期时间后失效
安全与性能:存储在客户端,安全性差,对于敏感数据建议加密后存储
优点缺点与注意事项:可以方便地关联网站和用户,长久保存用户设置
在bs的架构中,浏览器作为客户端,与服务器之间通过session保持连接状态。以前面试的时候,经常被问及一个问题:浏览器禁止cookie时,服务器与客户端浏览器能否保持session连接?
其实要完全回答正确这个问题,需要对cookie的作用有全面的了解。具体cookie的解释大家可以google一下。我以前一直有一个误解(估计很多人都有),以为cookie就是一个文件,用来保存用户信息或者其他信息。如果这么认为的话,那么浏览器禁用cookie也就是不保存成文件而已,并不影响浏览器与服务器交互。其实如果我们看看浏览器发出的http请求消息和服务器的http响应消息就一目了然了。
Http request:
GET /cluster/index.jsp HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
Http response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1; Path=/cluster
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 377
Date: Tue, 02 Mar 2010 02:58:32 GMT
这是我们打开浏览器后,第一次请求一个网址时的请求和响应消息(如果客户本地没有该网址的cookie)。首先看response消息,可以看到一行醒目的字符串Set-Cookie: JSESSIONID=1363B650B295DE9494A33805F62BC5ED.server1; Path=/cluster。其实这行是在告诉客户端浏览器,把这段cookie保存下来,根据cookie的存活时间,这段cookie信息有可能只存在在内存中,也可能保存到文件中。下面不关闭浏览器,再次请求同样的url,观察第二次的请求和响应消息:
Http request:
GET /cluster/user_details.jsp HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1
Http response
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4918D6ED22B81B587E7AF7517CE24E25.server1; Expires=Tue, 02-Mar-2010 22:15:38 GMT
Content-Type: text/html
Content-Length: 252
Date: Tue, 02 Mar 2010 05:35:38 GMT
这个时候注意看请求信息中多了一行Cookie,浏览器将这个Cookie信息传递给服务器,这次服务器返回的Set-Cookie中的JSESSIONID就和请求的JSESSIONID一致了。
通过这两次实验,总结出以下几点:
1.session是什么?session其实是在无状态会话的http协议下,为了实现有状态会话而设计的一种实现方法。
2.如何实现session会话?简单的说,就是在服务器端创建session对象,记录会话信息,同时在客户端与服务器的交互过程中,通过JSESSIONID字符串唯一标识session对象(客户端只需要保存JSESSIONID这个字符串,服务器端只需要保存JSESSIONID和真实的session对象之间的关联关系),而这个JSESSIONID字符串就是通过http协议的cookie功能实现的。
3.浏览器禁用cookie时会发生什么?浏览器禁止了Cookie,那么第二次请求中将不包含有Cookie信息(当然更不存在JSESSIONID信息),服务器也就不会收到JSESSIONID的值,于是服务器认为是新请求,又创建一个服务器session,同时将本次生成的JSESSIONID再次通过响应信息中的Set-Cookie返回给客户端。如此循环交互,服务器将永远认为该客户浏览器的请求是新请求,因此无法实现session功能。
最后回到最初的那个面试问题:浏览器禁止cookie时,服务器与客户端浏览器能否保持session连接?当然可以,但是现在不能依靠浏览器自动完成这个功能,而是需要服务器端通过重写URL的方法来实现,就是在返回的页面中,在URL里将JSESSIONID的值作为请求参数传递给服务器端,例如<a href="">。
注意:JSESSIONID要大写,小写的服务器不认。
为什么需要Cookie? 因为HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分 是不是来源于同一个浏览器。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。
Cookie能做什么? Cookie只是一段文本,所以它只能保存字符串。而且浏览器对它有大小限制以及 它会随着每次请求被发送到服务器,所以应该保证它不要太大。 Cookie的内容也是明文保存的,有些浏览器提供界面修改,所以, 不适合保存重要的或者涉及隐私的内容。
Cookie 的限制。 大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小,最好用 Cookie 来存储少量数据,或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户,以及从数据库或其他数据源中读取用户信息。 浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。
通过前面的内容,我们了解到Cookie是用于维持服务端会话状态的,通常由服务端写入,在后续请求中,供服务端读取。
从上图,您应该能发现,我们在服务端写的Cookie,最后其实是通过HTTP的响应头这种途径发送到客户端的。每一个写入动作, 都会产生一个【Set-Cookie】的响应头。
浏览器正是在每次获取请求的响应后,检查这些头来接收Cookie的。
还是类似的问题:大家有没有想过,Cookie是如何传到服务端的呢?我们还是继续使用Fiddler来寻找答案吧。
从图片中,我们可以发现,Cookie是放在请求头中,发送到服务端的。如果你一直刷新页面,就能发现, 每次HTTP请求,Cookie都会被发送。当然了,浏览器也不是发送它所接收到的所有Cookie,它会检查当前要请求的域名以及目录, 只要这二项目与Cookie对应的Domain和Path匹配,才会发送。对于Domain则是按照尾部匹配的原则进行的。
所以,我在访问 www.cnblogs.com 时,浏览器并不会将我在浏览 www.163.com 所接收到的 Cookie 发出去。
删除Cookie:其实就是在写Cookie时,设置Expires为一个【早于现在时间的时间】。也就是:设置此Cookie已经过期, 浏览器接收到这个Cookie时,便会删除它们。
HttpCookie cookie = new HttpCookie("MyCookieName", null); cookie.Expires = new DateTime(1900, 1, 1); Response.Cookies.Add(cookie);
由于Cookie对于用户来说,是个不可见的东西,而且每次请求都会传递到 服务端,所以它就是很理想的会话标识ID的保存容器。虽然也支持无Cookie的会话,但那种方式要修改URL,也有它的缺点,因此这种方法并没有广泛的使用。
Session的实现是与Cookie有关的,服务端需要将会话标识ID保存到Cookie中。
这里再一次申明,除非你使用无Cookie的会话模式,否则Session是需要Cookie的支持。反过来,Cookie并不需要Session的支持。
从以上图片,您应该能发现:浏览器能提供一些界面让用户清楚的观察我们在服务端写的Cookie, 甚至有些浏览器还提供很方便的修改功能。如下图所示:
所以,我们在服务端写代码读取Cookie时,尤其是涉及类型转换、反序列化或者解密时,一定要注意这些操作都有可能会失败。 而且上图也清楚的反映了一个事实:Cookie中的值都是“一目了然”的,任何人都能看到它们。所以,我们尽量不要直接在Cookie中 保存一些重要的或者敏感的内容。如果我们确实需要使用Cookie保存一些重要的内容,但又不希望被他人看懂, 我们可以使用一些加密的方法来保护这些内容。
1. 对于一些重要性不高的内容,我们可以使用Base64之类的简单处理方式来处理。
2. 对于重要性相对高一点的内容,我们可以利用提供的一些加密工具类,自己来设计加密方法来保护。不过, 密码学与加密解密并不是很简单的算法,因此,自己设计的加密方式可能不会很安全。
Cookie的RFC文档:RFC 2109 --> RFC 2965 --> RFC 6265
Cookie可分为Session Cookie和持久Cookie,没有指定其Expires属性的Cookie为Session Cookie
安全Cookie是指通过https访问时所使用的Cookie,Cookie信息会被加密传送
HttpOnly Cookie是指仅通过Http浏览器进行访问时的Cookie,只对Session Cookie有效
第三方Cookie是指第三方网站在用户访问第一方网站时所设置的Cookie,可用来跟踪用户访问足迹
超级Cookie是指以.com,.co.uk等公共后缀为域名的Cookie,安全原因被禁用
僵尸Cookie是指用户删除以后又被自动创建的Cookie,通常由脚本从缓存中创建,evercookie是僵尸Cookie的一个开源实现
Cookie的用途
用作Session的ID,让sessionid浏览器和服务器之间传递
网站登录
个性化,如网站风格,布局等
跟踪,也可使用访问者的IP地址或者检查http头的referrer属性来实现。如果一个请求中不包含Cookie,则可以认为这是首次访问,可设一个Cookie,然后后续的访问都可以用Cookie记录用户访问的页面,访问时间,计算停留时间等
浏览器通常要能存储300个每个4k大小的Cookie,每个域名/服务器下要能够存储至少20个Cookie
服务器可以设置其他的Cookie属性,如domain, path, expires, maximum-age等,但是浏览器只会把name-value值对发送到服务器,Cookie的其他属性是用来让浏览器何时该删除或阻止一个Cookie
domain跟path用来决定在哪些页面应该发送该Cookie:Domain=docs.foo.com; Path=/accounts只在docs.foo.com/accounts页面传送该Cookie,Domain=.foo.com; Path=/和Domain=.foo.com; Path=/则允许在.foo.com的任何子域名和路径下传送该Cookie
Expires属性指定浏览器该何时删除该Cookie,格式为"Wdy, DD-Mon-YYYY HH:MM:SS GMT",RFC2109也允许使用Max-Age属性来设定浏览器在收到该Cookie后多长时间内保留该Cookie,超过该时常则删除该Cookie
服务器段可以重新设置某一Cookie的值,也可以把某一Cookie的Expires值改为过去的某一时间点来达到立即删除该Cookie的目的
HttpOnly的Cookie不能通过非Http的方式进行访问,如通过javascript的document.cookie
用于页面访问跟踪的1×1像素gif图片也叫web bug,使用该跟踪技术图片服务器应返回no-cache响应头使得浏览器每次都要请求该图片,从而有利于跟踪。此外也可以使用iframe,style,script,input link,embed,object等标签进行跟踪
使用Cookie来记录页面导航的问题是,当用户点击浏览器的后退或前进按钮时,Cookie里面记录的页面信息并没有跟着后退或前进
Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session也会失效。
Cookie是客户端的存储空间,由浏览器来维持。
可以试一下,即使不写Cookie,在使用request.getCookies();取出的Cookie数组的长度也是1,而这个Cookie的名字就是JSESSIONID,还有一个很长的二进制的字符串,是SessionID的值。
实质上 URL 重写是通过向 URL 连接添加参数,并把 session ID 作为值包含在连接中。然而,为使这生效,你需要为你的 servlet 响应部分的每个连接添加 session ID.
Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,对于JSP而言也可以直接写入jsessionid,这样服务器可以知道该用户是否合法用户以及是否需要重新登录等。
服
服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookies的功用。另一个重要应用场合是“购物车”之类处理。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入Cookies,以便在最后付款时提取信息。
Cookie可以保持登录信息到用户下次与服务器的会话,换句话说,下次访问同一网站时,用户会发现不必输入用户名和密码就已经登录了(当然,不排除用户手工删除Cookie)。而还有一些Cookie在用户退出会话的时候就被删除了,这样可以有效保护个人隐私。
Cookie在生成时就会被指定一个Expire值,这就是Cookie的生存周期,在这个周期内Cookie有效,超出周期Cookie就会被清除。有些页面将Cookie的生存周期设置为“0”或负值,这样在关闭页面时,就马上清除Cookie,不会记录用户信息,更加安全。
如果
在一台计算机中安装多个浏览器,每个浏览器都会在各自独立的空间存放cookie。因为cookie中不但可以确认用户,还能包含计算机和浏览器的信息,所以一个用户用不同的浏览器登录或者用不同的计算机登录,都会得到不同的cookie信息,另一方面,对于在同一台计算机上使用同一浏览器的多用户群,cookie不会区分他们的身份,除非他们使用不同的用户名登录。
Cookies在某种程度上说已经严重危及用户的隐私和安全。其中的一种方法是:一些公司的高层人员为了某种目的(譬如市场调研)而访问了从未去过的网站(通过搜索引擎查到的),而这些网站包含了一种叫做网页臭虫的图片,该图片透明,且只有一个象素大小(以便隐藏),它们的作用是将所有访问过此页面的计算机写入cookie。而后,电子商务网站将读取这些cookie信息,并寻找写入这些cookie的网站,随即发送包含了针对这个网站的相关产品广告的垃圾邮件给这些高级人员。
因为更具有针对性,使得这套系统行之有效,收到邮件的客户或多或少表现出对产品的兴趣。这些站点一旦写入cookie并使其运作,就可以从电子商务网站那里获得报酬,以维系网站的生存。
鉴于隐藏的危害性,瑞典已经通过对cookie立法,要求利用cookie的网站必须说明cookie的属性,并且指导用户如何禁用cookie。
尽管cookie没有病毒那么危险,但它仍包含了一些敏感信息:用户名,计算机名,使用的浏览器和曾经访问的网站。用户不希望这些内容泄漏出去,尤其是当其中还包含有私人信息的时候。
这并非危言耸听,一种名为Cross site scripting的工具可以达到此目的。在受到Cross site scripting攻击时,cookie盗贼和cookie毒药将窃取内容。一旦cookie落入攻击者手中,它将会重现其价值。
cookie盗贼:搜集用户cookie并发给攻击者的黑客。攻击者将利用cookie信息通过合法手段进入用户帐户。
cookie毒药:利用安全机制,攻击者加入代码从而改写cookie内容,以便持续攻击。
在与服务器传输数据时,通过在地址后面添加唯一查询串,让服务器识别是否合法用户,也可以避免使用cookie。
“Cookie”是小量信息,由网络服务器发送出来以存储在网络浏览器上,从而下次这位独一无二的访客又回到该网络服务器时,可从该浏览器读回此信息。这是很有用的,让浏览器记住这位访客的特定信息,如上次访问的位置、花费的时间或用户首选项(如样式表)。Cookie 是个存储在浏览器目录的文本文件,当浏览器运行时,存储在 RAM 中。一旦阁下从该网站或网络服务器退出,Cookie 也可存储在计算机的硬驱上。当访客结束其浏览器对话时,即终止的所有 cookie。
Microsoft 和 Netscape 使用 cookie 在其网站上创建个人起始页。各家公司利用 cookie 的一般用途包括:在线定货系统、网站个人化和网站跟踪。
网站个人化是 cookie 最有益的用途之一。例如,当谁来到 CNN 网站,但并不想查看任何商务新闻。网站允许他将该项选为选项。从那时起(或者直到 cookie 逾期),他在访问 CNN 网页时将不会读到商务新闻。
如果网站设计师旨在使网页能与访客更具互动作用,或者若设计师计划让访客自定义网站的外观,则就需要使用 cookie。而且,如果阁下想要网站在某些情况下改变其外观,cookie 则提供了一条快速、容易的途径,让阁下的 HTML 页面按需要而改变。最新型的服务器使用 cookie 有助于数据库的互动性,进而改进网站的整体互动性。
Cookie是利用了网页代码中的HTTP头信息进行传递的,浏览器的每一次网页请求,都可以伴随Cookie传递,例如,浏览器的打开或刷新网页操作。服务器将Cookie添加到网页的HTTP头信息中,伴随网页数据传回到你的浏览器,浏览器会根据你电脑中的Cookie设置选择是否保存这些数据。如果浏览器不允许Cookie保存,则关掉浏览器后,这些数据就消失。Cookie在电脑上保存的时间是不一样的,这些都是由服务器的设置不同决定的。Cookie有一个Expires(有效期)属性,这个属性决定了Cookie的保存时间,服务器可以通过设定Expires字段的数值,来改变Cookie的保存时间。如果不设置该属性,那么Cookie只在浏览网页期间有效,关闭浏览器,这些Cookie自动消失,绝大多数网站属于这种情况。通常情况下,Cookie包含Server、Expires、Name、value这几个字段,其中对服务器有用的只是Name和value字段,Expires等字段的内容仅仅是为了告诉浏览器如何处理这些Cookies。
多数网页编程语言都提供了对Cookie的支持。如javascript、VBScript、Delphi、ASP、SQL、PHP、C#等。在这些面向对象的编程语言中,对Cookie的编程利用基本上是相似的,大体过程为:先创建一个Cookie对象(Object),然后利用控制函数对Cookie进行赋值、读取、写入等操作。
Flash的代码隐患
Flash中有一个getURL()函数,Flash可以利用这个函数自动打开指定的网页。因此它可能把你引向一个包含恶意代码的网站。打个比方,当你在自己电脑上欣赏精美的Flash动画时,动画帧里的代码可能已经悄悄地连上网,并打开了一个极小的包含有特殊代码的页面。这个页面可以收集你的Cookie、也可以做一些其他的事情,比如在你的机器上种植木马甚至格式化你的硬盘等等。对于Flash的这种行为,网站是无法禁止的,因为这是Flash文件的内部行为。我们所能做到的,如果是在本地浏览尽量打开防火墙,如果防火墙提示的向外发送的数据包并不为你知悉,最好禁止。如果是在Internet上欣赏,最好找一些知名的大网站。
说明:浏览器创建了一个Cookie后,对于每一个针对该网站的请求,都会在Header中带着这个Cookie;不过,对于其他网站的请求Cookie是绝对不会跟着发送的。而且浏览器会这样一直发送,直到Cookie过期为止。
要删除一个已经存在的Cookie,有三个办法:
一是调用只带有name参数的SetCookie,那么名为这个name的Cookie 将被从关系户机上删掉;另一个办
法是设置Cookie的失效时间为time()或time()-1,那么这个Cookie在这个页面的浏览完之后就被删除了(其
实是失效了)。
要注意的是,当一个Cookie被删除时,它的值在当前页在仍然有效的。
Android Cookie:
通过分析com.android.browser的源码,发现android默认的browser增加cookie是在数据库中增加记录,和window 不同,win是采用一个txt文本文件的形式来存储cookie。而android是将cookie存储在数据库中。具体的介绍在《android cookie存储位置》一文中有介绍。我们都知道,android每个应用程序的存储空间都是独立的。不管使用preference还是database存储,都会在每个 /data/data/package name/下面进行存储(preference存储在/data/data/package name/shared_prefs/xxxx.xml)。前面也说到cookie是存在数据库中,那么如果采用非浏览器访问网络需要保留cookie的话我们就应该在database中建立cookies表,并且存入相应的cookies数据。
Http Cookie相关:
//获取cookie信息
List<Cookie> cookies = client.getCookieStore().getCookies();
if (cookies.isEmpty()) {
Log.i(TAG, "-------Cookie NONE---------");
} else {
for (int i = 0; i < cookies.size(); i ) {
//保存cookie
cookie = cookies.get(i);
Log.d(TAG, cookies.get(i).getName() "=" cookies.get(i).getValue() );
//保存登录信息,下次无需登录
String PREFS_NAME = "nma.qztc.com";
SharedPreferences settings = v.getContext().getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("staff_id", username);
editor.putString("pwd", password);
editor.commit();
}
Activity cookie共享:
对于登录功能本身没有任何特别,使用httpclient向服务器post用户名密码即可。
但是为了保持登录的状态(在各个Activity之间切换时要让网站知道用户一直是处于登录的状态)就需要进行cookie的读写。
httpclient相当强大,读写cookie非常容易:
CookieStore cookies=((AbstractHttpClient)client).getCookieStore();//读cookie
((AbstractHttpClient) client).setCookieStore(cookies);//写cookie
另外的一个问题是,为了在各个activity之间使用一个共通的cookie,需要一个全局变量来解决问题。对于java来说,可以使用静态类,但是对于android来说,更符合android结
构的作法是使用拥有这些activity的application类:
首先声明一个application类用来存取cookie:
public class myApp extends Application {
private CookieStore cookies;
public CookieStore getCookie(){
return cookies;
}
public void setCookie(CookieStore cks){
cookies = cks;
}
}
另外,要在AndroidManifest.xml文件中将上面声明的这个类绑定到包含所有activity的application上,方法是为application标签加 android:name=".myApp"
这样就可以在Activity中使用这个“全局变量”了:
//设置cookie
myApp appCookie = ((myApp)getApplication());
appCookie.setCookie(cookies);
//读取cookie
myApp appCookie = ((myApp)getApplicationContext());
cookies = appCookie.getCookie();
Session Cookie的问题:
当Android应用程序访问WEB服务器的时候,我们为了与服务器保持同一会话,也就是说当前登录用户与服务器的交互是在同一个SessionId下。
当我们登录成功的时候,可以通过HTTP请求获取到Cookie信息,其中包括会话的SessionId,同时也可以自己将SessionId放入Json中返回。Session我们可以用一个静态变量来存放,每次向服务器发送请求的时候将SessionId带过去,服务器会自动检验这个SessionId有没有失效。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(访问地址);
httpPost.setHeader("Cookie", "JSESSIONID=" + 我们在静态变量里存放的SessionId);
HttpResponse httpResponse = httpclient.execute(httpPost);
这样就可以将SessionId带过去了。
-----------------------------------------------
Webview Cookie相关:
将cookie信息带入到webview中,之前总是出现有时cookie读取成功有时不成功,找了半天发现将
cookieManager.removeSessionCookie();
这句去掉就好了,暂时还没有出现什么问题,由于原来的web应用是采用session验证,所以在读取cookie成功后也将session信息写入,这样就双保险了.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bind);
//------- Web Browser activity
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
for(String c:cookies)
cookieManager.setCookie(url,c);
CookieSyncManager.getInstance().sync();
webView.loadUrl(url);
}
给urlconnection添加cookie:
String getCookie(Context context){
CookieManager cookieManager = CookieManager.getInstance();
String cookie = cookieManager.getCookie("cookie");
if(cookie != null){
return cookie;
}else{
cookie= “XXX”;
cookieManager.setCookie("cookie", cookie);
return cookie;
}
}
可以看到 我们只要设置在这里面然后再使用的时候我们在自己的httpconnection中添加就行了
URL url = new URL(urlPath);
HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
httpUrlConn.setRequestProperty("cookie", getCookie(context));
这样做 貌似是正确的。。当时我也是这么理解的。。但是很不幸的 是 。。。报错了。。
提示createInstance() must be instance called before getinstance()
很明显 我们必须要首先产生这个实例。。。结果查了一下API 需要在 getInstance()之前先
CookieSyncManager.createInstance(context);
这样之后就不会出错了。。createInstance 之后保证了我们的。。webkit在使用CookieManager的时候 使用的是同一个CookieManager并保证了 线程同步。。。。
这一点我们可以参看CookieSyncManager.createInstance(context);的源码。。。。。
webview 删除保存于手机上的缓存、cookie:
查看root过的手机data下的文件,会发现有这个东西:webview命名的东西
删除保存于手机上的缓存.
// clear the cache before time numDays
private int clearCacheFolder(File dir, long numDays) {
int deletedFiles = 0;
if (dir!= null && dir.isDirectory()) {
try {
for (File child:dir.listFiles()) {
if (child.isDirectory()) {
deletedFiles += clearCacheFolder(child, numDays);
}
if (child.lastModified() < numDays) {
if (child.delete()) {
deletedFiles++;
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
return deletedFiles;
}
打开关闭使用缓存
//优先使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//不使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
在退出应用的时候加上如下代码
File file = CacheManager.getCacheFileBaseDir();
if (file != null && file.exists() && file.isDirectory()) {
for (File item : file.listFiles()) {
item.delete();
}
file.delete();
}
context.deleteDatabase("webview.db");
context.deleteDatabase("webviewCache.db");
Android的CookieManager只提供了removeAllCookies方法,用来删除所有的cookie,有什么办法只删除和特定url关联的cookie呢?本来打算使用setCookie(url, value)将指定url关联的cookie设为空串,但试了一下发现这个方法只是在已有的基础上继续添加cookie,并不能重置已有的cookie。
有朋友给打答案:
/**
* 同步一下cookie
*/
public static void synCookies(Context context, String url) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();//移除
cookieManager.setCookie(url, cookies);//指定要修改的cookies
CookieSyncManager.getInstance().sync();
}
CookieManager的底层实现:
1. 取Cookie的API
view sourceprint?1.CookieManager.getInstance().getCookie(url);
2.CookieManager的getInstance()
02.* Gets the singleton CookieManager instance. If this method is used
03.* before the application instantiates a {@link WebView} instance,
04.* {@link CookieSyncManager#createInstance(Context)} must be called
05.* first.
06.*
07.* @return the singleton CookieManager instance
08.*/
09.public static synchronized CookieManager getInstance() {
10.return WebViewFactory.getProvider().getCookieManager();
11.}
3.WebViewFactory是个工厂模式,额
view sourceprint?01.static synchronized WebViewFactoryProvider getProvider() {
02.// For now the main purpose of this function (and the factory abstraction) is to keep
03.// us honest and minimize usage of WebViewClassic internals when binding the proxy.
04.if (sProviderInstance != null) return sProviderInstance;
05.
06.sProviderInstance = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY);
07.if (sProviderInstance == null) {
08.if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage");
09.sProviderInstance = new WebViewClassic.Factory();
10.}
11.return sProviderInstance;
12.}
4.层层嵌套,最后生成的instance是CookieManagerClassic
2.public CookieManager getCookieManager() {
3.return CookieManagerClassic.getInstance();
4.}
5. getCookie方法 实现在CookieManagerClassic上
02.public String getCookie(String url) {
03.return getCookie(url, false);
04.}
05.
06.@Override
07.public String getCookie(String url, boolean privateBrowsing) {
08.WebAddress uri;
09.try {
10.uri = new WebAddress(url);
11.} catch (ParseException ex) {
12.Log.e(LOGTAG, "Bad address: " + url);
13.return null;
14.}
15.
16.return nativeGetCookie(uri.toString(), privateBrowsing);
17.}
6. nativeGetCookie定义在CookieManager.cpp里, 恩。。。。JNI。。。
view sourceprint?1.static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing)
2.{
3.GURL gurl(jstringToStdString(env, url));
4.CookieOptions options;
5.options.set_include_httponly();
6.std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options);
7.return stdStringToJstring(env, cookies);
8.}
7. WebCookieJar.cpp中,定义了从哪里去取得Cookie情报
view sourceprint?01.WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing)
02.{
03.MutexLocker lock(instanceMutex);
04.if (!isFirstInstanceCreated && fileSchemeCookiesEnabled)
05.net::CookieMonster::EnableFileScheme();
06.isFirstInstanceCreated = true;
07.scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
08.if (!instancePtr->get())
09.*instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing));
10.return instancePtr->get();
11.}
12.
13.static std::string databaseDirectory(bool isPrivateBrowsing)
14.{
15.static const char* const kDatabaseFilename = "/webviewCookiesChromium.db";
16.static const char* const kDatabaseFilenamePrivateBrowsing ="/webviewCookiesChromiumPrivate.db";
17.
18.std::string databaseFilePath = databaseDirectory();
19.databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename);
20.return databaseFilePath;
21.}
8.最后读取Cookie情报发生在cookie_monster.cc。 额,最后调到C++, 去读.db文件,无敌了
view sourceprint?01.std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
02.const CookieOptions& options) {
03.base::AutoLock autolock(lock_);
04.InitIfNecessary();
05.
06.if (!HasCookieableScheme(url)) {
07.return std::string();
08.}
09.
10.TimeTicks start_time(TimeTicks::Now());
11.
12.// Get the cookies for this host and its domain(s).
13.std::vector<CanonicalCookie*> cookies;
14.FindCookiesForHostAndDomain(url, options, true, &cookies);
15.std::sort(cookies.begin(), cookies.end(), CookieSorter);
16.
17.std::string cookie_line;
18.for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin();
19.it != cookies.end(); ++it) {
20.if (it != cookies.begin())
21.cookie_line += "; ";
22.// In Mozilla if you set a cookie like AAAA, it will have an empty token
23.// and a value of AAAA. When it sends the cookie back, it will send AAAA,
24.// so we need to avoid sending =AAAA for a blank token value.
25.if (!(*it)->Name().empty())
26.cookie_line += (*it)->Name() + "=";
27.cookie_line += (*it)->Value();
28.}
29.
30.histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
31.
32.VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line;
33.
34.return cookie_line;
35.}
Cookie异步存储的原理:
CookieSyncManager继承 WebSyncManager, 是负 责cookie从内存到持久存储的 类,cookie 的同步可以分为自动同步和强制同步两种.
1. 自动同步
自动同步 是每过5分钟同步一次,是开启一个线程来负责ram到 database的同步,这个线程维护消息队列,线程启动时发一个delay时间为SYNC_LATER_INTERVAL(5min)的 消息
Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_LATER_INTERVAL);
每次处理完这个消息会添加另外一个delay为5min的消息。
2.强制同步
强制同步调用sync()接口
public void sync() {
if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager sync ***");
}
if (mHandler == null) {
return;
}
mHandler.removeMessages(SYNC_MESSAGE);
Message msg = mHandler.obtainMessage(SYNC_MESSAGE);
mHandler.sendMessageDelayed(msg, SYNC_NOW_INTERVAL);
}
sync方法是触发一个delay为100毫秒的消息。
不管哪种,cookie保存是通过另外一个线程的Handler来实现的,需要Looper到这个消息才去做持久化操作,是 异步实现。