网络请求框架——OkGo解读(二)——cookie

cookie:
public interface CookieStore {

    /** 保存url对应所有cookie */
    void saveCookies(HttpUrl url, List cookie);

    /** 加载url所有的cookie */
    List loadCookies(HttpUrl url);

    /** 获取当前所有保存的cookie */
    List getAllCookie();

    /** 根据url和cookie移除对应的cookie */
    boolean removeCookie(HttpUrl url, Cookie cookie);

    /** 根据url移除所有的cookie */
    boolean removeCookies(HttpUrl url);

    /** 移除所有的cookie */
    boolean removeAllCookie();
}

cookie持久化保存:  ---- 使用SharePreferences持久化存储 cookie
public class PersistentCookieStore implements CookieStore {

    private static final String LOG_TAG = "PersistentCookieStore";
    private static final String COOKIE_PREFS = "okgo_cookie";        //cookie使用prefs保存
    private static final String COOKIE_NAME_PREFIX = "cookie_";          //cookie持久化的统一前缀

    private final HashMap> cookies;
    private final SharedPreferences cookiePrefs;

    public PersistentCookieStore() {
        cookiePrefs = OkGo.getContext().getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE);
        cookies = new HashMap<>();

        //将持久化的cookies缓存到内存中,数据结构为 Map>
        Map prefsMap = cookiePrefs.getAll();
        for (Map.Entry 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 = decodeCookie(encodedCookie);
                        if (decodedCookie != null) {
                            if (!cookies.containsKey(entry.getKey()))
                                cookies.put(entry.getKey(), new ConcurrentHashMap());
                            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,只返回没有过期的cookie */
    @Override
    public List loadCookies(HttpUrl url) {
        ArrayList ret = new ArrayList<>();
        if (cookies.containsKey(url.host())) {
            Collection 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 void saveCookies(HttpUrl url, List urlCookies) {
        if (!cookies.containsKey(url.host())) {
            cookies.put(url.host(), new ConcurrentHashMap());
        }
        for (Cookie cookie : urlCookies) {
            //当前cookie是否过期
            if (isCookieExpired(cookie)) {
                removeCookie(url, cookie);
            } else {
                saveCookie(url, cookie, getCookieToken(cookie));
            }
        }
    }

    /**
     * 保存cookie,并将cookies持久化到本地,数据结构为
     * Url.host -> Cookie1.name,Cookie2.name,Cookie3.name
     * cookie_Cookie1.name -> CookieString
     * cookie_Cookie2.name -> CookieString
     */
    private void saveCookie(HttpUrl url, Cookie cookie, String name) {
        //内存缓存
        cookies.get(url.host()).put(name, cookie);
        //文件缓存
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
        prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie)));
        prefsWriter.commit();
    }

    /** 根据url移除当前的cookie */
    @Override
    public boolean removeCookie(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(COOKIE_NAME_PREFIX + name)) {
                prefsWriter.remove(COOKIE_NAME_PREFIX + name);
            }
            prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
            prefsWriter.commit();
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean removeCookies(HttpUrl url) {
        if (cookies.containsKey(url.host())) {
            //文件移除
            Set cookieNames = cookies.get(url.host()).keySet();
            SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
            for (String cookieName : cookieNames) {
                if (cookiePrefs.contains(COOKIE_NAME_PREFIX + cookieName)) {
                    prefsWriter.remove(COOKIE_NAME_PREFIX + cookieName);
                }
            }
            prefsWriter.remove(url.host());
            prefsWriter.commit();
            //内存移除
            cookies.remove(url.host());
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean removeAllCookie() {
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.clear();
        prefsWriter.commit();
        cookies.clear();
        return true;
    }

    /** 获取所有的cookie */
    @Override
    public List getAllCookie() {
        ArrayList 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(SerializableHttpCookie 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 = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
        } 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;
    }
}

cookie的内存管理:
public class MemoryCookieStore implements CookieStore {

    private final HashMap> memoryCookies = new HashMap<>();

    @Override
    public synchronized void saveCookies(HttpUrl url, List cookies) {
        List oldCookies = memoryCookies.get(url.host());
        List needRemove = new ArrayList<>();
        for (Cookie newCookie : cookies) {
            for (Cookie oldCookie : oldCookies) {
                if (newCookie.name().equals(oldCookie.name())) {
                    needRemove.add(oldCookie);
                }
            }
        }
        oldCookies.removeAll(needRemove);
        oldCookies.addAll(cookies);
    }

    @Override
    public synchronized List loadCookies(HttpUrl url) {
        List cookies = memoryCookies.get(url.host());
        if (cookies == null) {
            cookies = new ArrayList<>();
            memoryCookies.put(url.host(), cookies);
        }
        return cookies;
    }

    @Override
    public synchronized List getAllCookie() {
        List cookies = new ArrayList<>();
        Set httpUrls = memoryCookies.keySet();
        for (String url : httpUrls) {
            cookies.addAll(memoryCookies.get(url));
        }
        return cookies;
    }

    @Override
    public synchronized boolean removeCookie(HttpUrl url, Cookie cookie) {
        List cookies = memoryCookies.get(url.host());
        return (cookie != null) && cookies.remove(cookie);
    }

    @Override
    public synchronized boolean removeCookies(HttpUrl url) {
        return memoryCookies.remove(url.host()) != null;
    }

    @Override
    public synchronized boolean removeAllCookie() {
        memoryCookies.clear();
        return true;
    }
}
扩展了解 以下内容:

/*   HttpUrl   类
 统一资源定位符 http 或 https 使用这个类进行组合和分解 互联网地址 
还可用做URL的解析 (点击相关链接指定页面)    
 用于检索资源,尽管url有多种,这个类只支持http、https
 */

expiresAt             到期时间     
domain                true时为唯一的域    
path                      cookie的路径  
secure                  为true时是仅限于https(安全)    
httpOnly               为true时 仅限于http 在web浏览器可防止cookie访问脚本
persistent            回话是否到期  失效     内容能够被保存的时间
private Cookie(String name, String value, long expiresAt, String domain, String path,
    boolean secure, boolean httpOnly, boolean hostOnly, boolean persistent) {
  this.name = name;
  this.value = value;
  this.expiresAt = expiresAt;
  this.domain = domain;
  this.path = path;
  this.secure = secure;
  this.httpOnly = httpOnly;
  this.hostOnly = hostOnly;
  this.persistent = persistent;
}

cookie 的序列化:
public class SerializableHttpCookie implements Serializable {
    private static final long serialVersionUID = 6374381323722046732L;

    private transient final Cookie cookie;
    private transient Cookie clientCookie;

    public SerializableHttpCookie(Cookie cookie) {
        this.cookie = cookie;
    }

    public Cookie getCookie() {
        Cookie bestCookie = cookie;
        if (clientCookie != null) {
            bestCookie = clientCookie;
        }
        return bestCookie;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(cookie.name());
        out.writeObject(cookie.value());
        out.writeLong(cookie.expiresAt());
        out.writeObject(cookie.domain());
        out.writeObject(cookie.path());
        out.writeBoolean(cookie.secure());
        out.writeBoolean(cookie.httpOnly());
        out.writeBoolean(cookie.hostOnly());
        out.writeBoolean(cookie.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;
        clientCookie = builder.build();
    }
}


CookieJar的实现类
public class CookieJarImpl implements CookieJar {

    private CookieStore cookieStore;
    private Map> userCookies = new HashMap<>();  //用户手动添加的Cookie

    public void addCookies(List cookies) {
        for (Cookie cookie : cookies) {
            String domain = cookie.domain();
            Set domainCookies = userCookies.get(domain);
            if (domainCookies == null) {
                domainCookies = new HashSet<>();
                userCookies.put(domain, domainCookies);
            }
            domainCookies.add(cookie);
        }
    }

    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 cookies) {
        cookieStore.saveCookies(url, cookies);                                   //这里调用接口的保存cookies方法
    }                                                                       // 分别把cookie保存到了 内存 和 sp

    @Override
    public synchronized List loadForRequest(HttpUrl url) {
        List requestUrlCookies = cookieStore.loadCookies(url);
        Set userUrlCookies = userCookies.get(url.host());
        Set cookieSet = new HashSet<>();
        if (requestUrlCookies != null) cookieSet.addAll(requestUrlCookies);
        if (userUrlCookies != null) cookieSet.addAll(userUrlCookies);
        return new ArrayList<>(cookieSet);
    }

    public CookieStore getCookieStore() {
        return cookieStore;
    }
}

扩展了解:  CookieJar  实现这个接口 负责选择 cookie的接受和拒绝     
public interface CookieJar {
  /** A cookie jar that never accepts any cookies. */
  CookieJar NO_COOKIES = new CookieJar() {
    @Override public void saveFromResponse(HttpUrl url, List cookies) {
    }

    @Override public List loadForRequest(HttpUrl url) {
      return Collections.emptyList();
    }
  };

  /**
   * Saves {@code cookies} from an HTTP response to this store according to this jar's policy.
   *
   * 

Note that this method may be called a second time for a single HTTP response if the response * includes a trailer. For this obscure HTTP feature, {@code cookies} contains only the trailer's * cookies. */ void saveFromResponse(HttpUrl url, List cookies); /** * Load cookies from the jar for an HTTP request to {@code url}. This method returns a possibly * empty list of cookies for the network request. * *

Simple implementations will return the accepted cookies that have not yet expired and that * {@linkplain Cookie#matches match} {@code url}. */ List loadForRequest(HttpUrl url); }




你可能感兴趣的:(网络请求框架——OkGo解读(二)——cookie)