Cookie介绍及在Android中的使用总结

Cookie介绍
cookie的起源
早期Web开发面临的最大问题之一是如何管理状态。简言之,服务器端没有办法知道两个请求是否来自于同一个浏览器。那时的办法是在请求的页面中插入一个token,并且在下一次请求中将这个token返回(至服务器)。这就需要在form中插入一个包含token的隐藏表单域,或着在URL的qurey字符串中传递该token。这两种办法都强调手工操作并且极易出错。

Lou Montulli(卢·蒙特利),那时是网景通讯的一个雇员,被认为在1994年将“magic cookies”的概念应用到了web通讯中。他意图解决的是web中的购物车,现在所有购物网站都依赖购物车。他的最早的说明文档提供了一些cookies工作原理的基本信息该文档在RFC2109中被规范化(这是所有浏览器实现cookies的参考依据),并且最终逐步形成了REF2965.Montulli最终也被授予了关于cookies的美国专利。网景浏览器在它的第一个版本中就开始支持cookies,并且当前所有web浏览器都支持cookies。

最早的时候是RFC2109协议,目前最新的是RFC6265协议,想详细了解的可以去看看文档:

RFC2109 https://tools.ietf.org/html/rfc2109

RFC2965 https://tools.ietf.org/html/rfc2965

RFC6265 https://tools.ietf.org/html/rfc6265

cookie是什么
Cookie是服务器保存在浏览器的一小段文本信息,每个 Cookie 的大小一般不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。

cookie的用途
会话管理
1.1 记录用户的登录状态是cookie最常用的用途。通常web服务器会在用户登录成功后下发一个签名来标记session的有效性,这样免去了用户多次认证和登录网站。

1.2 记录用户的访问状态,例如导航啊,用户的注册流程啊。

个性化信息
2.1 Cookie也经常用来记忆用户相关的信息,以方便用户在使用和自己相关的站点服务。例如:ptlogin会记忆上一次登录的用户的QQ号码,这样在下次登录的时候会默认填写好这个QQ号码。
2.2 Cookie也被用来记忆用户自定义的一些功能。用户在设置自定义特征的时候,仅仅是保存在用户的浏览器中,在下一次访问的时候服务器会根据用户本地的cookie来表现用户的设置。例如google将搜索设置(使用语言、每页的条数,以及打开搜索结果的方式等等)保存在一个COOKIE里。

记录用户的行为
最典型的是公司的TCSS系统。它使用Cookie来记录用户的点击流和某个产品或商业行为的操作率和流失率。当然功能可以通过IP或http header中的referrer实现,但是Cookie更精准一些。

创建cookie
服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段。Set-Cookie消息的格式如下面的字符串(中括号中的部分都是可选的)。

Set-Cookie:name=value [ ;expires=date][ ;max-age=time][ ;domain=domain][ ;path=path][ ;secure][ ;httponly]

上面可选的字段是Cookie的属性,一个Set-Cookie字段里面,可以同时包括多个属性,没有次序的要求。如下一个例子:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

如果服务器想改变一个早先设置的 Cookie,必须同时满足四个条件:Cookie 的key、domain、path和secure都匹配。只要有一个属性不同,就会生成一个全新的 Cookie,而不是替换掉原来那个 Cookie。举例来说,如果原始的 Cookie 是用如下的Set-Cookie设置的。

Set-Cookie: key1=value1; domain=example.com; path=/blog

改变上面这个 Cookie 的值,就必须使用同样的Set-Cookie。

Set-Cookie: key1=value2; domain=example.com; path=/blog

HTTP 回应可以包含多个Set-Cookie字段,即在浏览器生成多个 Cookie,如下。

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

cookie的属性
##### Expires,Max-Age

Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式。可以通过设置它的expires属性为一个过去的日期来删除这个cookie。

Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。

如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。

如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。

Domain
Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前 URL 的一级域名,比如www.example.com会设为.example.com,而且以后如果访问.example.com的任何子域名,HTTP 请求也会带上这个 Cookie。如果服务器在Set-Cookie字段指定的域名,不属于当前域名,浏览器会拒绝这个 Cookie。

RFC2109规定domain必须满足以.开头。

Path
Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致。path属性的默认值是发送Set-Cookie消息头所对应的URL中的path部分。

Secure
Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器,以确保cookie在从客户端传递到Server的过程中始终加密的。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。

HttpOnly
HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是Document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。

发送cookie
当一个cookie存在,并且可选条件允许的话,该cookie的值会在接下来的每个请求中被发送至服务器。cookie的值被存储在名为Cookie的HTTP消息头中,例如:

Cookie : name=value

​    如果在指定的请求中有多个cookies,那么它们会被分号和空格分开,例如:

Cookie:name1=value1 ; name2=value2 ; name3=value3

Cookie缺陷
Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。

由于在HTTP请求中的Cookie是明文传递的,所以安全性成问题,除非用HTTPS。

Cookie的大小限制在4KB左右,对于复杂的存储需求来说是不够用的。

Cookie在Android中的使用
Cookie持久化
持久化保存cookie有很多方式,可以用数据库,可以用文件,SharedPreferences,还可以保存到系统Webview的CookieManager里(其实也是个数据库)。

如果我们自己本地保存cookie,要做好本地Cookie和Webview的cookie同步,所以最好的办法是把本地请求获得的Cookie也保存到系统Webview的CookieManager里,取的时候从Webview的CookieManager里取,让CookieManager统一管理岂不美滋滋,哈哈。

PersistentCookieJar是一个持久化在SharedPreferences中的例子,代码也不复杂,大家可以看看:
https://github.com/franmontiel/PersistentCookieJar A persistent CookieJar implementation for OkHttp 3 based on SharedPreferences.

OkHttp3.0 中实现 Cookie 持久化管理
3.0之后OKHttp是加了CookieJar和Cookie两个类的,通过实现CookieJar即可管理cookie。

private class CookiesManager implements CookieJar {

    private final PersistentCookieStore cookieStore = new PersistentCookieStore(getApplicationContext());

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        if (cookies != null && cookies.size() > 0) {
            for (Cookie item : cookies) {
                cookieStore.add(url, item);
            }
        }
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        List<Cookie> cookies = cookieStore.get(url);
        return cookies;
    }
}

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cookieJar(new CookiesManager());


WebView中的Cookie机制
WebView是基于webkit内核的UI控件,相当于一个浏览器客户端。它会在本地维护每次会话的cookie(保存在data/data/package_name/app_WebView/Cookies),如下图:


数据就保存在Cookies那个文件里,其实是个数据库,把后缀改成.db用数据库打开可以看到里面的表结构,主要有host_key, name, value, path等,host_key其实就是前面说的domain,这些字段其实也都是前面说的属性:


当WebView加载URL的时候,WebView会从本地读取该URL对应的cookie,并携带该cookie与服务器进行通信。WebView通过android.webkit.CookieManager类来维护cookie。CookieManager是 WebView的cookie管理类。

之前同步cookie需要用到CookieSyncManager类,现在这个类已经被deprecated。如今WebView已经可以在需要的时候自动同步cookie了。

CookieSyncManager

在安卓5.0以下,主要使用CookieSyncManager在内存和存储器之间同步浏览器的cookie,另外CookieSyncManager同步策略是在一个独立的线程里定时进行同步。

cookie开始同步:注意每次同步的时间间隔是5分钟

CookieSyncManager.createInstance(context); 
CookieSyncManager.getInstance().startSync();

cookie停止同步:

CookieSyncManager.getInstance().stopSync()

cookie立即同步:调用了该方法会立即进行cookie的同步,代码如下:

//一般是在webview中的onPageFinished(WebView, String)方法进行强制同步
CookieSyncManager.getInstance().sync()

删除cookie操作:

CookieSyncManager.createInstance(this); 
CookieManager.getInstance().removeAllCookie(); 
CookieManager.getInstance().removeSessionCookie();
CookieSyncManager.getInstance().sync(); 

CookieManager

从5.0之后,webview已经内置了cookie的同步操作了。

删除所有Cookie

CookieManager.getInstance().removeAllCookies(null); 
CookieManager.getInstance().flush();

保存Cookie

CookieManager.getInstance().setCookie(String url, String value)

获取Cookie

CookieManager.getInstance().getCookie(url)

我们综合两个Manager, 最后写法:

同步Cookie

CookieSyncManager.createInstance(this); 
if (Build.VERSION.SDK_INT < 21) {
    CookieSyncManager.getInstance().sync();
} else {
    CookieManager.getInstance().flush();
}

删除所有Cookie

CookieSyncManager.createInstance(this); 
CookieManager.getInstance().removeAllCookie(); 
CookieManager.getInstance().removeSessionCookie(); 
if (Build.VERSION.SDK_INT < 21) {
    CookieSyncManager.getInstance().sync();
} else {
    CookieManager.getInstance().flush();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值