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.1 记录用户的登录状态是cookie最常用的用途。通常web服务器会在用户登录成功后下发一个签名来标记session的有效性,这样免去了用户多次认证和登录网站。

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

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

  3. 记录用户的行为
    最典型的是公司的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 的keydomainpathsecure都匹配。只要有一个属性不同,就会生成一个全新的 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。

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

如果Set-Cookie字段没有指定ExpiresMax-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缺陷

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

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

  3. 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同步策略是在一个独立的线程里定时进行同步。

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

    CookieSyncManager.createInstance(context); 
    CookieSyncManager.getInstance().startSync();
    
  2. cookie停止同步:

    CookieSyncManager.getInstance().stopSync()
    
  3. cookie立即同步:调用了该方法会立即进行cookie的同步,代码如下:

    //一般是在webview中的onPageFinished(WebView, String)方法进行强制同步
    CookieSyncManager.getInstance().sync()
    
  4. 删除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();
}

猜你喜欢

转载自blog.csdn.net/Kelaker/article/details/82751287