最近在做项目的时候遇见一个需求登录成功需要在请求头加入token但是移动端已经加上了token但是始终不对咨询了后端小哥才发现还需要保持session持久化(真是一语点醒梦中人啊!)这样就和浏览器差不多了于是我开始了疯狂查资料。。。。。。
一、Retrofit2.0请求时session为什么会出现无效
Retrofit2.0中,每个请求之间是独立开的,那么不管登录多少次,都是无效的,无法进行其他权限操作。然而浏览器是默认自动保存服务器发送过来的Session信息的,也就是默认保持Session信息的,只有当浏览器关闭,才会清除这些Session信息。那么Session是保存在服务器的吗?注意,这里用的是“保持”,换一种说法,就是让服务器知道这还是你,而不是新的用户连接。
二、解决session失效问题实现持久化
1、导入PersistentCookieJar依赖
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
2、添加拦截器加入PersistentCookieJar
new Retrofit.Builder()
.baseUrl(RequestConstant.BASE_URL)
// RxJava2
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// 字符串
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()))
//添加OkHttp
.client(initOkHttpClient())
.build();
private OkHttpClient initOkHttpClient() {
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptorHttp = new HttpLoggingInterceptor(message -> {
LogUtils.e(TAG + message);
});
interceptorHttp.setLevel(HttpLoggingInterceptor.Level.BODY);
httpBuilder.connectTimeout(5, TimeUnit.MINUTES).
readTimeout(5, TimeUnit.MINUTES).
writeTimeout(5, TimeUnit.MINUTES)
//使用PersistentCookieJar
.cookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(BaseApplication.getAppContext())))
.proxy(Proxy.NO_PROXY)
.addInterceptor(interceptorHttp)
.addInterceptor(mTokenInterceptor);
return httpBuilder.build();
}
这样我们就可以实现持久化但是出现了一个bug,每次退出了APP都需要重新登录无法真正的持久化。
三、解决每次退出APP都需要重新登录实现真正持久化
1、这里可以参考OKGO里面实现的库,Cookie,实现
CookieJarImpl继承CookieJar和SPCookieStore。
public class SPCookieStore implements CookieStore {
private static final String COOKIE_PREFS = "okgo_cookie"; //cookie使用prefs保存
private static final String COOKIE_NAME_PREFIX = "cookie_"; //cookie持久化的统一前缀
/**
* 数据结构如下
* Url.host -> cookieToken1,cookieToken2,cookieToken3
* cookie_cookieToken1 -> cookie1
* cookie_cookieToken2 -> cookie2
* cookie_cookieToken3 -> cookie3
*/
private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
private final SharedPreferences cookiePrefs;
public SPCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中,数据结构为 Map<Url.host, Map<CookieToken, Cookie>>
Map<String, ?> prefsMap = cookiePrefs.getAll();
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
if ((entry.getValue()) != null && !entry.getKey().startsWith(COOKIE_NAME_PREFIX)) {
//获取url对应的所有cookie的key,用","分割
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
for (String name : cookieNames) {
//根据对应cookie的Key,从xml中获取cookie的真实值
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
Cookie decodedCookie = SerializableCookie.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);
}
}
}
}
}
}
private String getCookieToken(Cookie cookie) {
return cookie.name() + "@" + cookie.domain();
}
/** 当前cookie是否过期 */
private static boolean isCookieExpired(Cookie cookie) {
return cookie.expiresAt() < System.currentTimeMillis();
}
/** 将url的所有Cookie保存在本地 */
@Override
public synchronized void saveCookie(HttpUrl url, List<Cookie> urlCookies) {
for (Cookie cookie : urlCookies) {
saveCookie(url, cookie);
}
}
@Override
public synchronized void saveCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) {
cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
}
//当前cookie是否过期
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
saveCookie(url, cookie, getCookieToken(cookie));
}
}
/** 保存cookie,并将cookies持久化到本地 */
private void saveCookie(HttpUrl url, Cookie cookie, String cookieToken) {
//内存缓存
cookies.get(url.host()).put(cookieToken, cookie);
//文件缓存
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + cookieToken, SerializableCookie.encodeCookie(url.host(), cookie));
prefsWriter.apply();
}
/** 根据当前url获取所有需要的cookie,只返回没有过期的cookie */
@Override
public synchronized List<Cookie> loadCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
if (!cookies.containsKey(url.host())) return ret;
Collection<Cookie> urlCookies = cookies.get(url.host()).values();
for (Cookie cookie : urlCookies) {
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
ret.add(cookie);
}
}
return ret;
}
/** 根据url移除当前的cookie */
@Override
public synchronized boolean removeCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) return false;
String cookieToken = getCookieToken(cookie);
if (!cookies.get(url.host()).containsKey(cookieToken)) return false;
//内存移除
cookies.get(url.host()).remove(cookieToken);
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeCookie(HttpUrl url) {
if (!cookies.containsKey(url.host())) return false;
//内存移除
ConcurrentHashMap<String, Cookie> urlCookie = cookies.remove(url.host());
//文件移除
Set<String> cookieTokens = urlCookie.keySet();
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (String cookieToken : cookieTokens) {
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
}
prefsWriter.remove(url.host());
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeAllCookie() {
//内存移除
cookies.clear();
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
return true;
}
/** 获取所有的cookie */
@Override
public synchronized List<Cookie> getAllCookie() {
List<Cookie> ret = new ArrayList<>();
for (String key : cookies.keySet()) {
ret.addAll(cookies.get(key).values());
}
return ret;
}
@Override
public synchronized List<Cookie> getCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
Map<String, Cookie> mapCookie = cookies.get(url.host());
if (mapCookie != null) ret.addAll(mapCookie.values());
return ret;
}
}
2、使用方法
private OkHttpClient initOkHttpClient() {
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptorHttp = new HttpLoggingInterceptor(message -> {
LogUtils.e(TAG + message);
});
interceptorHttp.setLevel(HttpLoggingInterceptor.Level.BODY);
httpBuilder.connectTimeout(5, TimeUnit.MINUTES).
readTimeout(5, TimeUnit.MINUTES).
writeTimeout(5, TimeUnit.MINUTES)
.cookieJar(new CookieJarImpl(new SPCookieStore(BaseApplication.getAppContext())))
.proxy(Proxy.NO_PROXY)
.addInterceptor(interceptorHttp)
.addInterceptor(mTokenInterceptor);
return httpBuilder.build();
}
CookieJarImpl实现CookieJar管理用户维护的cookie
public class CookieJarImpl implements CookieJar {
private CookieStore cookieStore;
public CookieJarImpl(CookieStore cookieStore) {
if (cookieStore == null) {
throw new IllegalArgumentException("cookieStore can not be null!");
}
this.cookieStore = cookieStore;
}
@Override
public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.saveCookie(url, cookies);
}
@Override
public synchronized List<Cookie> loadForRequest(HttpUrl url) {
return cookieStore.loadCookie(url);
}
public CookieStore getCookieStore() {
return cookieStore;
}
}
SPCookieStore实现CookieStore使用 SharedPreferences 持久化存储 cookie
public class SPCookieStore implements CookieStore {
private static final String COOKIE_PREFS = "okgo_cookie"; //cookie使用prefs保存
private static final String COOKIE_NAME_PREFIX = "cookie_"; //cookie持久化的统一前缀
/**
* 数据结构如下
* Url.host -> cookieToken1,cookieToken2,cookieToken3
* cookie_cookieToken1 -> cookie1
* cookie_cookieToken2 -> cookie2
* cookie_cookieToken3 -> cookie3
*/
private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
private final SharedPreferences cookiePrefs;
public SPCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中,数据结构为 Map<Url.host, Map<CookieToken, Cookie>>
Map<String, ?> prefsMap = cookiePrefs.getAll();
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
if ((entry.getValue()) != null && !entry.getKey().startsWith(COOKIE_NAME_PREFIX)) {
//获取url对应的所有cookie的key,用","分割
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
for (String name : cookieNames) {
//根据对应cookie的Key,从xml中获取cookie的真实值
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
Cookie decodedCookie = SerializableCookie.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);
}
}
}
}
}
}
private String getCookieToken(Cookie cookie) {
return cookie.name() + "@" + cookie.domain();
}
/** 当前cookie是否过期 */
private static boolean isCookieExpired(Cookie cookie) {
return cookie.expiresAt() < System.currentTimeMillis();
}
/** 将url的所有Cookie保存在本地 */
@Override
public synchronized void saveCookie(HttpUrl url, List<Cookie> urlCookies) {
for (Cookie cookie : urlCookies) {
saveCookie(url, cookie);
}
}
@Override
public synchronized void saveCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) {
cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
}
//当前cookie是否过期
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
saveCookie(url, cookie, getCookieToken(cookie));
}
}
/** 保存cookie,并将cookies持久化到本地 */
private void saveCookie(HttpUrl url, Cookie cookie, String cookieToken) {
//内存缓存
cookies.get(url.host()).put(cookieToken, cookie);
//文件缓存
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + cookieToken, SerializableCookie.encodeCookie(url.host(), cookie));
prefsWriter.apply();
}
/** 根据当前url获取所有需要的cookie,只返回没有过期的cookie */
@Override
public synchronized List<Cookie> loadCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
if (!cookies.containsKey(url.host())) return ret;
Collection<Cookie> urlCookies = cookies.get(url.host()).values();
for (Cookie cookie : urlCookies) {
if (isCookieExpired(cookie)) {
removeCookie(url, cookie);
} else {
ret.add(cookie);
}
}
return ret;
}
/** 根据url移除当前的cookie */
@Override
public synchronized boolean removeCookie(HttpUrl url, Cookie cookie) {
if (!cookies.containsKey(url.host())) return false;
String cookieToken = getCookieToken(cookie);
if (!cookies.get(url.host()).containsKey(cookieToken)) return false;
//内存移除
cookies.get(url.host()).remove(cookieToken);
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeCookie(HttpUrl url) {
if (!cookies.containsKey(url.host())) return false;
//内存移除
ConcurrentHashMap<String, Cookie> urlCookie = cookies.remove(url.host());
//文件移除
Set<String> cookieTokens = urlCookie.keySet();
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for (String cookieToken : cookieTokens) {
if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieToken)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + cookieToken);
}
}
prefsWriter.remove(url.host());
prefsWriter.apply();
return true;
}
@Override
public synchronized boolean removeAllCookie() {
//内存移除
cookies.clear();
//文件移除
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
return true;
}
/** 获取所有的cookie */
@Override
public synchronized List<Cookie> getAllCookie() {
List<Cookie> ret = new ArrayList<>();
for (String key : cookies.keySet()) {
ret.addAll(cookies.get(key).values());
}
return ret;
}
@Override
public synchronized List<Cookie> getCookie(HttpUrl url) {
List<Cookie> ret = new ArrayList<>();
Map<String, Cookie> mapCookie = cookies.get(url.host());
if (mapCookie != null) ret.addAll(mapCookie.values());
return ret;
}
}
CookieStore 公共接口
public interface CookieStore {
/** 保存url对应所有cookie */
void saveCookie(HttpUrl url, List<Cookie> cookie);
/** 保存url对应所有cookie */
void saveCookie(HttpUrl url, Cookie cookie);
/** 加载url所有的cookie */
List<Cookie> loadCookie(HttpUrl url);
/** 获取当前所有保存的cookie */
List<Cookie> getAllCookie();
/** 获取当前url对应的所有的cookie */
List<Cookie> getCookie(HttpUrl url);
/** 根据url和cookie移除对应的cookie */
boolean removeCookie(HttpUrl url, Cookie cookie);
/** 根据url移除所有的cookie */
boolean removeCookie(HttpUrl url);
/** 移除所有的cookie */
boolean removeAllCookie();
}
四、测试
两种方案都可以实现持久化根据用户需求来确定。