一、简介

Cookie会包含如下信息:name expires domain path secure;

name:cookie 的名字;

expires:过期时间。值是一个日期,一个时刻,而不是一个时长。在OkHttp中,你可以使用该字段在端上建立逻辑,也可以忽略该字段依靠server实现过期的逻辑。

domain:cookie的作用域,指定了cookie将要被发送至哪个域中。默认情况下,domain会被设置为创建该cookie的url所在的域名。但在OkHttp中默认是不存在Cookie机制的,因此这一点需要你来亲自实现完善。像百度这样的网站,会有很多name.baidu.com形式的站点,他们的顶级域名是一致的,但二级域名会有很多,比如waimai.baidu.com,bzclk.baidu.com等。domain的匹配通常是从域名的末尾开始匹配,并将命中的cookie作为有效cookie存储。

path:另一个控制cookie的发送时机的选项。类似于domain,path选项要求请求资源URL中必须存在指定的路径,才会发送cookie。通常是将path的值与请求的URL从开头开始逐个字符串比较完成匹配。如:Set-Cookie:name=Ghost;path=/ghost就要求URL的路径以/ghost开头,如/ghost,/ghostinmatrix都是命中的url。

secure:该选项只是一给标记而没有值。只有当一个请求通过SSL或者HTTPS创建的时候,包含secure的cookie才能被发送至服务器。这种cookie内容具有很高价值,如果一纯文本形式传递很有可能被篡改。事实上,机密且敏感的数据是不应该再cookie中存储的,因为cookie整个机制本身就是不安全的。

二、OkHttp的Cookie的说明及调用

OkHttp网络库提供了自定义 CookieJar机制来满足应用对Cookie的各种定制需求。在OkHttp的源码中,明确了两点:

1.不自定义CookieJar时默认没有Cookie, 如下:

/** A cookie jar that never accepts any cookies. */CookieJar NO_COOKIES = new CookieJar() {@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {}@Override public List<Cookie> loadForRequest(HttpUrl url) {return Collections.emptyList();}};

2. 在设置自定义CookieJar时,需要实现该接口的2个方法:

public class MyCookieJarImpl implements CookieJar {private final PersistentCookieStore cookieStore = PersistentCookieStore.getInstance();public MyCookieJarImpl (OkCookieManager cookieManager) {this.cookieManager = cookieManager;}@Overridepublic void saveFromResponse(HttpUrl url, List<Cookie> cookies) {//本地可校验cookie,并根据需要存储if (cookies != null && cookies.size() > 0) {for (Cookie item : cookies) {cookieStore.add(url, item);}}}@Overridepublic List<Cookie> loadForRequest(HttpUrl url) {//从本地拿取需要的cookiereturn cookieStore.get(url);;}
}

OkHttp在组装请求过程中,HttpEngine会调用CookieJar的loadForRequest方法,

private Request networkRequest(Request request) throws IOException {......List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());if (!cookies.isEmpty()) {result.header("Cookie", cookieHeader(cookies));}.....return result.build();}

其中的client就是 设置了CookieJar的OkHttpClient;

在请求接口的时候,还是在HttpEngine中,使用saveFromResponse方法将来自server的cookie存储到本地:

public void receiveHeaders(Headers headers) throws IOException {if (client.cookieJar() == CookieJar.NO_COOKIES) return;/***从Header中获取Cookie并解析为对象,如果Cookie存在,则saveFromResponse*一般app登录的时候headers中会有Cookie,而其他的接口的headers不包含Cookie*/List<Cookie> cookies = Cookie.parseAll(userRequest.url(), headers);if (cookies.isEmpty()) return;client.cookieJar().saveFromResponse(userRequest.url(), cookies);}

以上就是CookieJar的设置就调用流程,下面看看怎么将Cookie存储到本地,以下代码都很好理解,都要注释了,我就直接贴代码了,有问题的可以交流下:

public class PersistentCookieStore {private static final String LOG_TAG = "PersistentCookieStore";private static final String COOKIE_PREFS = "Cookies_Prefs";private final Map<String, Map<String, Cookie>> cookies;private final SharedPreferences cookiePrefs;private static PersistentCookieStore mInstance;public static PersistentCookieStore getInstance() {if(mInstance == null) {mInstance = new PersistentCookieStore(BaseApp.getInstance());}return mInstance;}private PersistentCookieStore(Context context) {cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);cookies = new HashMap<>();//将持久化的cookies缓存到内存中 即map cookiesMap<String, ?> prefsMap = cookiePrefs.getAll();for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");for (String name : cookieNames) {String encodedCookie = cookiePrefs.getString(name, null);if (encodedCookie != null) {Cookie decodedCookie = decodeCookie(encodedCookie);if (decodedCookie != null) {if (!cookies.containsKey(entry.getKey())) {cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());}cookies.get(entry.getKey()).put(name, decodedCookie);}}}}}protected String getCookieToken(Cookie cookie) {return cookie.name() + "@" + cookie.domain();}public synchronized void add(HttpUrl url, Cookie cookie) {String name = getCookieToken(cookie);//将cookies缓存到内存中 如果缓存过期 就重置此cookieif (!cookie.persistent() || cookie.expiresAt() > System.currentTimeMillis()) {if (!cookies.containsKey(url.host())) {cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());}cookies.get(url.host()).put(name, cookie);} else {if (cookies.containsKey(url.host())) {cookies.get(url.host()).remove(name);}}//讲cookies持久化到本地if (cookies.get(url.host()) != null) {SharedPreferences.Editor prefsWriter = cookiePrefs.edit();prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));prefsWriter.putString(name, encodeCookie(new SerializableOkHttpCookies(cookie)));prefsWriter.apply();}}public List<Cookie> get(HttpUrl url) {ArrayList<Cookie> ret = new ArrayList<>();if (cookies.containsKey(url.host()))ret.addAll(cookies.get(url.host()).values());return ret;}public synchronized boolean removeAll() {SharedPreferences.Editor prefsWriter = cookiePrefs.edit();prefsWriter.clear();prefsWriter.apply();cookies.clear();return true;}public synchronized boolean remove(HttpUrl url, Cookie cookie) {String name = getCookieToken(cookie);if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) {cookies.get(url.host()).remove(name);SharedPreferences.Editor prefsWriter = cookiePrefs.edit();if (cookiePrefs.contains(name)) {prefsWriter.remove(name);}prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));prefsWriter.apply();return true;} else {return false;}}public synchronized List<Cookie> getCookies() {ArrayList<Cookie> ret = new ArrayList<>();for (String key : cookies.keySet())ret.addAll(cookies.get(key).values());return ret;}/*** cookies 序列化成 string** @param cookie 要序列化的cookie* @return 序列化之后的string*/protected String encodeCookie(SerializableOkHttpCookies cookie) {if (cookie == null)return null;ByteArrayOutputStream os = new ByteArrayOutputStream();try {ObjectOutputStream outputStream = new ObjectOutputStream(os);outputStream.writeObject(cookie);} catch (IOException e) {l.d(LOG_TAG, "IOException in encodeCookie", e);return null;}return byteArrayToHexString(os.toByteArray());}/*** 将字符串反序列化成cookies** @param cookieString cookies string* @return cookie object*/protected Cookie decodeCookie(String cookieString) {byte[] bytes = hexStringToByteArray(cookieString);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);Cookie cookie = null;try {ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);cookie = ((SerializableOkHttpCookies) objectInputStream.readObject()).getCookies();} catch (IOException e) {l.d(LOG_TAG, "IOException in decodeCookie", e);} catch (ClassNotFoundException e) {l.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);}return cookie;}/*** 二进制数组转十六进制字符串** @param bytes byte array to be converted* @return string containing hex values*/protected String byteArrayToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder(bytes.length * 2);for (byte element : bytes) {int v = element & 0xff;if (v < 16) {sb.append('0');}sb.append(Integer.toHexString(v));}return sb.toString().toUpperCase(Locale.US);}/*** 十六进制字符串转二进制数组** @param hexString string of hex-encoded values* @return decoded byte array*/protected byte[] hexStringToByteArray(String hexString) {int len = hexString.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));}return data;}
}

Cookie持久化

如果Cookie仅存在于内存中,那么App关闭之后,所有cookie就都消失;而我们期望的是下次打开App的时候依然能够自动登录进入主界面,这就需要我们对Cookie进行文件级别的持久化。但是,okhttp3.Cookie有一个很坑爹的情况:它没有实现Serializable接口,无法序列化。因此,我们只能自己实现序列化:

public class SerializableOkHttpCookies implements Serializable {private transient final Cookie cookies;private transient Cookie clientCookies;public SerializableOkHttpCookies(Cookie cookies) {this.cookies = cookies;}public Cookie getCookies() {Cookie bestCookies = cookies;if (clientCookies != null) {bestCookies = clientCookies;}return bestCookies;}private void writeObject(ObjectOutputStream out) throws IOException {out.writeObject(cookies.name());out.writeObject(cookies.value());out.writeLong(cookies.expiresAt());out.writeObject(cookies.domain());out.writeObject(cookies.path());out.writeBoolean(cookies.secure());out.writeBoolean(cookies.httpOnly());out.writeBoolean(cookies.hostOnly());out.writeBoolean(cookies.persistent());}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {String name = (String) in.readObject();String value = (String) in.readObject();long expiresAt = in.readLong();String domain = (String) in.readObject();String path = (String) in.readObject();boolean secure = in.readBoolean();boolean httpOnly = in.readBoolean();boolean hostOnly = in.readBoolean();boolean persistent = in.readBoolean();Cookie.Builder builder = new Cookie.Builder();builder = builder.name(name);builder = builder.value(value);builder = builder.expiresAt(expiresAt);builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);builder = builder.path(path);builder = secure ? builder.secure() : builder;builder = httpOnly ? builder.httpOnly() : builder;clientCookies =builder.build();}
}

OKHTTP3自定义CookieJar和及Cookie持久化相关推荐

  1. OKHttp3 Cookie 持久化

    OkHttp3 的 Cookie 持久化其实网上的文章很多.我写这篇文章只是为了下次再做Cookie持久化的时候可以拿来就用. CookieJar 就是OkHttp3 提供用来获取Cookie的接口: ...

  2. CookieJar模块管理Cookie

    CookieJar:管理HTTP cookie值.存储HTTP请求生成的cookie.向传出的HTTP请求添加cookie的对象.整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后 ...

  3. 跨平台微信网络开源Mars与网络框架Okhttp、Volley、Retrofit,Cookie持久化

    > 1. Mars 移动端IM网络层跨平台组件库Mars- https://github.com/Tencent/mars Android.iOS.OS X 平台的 demo(微信开源Mars的 ...

  4. goahead 的认证和自定义登陆页面的cookie使用【原创】

    Author:张继飞 goahead认证,允许下面几种方式. NONE - the URL page cannot be accessed.  FULL - the URL can always be ...

  5. cookie的持久化管理

    okhttp自带一个cookie管理器 public static OkHttpClient getClient() {if (client == null) {synchronized (OkHtt ...

  6. python cookiejar_Python http.cookiejar模块:管理cookie

    通过前面章节的学习,使用 urlopen() 既可发送 GET 请求,也可发送 POST.PUT.DELETE.PATCH 等请求.因此,在绝大部分时候,完全可以使用 urllib.request 模 ...

  7. Android持久化保存cookie

    在解析网页信息的时候,需要登录后才能访问,所以使用httpclient模拟登录,然后把cookie保存下来,以供下一次访问使用,这时就需要持久化cookie中的内容. 在之前先科普一下基础知识: 什么 ...

  8. 爬虫-利用Cookiejar类对象获取登陆后的cookie信息-人人网旧版的模拟登陆演练

    http.cookiejar模块 演练-动态获取cookie然后访问个人中心 目标:构建相关的对象 流程: 创建cookiejar对象 创建cookie处理器对象 创建打开器对象 构建请求头(包含了u ...

  9. [Python]网络爬虫(三):使用cookiejar管理cookie 以及 模拟登录知乎

    大家好哈,上一节我们研究了一下爬虫的异常处理问题,那么接下来我们一起来看一下Cookie的使用. 为什么要使用Cookie呢? Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在 ...

最新文章

  1. php下curl与file_get_contents性能对比
  2. STM32的时钟系统RCC详细整理
  3. XenApp_XenDesktop_7.6实战篇之十六:安装Virtual Delivery Agent For Windows Server OS
  4. JavaScript计时器函数用法
  5. 【英语学习】【English L06】U02 Food L1 Food on the menu
  6. 复杂关联SQL的优化
  7. 如何用管程实现生产者消费者问题?
  8. python利用gzip压缩解压缩StringIO
  9. websocket 初识
  10. 图书管理系统数据库设计
  11. 蓝桥杯 算法提高 矩阵乘方
  12. 阿里云商标注册申请进度查询方法
  13. 移动端图片居多,加载过慢,使用延迟加载|懒加载( lazyload.js)
  14. 平衡运输问题及其表上作业法---指派问题及其匈牙利解法
  15. SQL Server无法连接到本地数据库
  16. IDEA 2021/2022 修改启动画面及设置编辑器背景图片
  17. Qt:配置Qt Creator
  18. 拉扎维模集英文原版阅读笔记1
  19. jython使用_使用Jython收集数据
  20. (转)Java RMI远程方法调用详解

热门文章

  1. Echarts 简单实现地图省 => 市 => 区 => 镇街 下钻
  2. java socket http请求_JAVA中利用socket实现HTTP请求
  3. 【竞赛经历】CSDN第39期竞赛题解
  4. 网络安全运维掌握这十点核心能力就够了吗?
  5. Linux系统迅速cd进入指定路径,快捷路径设置
  6. IDEA 断点调试快捷键
  7. Java面向对象相对于面向过程的优势?以及java创建对象的4中方式?
  8. A10负载均衡ACOS固件(包含AX、Thunder系列)
  9. OSChina 周六乱弹 ——忙,要有孙悟空的神通化身无数就好了。
  10. 数据库中事务是什么意思?