Retrofit2.0 ,OkHttp3完美同步持久Cookie实现免登录(二)
原文出自csdn:
http://blog.csdn.net/sk719887916/article/details/51700659;
通过对Retrofit2.0的<Retrofit 2.0 超能实践,完美支持Https传输>基础入门和案例实践,掌握了怎么样使用Retrofit访问网络,加入自定义header,包括加入SSL证书,基本的调试基础,但是正常的开发中会涉及cookie同步问题,可以实现一些自动或免登录登陆问题,接下来进入cookie同步姿势
Cookie
Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术。Cookies是当你浏览某网站时,由Web服务器置于你硬盘上的一个非常小的文本文件,它可以记录你的用户ID、密码、浏览过的网页、停留的时间等信息。当你再次来到该网站时,网站通过读取Cookies,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。
从本质上讲,它可以看作是你的身份证。但Cookies不能作为代码执行,也不会传送病毒,且为你所专有,并只能由提供它的服务器来读取。保存的信息片断以“名/值”对(name-value pairs)的形式储存,一个“名/值”对仅仅是一条命名的数据。一个网站只能取得它放在你的电脑中的信息,它无法从其它的Cookies文件中取得信息,也无法得到你的电脑上的其它任何东西。
Cookies中的内容大多数经过了加密处理,因此一般用户看来只是一些毫无意义的字母数字组合,只有服务器的CGI处理程序才知道它们真正的含义。
Cookie也是http的会话跟踪技术,也包含web端的session。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力.
Cookie结构
发送规则:
浏览器(设备)根据下面的几个规则决定是否发送某个Cookie信息:
- 请求的主机名是否与某个存储的Cookie的Domain属性匹配;
- 请求的端口号是否在该Cookie的Port属性列表中;
- 请求的资源路径是否在该Cookie的Path属性指定的目录及子目录中;
该Cookie的有效期是否已过。
下面是cookie代码示例:
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value Set-Cookie: name2=value2; Expires=Wed, 09 Jun 2021 10:18:14 GMT (content of page)Cookie请求头字段中的每个Cookie之间用逗号或分号分隔。
在Cookie请求头字段中除了必须有“名称=值”的设置外,还可以有Version、Path、Domain、Port等属性名之前,都要增加一个“$”字符作为前缀。
Version属性只能出现一次,且要位于Cookie请求头字段设置值的最前面,如果需要设置鞭个Cookie信息的Path、Domain、Port等属性,它们必须位于该Cookie信息的“名称=值”设置之后。
Path属性指向子目录的Cookie排在Path属性指向父目录的Cookie之前。
在Servlet程序中作用Cookie
Servlet API中提供了一个javax.servlet.http.Cookie类来封闭Cookie信息,它包含有生成Cookie信息和提取Cookie信息的各个属性的方法。
Cookie类的方法:
- (构造方法:public Cookie(java.lang.String name,java.lang.String value)
- getName方法
- setValue与getValue方法
- setMaxAge与getMaxAge方法
- setPath与getPath方法
- setDomian与getPath方法
- setVersion与getVersion方法
- setComment与getComment方法
- setSecoure与getSecure方法
自定义cookie
HttpClient中大家都知道加入cookie的方式
AsyncHttpClient client = new AsyncHttpClient();
PersistentCookieStore myCookieStore = new PersistentCookieStore(MainActivity.this);
client.setCookieStore(myCookieStore);
因此Retrofit中需自我实现一个PersistentCookieStore 用来储存OkHttpCookies。
方案一:
-PersistentCookieStore
/**
* Created by LIUYONGKUI on 2016-06-09.
*/
public class PersistentCookieStore {
private static final String LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "Cookies_Prefs";
private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
private final SharedPreferences cookiePrefs;
public PersistentCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中 即map cookies
Map<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 void add(HttpUrl url, Cookie cookie) {
String name = getCookieToken(cookie);
//将cookies缓存到内存中 如果缓存过期 就重置此cookie
if (!cookie.persistent()) {
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持久化到本地
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(name, encodeCookie(new OkHttpCookies(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 boolean removeAll() {
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
cookies.clear();
return true;
}
public 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 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(OkHttpCookies cookie) {
if (cookie == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (IOException e) {
Log.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 = ((OkHttpCookies) objectInputStream.readObject()).getCookies();
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
Log.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;
}
- OkHttpCookies
需要实现序列化的 OkHttpCookies 用来持久OkHttpCookies
/**
* Created by LIUYONGKUI on 2016-05-20.
*/
public class OkHttpCookies implements Serializable {
private transient final Cookie cookies;
private transient Cookie clientCookies;
public OkHttpCookies(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();
}
}
-自定义CookieManger
实现有一个自定义的CookieManger来管理cookies,实现以K-V结构获取set,getCookier
public class CookieManger implements CookieJar {
private static Context mContext;
private static PersistentCookieStore cookieStore;
public CookieManger(Context context) {
mContext = context;
if (cookieStore == null ) {
cookieStore = new PersistentCookieStore(mContext);
}
}
@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;
}
}
- Retrofit加入cookie
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
.cookieJar(new CookieManger(context))
.addInterceptor(loginInterceptor)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.build();
方案二:
方法一可能对某些网站不兼容,可以借助retfoit的拦截器实现
用来加入cookie
public class ReadCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
HashSet<String> preferences = (HashSet) Preferences.getDefaultPreferences().getStringSet(Preferences.PREF_COOKIES, new HashSet<>());
for (String cookie : preferences) {
builder.addHeader("Cookie", cookie);
Log.v("OkHttp", "Adding Header: " + cookie); // This is done so I know which headers are being added; this interceptor is used after the normal logging of OkHttp
}
return chain.proceed(builder.build());
}
}
用来保存Cookies
public class SaveCookiesInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
HashSet<String> cookies = new HashSet<>();
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
}
Preferences.getDefaultPreferences().edit()
.putStringSet(Preferences.PREF_COOKIES, cookies)
.apply();
}
return originalResponse;
}
}
okhttp
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new ReadCookiesInterceptor());
okHttpClient.interceptors().add(new SaveCookiesInterceptor());
Retrofit
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.build();
总结
实现加入cookies持久,实现免登陆基本步骤
1 实现可序列化的OkHttpcookies
2 实现储存OkHttpcookies的PersistentCookieStore
3 实现cookies管理工具CookieManger
4 构建OKHttpClient
5 Retrofit加入自定义的okHttpClient
6直接调用RetrofitClient
so iz 或许下一步你将会Retrofit遇到文件和图片上传问题,别急会 干货即将来临!
Retrofit系列:
**
项目地址:GitHub: https://github.com/NeglectedByBoss/RetrofitClient
**
Retrofit2.0 ,OkHttp3完美同步持久Cookie实现免登录(二)的更多相关文章
- Android Okhttp完美同步持久Cookie实现免登录
通过对Retrofit2.0的<Retrofit 2.0 超能实践,完美支持Https传输>基础入门和案例实践,掌握了怎么样使用Retrofit访问网络,加入自定义header,包括加入S ...
- Retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava
Retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava Retrofit,因为其简单与出色的性能,也是受到很多人的青睐,但是他和以往的通信 ...
- Android 使用Retrofit2.0+OkHttp3.0实现缓存处理+Cookie持久化第三方库
1.Retrofit+OkHttp的缓存机制 1.1.第一点 在响应请求之后在 data/data/<包名>/cache 下建立一个response 文件夹,保存缓存数据. 1.2.第二点 ...
- 通过设置PHPSESSID保存到cookie实现免登录
$cookieParams = session_get_cookie_params(); session_set_cookie_params( 3600,// 设置sessionID在cookie中保 ...
- Novate 网络库:Retrofit2.0和RxJava的又一次完美改进加强(Tamic博客 -CSDN)
作者/Tamic http://blog.csdn.net/sk719887916/article/details/52195428 前言 用过RxJava和Retrofit的朋友,用久了就会发现Re ...
- Android基于Retrofit2.0 +RxJava 封装的超好用的RetrofitClient工具类(六)
csdn :码小白 原文地址: http://blog.csdn.net/sk719887916/article/details/51958010 RetrofitClient 基于Retrofit2 ...
- Android进阶:七、Retrofit2.0原理解析之最简流程【下】
紧接上文Android进阶:七.Retrofit2.0原理解析之最简流程[上] 一.请求参数整理 我们定义的接口已经被实现,但是我们还是不知道我们注解的请求方式,参数类型等是如何发起网络请求的呢? 这 ...
- 采用Asp.Net的Forms身份验证时,持久Cookie的过期时间会自动扩展
原文:http://www.cnblogs.com/sanshi/archive/2012/06/22/2558476.html 若是持久Cookie,Cookie的有效期Expiration属性有当 ...
- 采用Asp.Net的Forms身份验证时,非持久Cookie的过期时间会自动扩展
问题描述 之前没有使用Forms身份验证时,如果在登陆过程中把HttpOnly的Cookie过期时间设为半个小时,总会收到很多用户的抱怨,说登陆一会就过期了. 所以总是会把Cookie过期时间设的长一 ...
随机推荐
- 【bzoj4444 scoi2015】国旗计划
题目描述 A 国正在开展一项伟大的计划 —— 国旗计划.这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈.这项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了 NN 名优秀的边防 ...
- Xamarin开发缺少的android_m2repository_rxx.zip下载地址以及MD5
android_m2repository_rxx.zip下载地址以及MD5, 注意:下载后需要改文件名,改为 MD5的值.zip 例如:android_m2repository_r29.zip 需改 ...
- Java正则过滤
import java.util.regex.Matcher; import java.util.regex.Pattern; public class LongStringtonumber { pu ...
- KMP及其改进算法
本文主要讲述KMP已经KMP的一种改进方法.若发现不正确的地方,欢迎交流指出,谢谢! KMP算法的基本思想: KMP的算法流程: 每当一趟匹配过程中出现字符比较不等时,不需回溯 i 指针,而是利用已经 ...
- 利用mybatis-generator自动生成数据持久化的代码
MyBatis生成器简介 MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代码生成器.它将生成所有版本的MyBatis的代码,以及版本2.2.0之后的iB ...
- 利用生产者消费者模型和MQ模型写一个自己的日志系统-并发设计里一定会用到的手段
一:前言 写这个程序主要是用来理解生产者消费者模型,以及通过这个Demo来理解Redis的单线程取原子任务是怎么实现的和巩固一下并发相关的知识:这个虽然是个Demo,但是只要稍加改下Appender部 ...
- (一)python基础知识
Python:解释型语言(一边翻译一边运行)注释:单行注释(#).多行注释(ctrl+/):''' '''和""" """ (python2 ...
- Kinect 深度图像格式
Kinect的深度图像有16bit,2byte,如图: 第15位:标志位,不用做深度计算 第14~3位:深度图像数据,即距离,以毫米为单位 第0~2位:深度图中人的ID(PlayerID) 深度图有两 ...
- 牛客网编程练习之PAT乙级(Basic Level):1041 说反话
直接分隔取反即可 AC代码: import java.util.Scanner; /** * @author CC11001100 */ public class Main { public stat ...
- MLDS笔记:浅层结构 vs 深层结构
深度学习出现之前,机器学习方面的开发者通常需要仔细地设计特征.设计算法,且他们在理论上常能够得知这样设计的实际表现如何: 深度学习出现后,开发者常先尝试实验,有时候实验结果常与直觉相矛盾,实验后再找出 ...