Instantly share code, notes, and snippets.
Last active
August 25, 2017 04:29
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save boyan01/d3bdff83e69dc0c1a185ebbe0bea778b to your computer and use it in GitHub Desktop.
缓存 OkHttp Cookie 到指定文件
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* author : SUMMERLY | |
* e-mail : [email protected] | |
* time : 2017/8/23 | |
* desc : 用于持续保存Cookie | |
*/ | |
abstract class CookieStore { | |
abstract fun add(url: HttpUrl, cookie: Cookie) | |
abstract fun get(url: HttpUrl): List<Cookie> | |
abstract fun removeAll(): Boolean | |
abstract fun remove(url: HttpUrl, cookie: Cookie): Boolean | |
abstract fun getCookies(): List<Cookie> | |
protected fun getCookieToken(cookie: Cookie): String { | |
return cookie.name() + "@" + cookie.domain() | |
} | |
/** | |
* cookies 序列化成 string | |
* | |
* @param cookie 要序列化的cookie | |
* @return 序列化之后的string | |
*/ | |
protected fun encodeCookie(cookie: SerializableOkHttpCookies): String { | |
val os = ByteArrayOutputStream() | |
try { | |
val outputStream = ObjectOutputStream(os) | |
outputStream.writeObject(cookie) | |
} catch (e: IOException) { | |
error("IOException in encodeCookie") | |
} | |
return byteArrayToHexString(os.toByteArray()) | |
} | |
/** | |
* 将字符串反序列化成cookies | |
* | |
* @param cookieString cookies string | |
* @return cookie object | |
*/ | |
protected fun decodeCookie(cookieString: String): Cookie? { | |
val cookie = attempt { | |
val bytes = hexStringToByteArray(cookieString) | |
val byteArrayInputStream = ByteArrayInputStream(bytes) | |
val objectInputStream = ObjectInputStream(byteArrayInputStream) | |
(objectInputStream.readObject() as SerializableOkHttpCookies).cookies | |
} | |
return cookie.value | |
} | |
/** | |
* 二进制数组转十六进制字符串 | |
* | |
* @param bytes byte array to be converted | |
* @return string containing hex values | |
*/ | |
private fun byteArrayToHexString(bytes: ByteArray): String { | |
val sb = StringBuilder(bytes.size * 2) | |
for (element in bytes) { | |
val v = element.toInt() and 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 | |
*/ | |
private fun hexStringToByteArray(hexString: String): ByteArray { | |
val len = hexString.length | |
val data = ByteArray(len / 2) | |
var i = 0 | |
while (i < len) { | |
data[i / 2] = ((Character.digit(hexString[i], 16) shl 4) + Character.digit(hexString[i + 1], 16)).toByte() | |
i += 2 | |
} | |
return data | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PersistentCookieStoreForTest(private val path: String) : CookieStore() { | |
private val cookies: MutableMap<String, ConcurrentHashMap<String, Cookie>> = HashMap() | |
private val data: MutableMap<String, String> = HashMap() | |
init { | |
//将cookies载入内存 | |
restore() | |
} | |
override fun add(url: HttpUrl, cookie: Cookie) { | |
val name = getCookieToken(cookie) | |
//将cookies缓存到内存中 如果缓存过期 就重置此cookie | |
if (cookie.persistent()) { | |
if (!cookies.containsKey(url.host())) { | |
cookies.put(url.host(), ConcurrentHashMap()) | |
} | |
cookies[url.host()]!!.put(name, cookie) | |
//将 cookies保存在 [data] 中 | |
data.put(url.host(), cookies[url.host()]!!.keys.joinToString(",")) | |
data.put(name, encodeCookie(SerializableOkHttpCookies(cookie))) | |
} else { | |
cookies[url.host()]?.remove(name) | |
} | |
} | |
override fun get(url: HttpUrl): List<Cookie> { | |
val result = ArrayList<Cookie>(5) | |
cookies[url.host()]?.values?.let { | |
result.addAll(it) | |
} | |
return result | |
} | |
override fun removeAll(): Boolean { | |
File(path).deleteOnExit() | |
cookies.clear() | |
data.clear() | |
return true | |
} | |
//实际上我们测试时并不会用到此方法 | |
override fun remove(url: HttpUrl, cookie: Cookie): Boolean { | |
error("do not need to use this method in test") | |
} | |
override fun getCookies(): List<Cookie> { | |
error("do not need to use this method in test") | |
} | |
/** | |
* 将存在文件 [path] 的 cookies 拿出来 | |
*/ | |
@Test | |
fun restore() { | |
val file = File(path).takeIf { it.exists() } ?: return | |
val sb = StringBuilder() | |
file.useLines { | |
sb.append(it) | |
} | |
if (sb.isEmpty()) { | |
return | |
} | |
val fileData = Gson().fromJson<HashMap<String, String>>(file.reader(), object : TypeToken<HashMap<String, String>>() {}.type) ?: return | |
data.clear() | |
data.putAll(fileData) | |
fileData.forEach { entry -> | |
entry.value.split(",").forEach { name -> | |
val encodedCookie = fileData[name] | |
encodedCookie?.let { | |
decodeCookie(it)?.let { cookie -> | |
if (!cookies.containsKey(entry.key)) { | |
cookies.put(entry.key, ConcurrentHashMap()) | |
} | |
cookies[entry.key]?.put(name, cookie) | |
} | |
} | |
} | |
} | |
} | |
/** | |
* 如想将 cookie 临时保存在电脑上,请务必在测试后使用此方法 | |
*/ | |
@Test | |
fun save() { | |
val json = Gson().toJson(data) | |
val file = File(path) | |
file.parentFile.mkdirs() | |
if (file.exists()) { | |
file.delete() | |
} | |
file.createNewFile() | |
file.outputStream().use { | |
it.write(json.toByteArray()) | |
it.flush() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment