OKHTTP3自定义CookieJar和及Cookie持久化
一、简介
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持久化相关推荐
- OKHttp3 Cookie 持久化
OkHttp3 的 Cookie 持久化其实网上的文章很多.我写这篇文章只是为了下次再做Cookie持久化的时候可以拿来就用. CookieJar 就是OkHttp3 提供用来获取Cookie的接口: ...
- CookieJar模块管理Cookie
CookieJar:管理HTTP cookie值.存储HTTP请求生成的cookie.向传出的HTTP请求添加cookie的对象.整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后 ...
- 跨平台微信网络开源Mars与网络框架Okhttp、Volley、Retrofit,Cookie持久化
> 1. Mars 移动端IM网络层跨平台组件库Mars- https://github.com/Tencent/mars Android.iOS.OS X 平台的 demo(微信开源Mars的 ...
- goahead 的认证和自定义登陆页面的cookie使用【原创】
Author:张继飞 goahead认证,允许下面几种方式. NONE - the URL page cannot be accessed. FULL - the URL can always be ...
- cookie的持久化管理
okhttp自带一个cookie管理器 public static OkHttpClient getClient() {if (client == null) {synchronized (OkHtt ...
- python cookiejar_Python http.cookiejar模块:管理cookie
通过前面章节的学习,使用 urlopen() 既可发送 GET 请求,也可发送 POST.PUT.DELETE.PATCH 等请求.因此,在绝大部分时候,完全可以使用 urllib.request 模 ...
- Android持久化保存cookie
在解析网页信息的时候,需要登录后才能访问,所以使用httpclient模拟登录,然后把cookie保存下来,以供下一次访问使用,这时就需要持久化cookie中的内容. 在之前先科普一下基础知识: 什么 ...
- 爬虫-利用Cookiejar类对象获取登陆后的cookie信息-人人网旧版的模拟登陆演练
http.cookiejar模块 演练-动态获取cookie然后访问个人中心 目标:构建相关的对象 流程: 创建cookiejar对象 创建cookie处理器对象 创建打开器对象 构建请求头(包含了u ...
- [Python]网络爬虫(三):使用cookiejar管理cookie 以及 模拟登录知乎
大家好哈,上一节我们研究了一下爬虫的异常处理问题,那么接下来我们一起来看一下Cookie的使用. 为什么要使用Cookie呢? Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在 ...
最新文章
- php下curl与file_get_contents性能对比
- STM32的时钟系统RCC详细整理
- XenApp_XenDesktop_7.6实战篇之十六:安装Virtual Delivery Agent For Windows Server OS
- JavaScript计时器函数用法
- 【英语学习】【English L06】U02 Food L1 Food on the menu
- 复杂关联SQL的优化
- 如何用管程实现生产者消费者问题?
- python利用gzip压缩解压缩StringIO
- websocket 初识
- 图书管理系统数据库设计
- 蓝桥杯 算法提高 矩阵乘方
- 阿里云商标注册申请进度查询方法
- 移动端图片居多,加载过慢,使用延迟加载|懒加载( lazyload.js)
- 平衡运输问题及其表上作业法---指派问题及其匈牙利解法
- SQL Server无法连接到本地数据库
- IDEA 2021/2022 修改启动画面及设置编辑器背景图片
- Qt:配置Qt Creator
- 拉扎维模集英文原版阅读笔记1
- jython使用_使用Jython收集数据
- (转)Java RMI远程方法调用详解