Retrofit2.0 and OkHttp3 perfectly synchronize persistent cookies for login-free implementation tutorial (2), retrofit2.0okhttp3
Cookie
Cookies are a technology that allows the website server to store a small amount of data to the client's hard disk or memory, or to read data from the client's hard disk. Cookies are a very small text file placed on your hard disk by the Web server when you browse a website, it can record your user ID, password, browsed webpage, stay time, and other information. When you come to this website again, the website can read Cookies and learn your related information to make corresponding actions, such as displaying your welcome slogans on the page, or you can log on directly without entering the ID or password.
Essentially, it can be viewed as your ID card. However, Cookies cannot be executed as code, nor are viruses transmitted. They are private to you and can only be read by servers that provide them. The saved information fragments are stored in the form of "name/value" pairs (name-value pairs). A "name/value" pair is only a piece of named data. A website can only obtain information stored on your computer. It cannot obtain information from other Cookies, or get anything else on your computer.
Most of the content in Cookies is encrypted. Therefore, generally, users only seem to have meaningless combinations of letters and numbers. Only CGI processors on the server can understand their true meanings.
Cookie is also an http session tracking technology, and also contains sessions on the web side. Cookie is used to solve the stateless defects of HTTP.
Cookie Structure
Sending rule:
The browser (device) determines whether to send a Cookie based on the following rules:
Whether the requested host name matches the Domain attribute of a stored Cookie; whether the requested Port number is in the Port attribute list of the Cookie; whether the requested resource Path is in the directory and subdirectory specified by the Path attribute of the Cookie;
Whether the Cookie has expired.
The following is an example of cookie code:
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)
Each Cookie in the Cookie request header field is separated by commas (,) or semicolons.
In addition to setting "name = value", the Cookie request header field can contain attribute names such as Version, Path, Domain, and Port, you must add a "$" character as the prefix.
The Version attribute can only appear once and must be located at the top of the Cookie request header field setting value. If you need to set the paths, domains, ports, and other attributes of the Cookie information, they must be placed after the "name = value" setting of the Cookie information.
The Path property points to the subdirectory Cookie before the Path property points to the parent directory Cookie.
Function Cookie in Servlet Program
The Servlet API provides a javax. servlet. http. Cookie class to disable Cookie information. It contains methods for generating Cookie information and extracting Cookie information attributes.
Cookie method:
(Constructor: public Cookie (java. lang. string name, java. lang. string value) getName method setValue and getValue Methods setMaxAge and getMaxAge Methods setPath and getPath Methods setDomian and getPath Methods setVersion and getVersion Methods setComment and getComment Methods setSecoure and getSecure Methods custom cookies
Everyone in HttpClient knows how to add cookies.
AsyncHttpClient client = new AsyncHttpClient();PersistentCookieStore myCookieStore = new PersistentCookieStore(MainActivity.this);client.setCookieStore(myCookieStore);
Therefore, you must implement a PersistentCookieStore to store OkHttpCookies in fit.
Solution 1:-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
> Cookies; private final SharedPreferences cookiePrefs; public PersistentCookieStore (Context context) {cookiePrefs = context. getSharedPreferences (COOKIE_PREFS, 0); cookies = new HashMap <> (); // cache persistent cookies into memory, that is, map cookies Map
PrefsMap = cookiePrefs. getAll (); for (Map. Entry
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
();} Cookies. get (entry. getKey ()). put (name, decodedCookie) ;}}} protected String getCookieToken (Cookie cookie) {return cookie. name () + "@" + cookie. domain ();} public void add (HttpUrl url, Cookie) {String name = getCookieToken (cookie ); // cache cookies into the memory. if the cache expires, reset the cookie if (! Cookie. persistent () {if (! Cookies. containsKey (url. host () {cookies. put (url. host (), new ConcurrentHashMap
();} Cookies. get (url. host ()). put (name, cookie);} else {if (cookies. containsKey (url. host () {cookies. get (url. host ()). remove (name) ;}// persists cookies to the local SharedPreferences. editor prefsWriter = cookiePrefs. edit (); prefsWriter. putString (url. host (), TextUtils. join (",", cookies. get (url. host ()). keySet (); prefsWriter. putString (name, encodeCookie (new OkHttpCookies (cookies); prefsWriter. apply ();} public List
Get (HttpUrl url) {ArrayList
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) {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
GetCookies () {ArrayList
Ret = new ArrayList <> (); for (String key: cookies. keySet () ret. addAll (cookies. get (key ). values (); return ret ;} /*** serialize cookies to string ** @ param cookie the cookie to be serialized * @ return the serialized string */protected String encodeCookie (okhttpcookie 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 ();}/*** deserialize the string into cookies ** @ param cookieString cookies String * @ return cookie object */protected Cookie decodeCookie (string cookieString) {byte [] bytes = bytes (cookieString); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); Cookie cookie = null; try {ObjectInputStream objectInputStream = new ObjectInputStream (bytes ); 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 ;} /*** convert a binary array to a hexadecimal string ** @ 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 );} /*** convert a hexadecimal string to a binary array ** @ 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 ;}
The OkHttpCookies to be serialized are used to persist 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();}
}
-Custom CookieManger
Implement a custom CookieManger to manage cookies, implement to get set, getCookier in K-V Structure
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); }}@Overridepublic void saveFromResponse(HttpUrl url, List
cookies) { if (cookies != null && cookies.size() > 0) { for (Cookie item : cookies) { cookieStore.add(url, item); } }}@Overridepublic List
loadForRequest(HttpUrl url) { List
cookies =cookieStore.get(url); return cookies;}}
-Added duplicate fit to 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();
Summary
Implement persistent addition of cookies to achieve basic login-free steps
1. Implement serializable OkHttpcookies
2. Implement PersistentCookieStore for storing OkHttpcookies
3. Implement cookie management tool CookieManger
4. Build an OKHttpClient
5. Added Retrofit to the custom okHttpClient.
6. directly call the movie fitclient
Solution 2:
Method 1 may be incompatible with some websites. It can be implemented using the IT blocker.
Used to add cookies
public class ReadCookiesInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); HashSet
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()); }
}
Used to save 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
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();
Solution 3: RxJava + Retrofit
Implement the Interceptor of OKhttp to append the local cookie to the http request header.
Public class AddCookiesInterceptor implements Interceptor {private Context context; private String lang; public AddCookiesInterceptor (Context context, String lang) {super (); this. context = context; this. lang = lang ;}@ Override public Response intercept (Chain chain) throws IOException {if (chain = null) Log. d ("http", "Addchain = null"); final Request. builder builder = chain. request (). newBuilder (); SharedPreferences sharedPreferences = context. getSharedPreferences ("cookie", Context. MODE_PRIVATE); Observable. just (sharedPreferences. getString ("cookie ","")). subscribe (new Action1
() {@ Override public void call (String cookie) {if (cookie. contains ("lang = ch") {cookie = cookie. replace ("lang = ch", "lang =" + lang);} if (cookie. contains ("lang = en") {cookie = cookie. replace ("lang = en", "lang =" + lang);} // Add cookie // Log. d ("http", "AddCookiesInterceptor" + cookie); builder. addHeader ("cookie", cookie) ;}}); return chain. proceed (builder. build ());}}
Implement Interceptor to store the cookie returned by Http to a local device
public class ReceivedCookiesInterceptor implements Interceptor { private Context context; SharedPreferences sharedPreferences; public ReceivedCookiesInterceptor(Context context) { super(); this.context = context; sharedPreferences = context.getSharedPreferences("cookie", Context.MODE_PRIVATE); } @Override public Response intercept(Chain chain) throws IOException { if (chain == null) Log.d("http", "Receivedchain == null"); Response originalResponse = chain.proceed(chain.request()); Log.d("http", "originalResponse" + originalResponse.toString()); if (!originalResponse.headers("set-cookie").isEmpty()) { final StringBuffer cookieBuffer = new StringBuffer(); Observable.from(originalResponse.headers("set-cookie")) .map(new Func1
() { @Override public String call(String s) { String[] cookieArray = s.split(";"); return cookieArray[0]; } }) .subscribe(new Action1
() { @Override public void call(String cookie) { cookieBuffer.append(cookie).append(";"); } }); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("cookie", cookieBuffer.toString()); Log.d("http", "ReceivedCookiesInterceptor" + cookieBuffer.toString()); editor.commit(); } return originalResponse; }}
Add the Interceptor to fit. See method 2.
This method is provided by androidformjm to thank the author;