1. 程式人生 > >使用OKHttp模擬登陸知乎,兼談OKHttp中Cookie的使用!

使用OKHttp模擬登陸知乎,兼談OKHttp中Cookie的使用!

本文主要是想和大家探討技術,讓大家學會Cookie的使用,切勿做違法之事!

很多Android初學者在剛開始學習的時候,或多或少都想自己搞個應用出來,把自己學的十八般武藝全都用在這個APP上,其實這個想法很好,專案驅動學習,效率更高,這是大學老師教給我的。可是一個APP,如果純粹搞成一個本地應用,會變得很沒有意思,所以我們一般還是做網路應用,網路應用涉及到網路伺服器的搭建,資料的採集等等太過於耗時,有的人可能剛剛搭建一個網路伺服器就耗費了很長時間,搞得都沒有信心學習Android了,針對這種情況,我一般建議大家自己去抓包。抓包又會遇到新問題,就是有可能你需要模擬登陸。因此,本文以知乎登陸為例,帶大家來看看模擬登陸,同時也來看看OKHttp中Cookie的使用問題。

為什麼選擇知乎作為切入點呢?沒什麼,在想到這個話題的一瞬間剛好想到了知乎!

實際上模擬登陸還是很簡單的,麻煩的是需要我們去仔細分析請求的介面和引數!

本文內容主要包括以下三個方面

1.知乎登陸介面和引數分析

2.模擬登陸

3.Cookie持久化

OK,那就開始吧!

1.知乎登陸介面和引數分析

本文采用Chrome瀏覽器來進行分析,首先開啟知乎登入頁面,如下:

按下F12,開啟Chrome的除錯視窗:

然後在知乎的登入頁面輸入使用者名稱和登入密碼,注意觀察除錯視窗的日誌:

在這裡我們可以看到傳遞給伺服器的引數主要有如下四個,分別是_xsrf,password,remember_me,以及email四個,remember_me很好理解,是否記住密碼,email實際就是我們的賬號名稱,password實際就是我們的登入密碼,至於_xsrf則是登入頁面的一個隱藏域,這個資料很容易拿到,同時,從這裡我們還可以看出知乎登入時請求的介面是https://www.zhihu.com/login/email

OK,分析完這些之後,我們就可以動手開始編碼了。

2.模擬登陸

知道了知乎在登入的過程中需要傳遞哪些引數之後,接下來我們就可以動手模擬登入。

使用者名稱、密碼以及記住我這三個引數非常容易記憶,很容易獲取,之後第一個引數稍微有些麻煩,我們開啟使用者登入頁面的原始碼,會看到如下一行程式碼:

這個呢其實就是隱藏域的值。好了,現在登入所需要的四個引數都知道從哪裡獲取了,那我們就開始登入吧,我的登入頁面如下:

輸入使用者名稱和密碼,點選登入按鈕就可以執行登入操作了,但是在執行登入操作之前,我需要先訪問知乎的登入頁面,拿到那個隱藏域的值。於是乎,我的登入邏輯是這樣:

先來看如何獲取隱藏域,這裡涉及到如何解析HTML文字,我在這裡用到了Jsoup庫,對該庫不瞭解的小夥伴請自行Google,核心程式碼如下(完整程式碼小夥伴們自行在文末下載該Project):

Request request = new Request.Builder().url("https://www.zhihu.com/#signin").build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String resp = response.body().string();
                Document parse = Jsoup.parse(resp);
                Elements select = parse.select("input[type=hidden]");
                Element element = select.get(0);
                String xsrf = element.attr("value");
                Message msg = mHandler.obtainMessage();
                msg.what = 1;
                msg.obj = xsrf;
                Log.d("google_lenve_fb", "onResponse: xsrf:" + xsrf);
                mHandler.sendMessage(msg);
            }
        });

在下載到該HTML文字之後,先將該文字轉為一個Document物件,然後使用select選擇器,找到有一個屬性為type=hidden的input節點,然後獲取該節點中的value屬性,那麼毫無疑問,該value屬性,就是我們要得_xsrf的值。有了這個值之後,接下來訪問登入頁面即可登入成功,程式碼如下:
FormBody formBody = new FormBody.Builder()
                .add("captcha_type", "cn")
                .add("_xsrf", xsrf)
                .add("password", passwordEt.getText().toString())
                .add("remember_me", "true")
                .add("email", usernameEt.getText().toString())
                .build();
        Request request = new Request.Builder().post(formBody).url(loginUrl).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d("google_lenve_fb", "onResponse: " + response.body().string().toString());
            }
        });

登入成功之後,知乎會返回如下一行Json:
{
    "r":0,
    "msg":"登入成功"
}

至此為止,我們的模擬登入就成功了,是不是很簡單!!!

可是單純的模擬登入並沒有什麼意義,舉個栗子,我們知道要想獲取使用者的私信,必須是登入狀態才能獲取,在知乎中獲取使用者私信的頁面地址是:

https://www.zhihu.com/inbox

可是即使你模擬登入成功了,還是無法獲取這個頁面的資訊,當你訪問這個頁面的時候,系統會自動跳轉到登入頁面,因為系統並不知道你已經登入了。那麼我該怎麼做,才能讓系統知道我已經登入成功了呢?這裡就涉及到Cookie。

3.Cookie持久化

Cookie這個東西最早由網景的員工在1994年提出,他在他的原始說明文件中解釋了Cookie工作原理的基本資訊,該文字後來被作為規範納入到RFC 2965中,網景瀏覽器從一開始就支援Cookie,現如今所有的Web瀏覽器都支援Cookie。那麼Cookie到底是什麼?其實就是瀏覽器儲存在使用者電腦上的一小段文字,該文字從何而來?在使用者首次登入的時候,伺服器會返回一段Cooike文字,瀏覽器將該文字存入到使用者的電腦中,以後每當使用者向該伺服器發起網路請求時,瀏覽器都會攜帶上這段文字,這樣伺服器就知道該使用者是否已經登入過了。

OK,上文是我們對Cookie一個簡單的介紹,接下來我們就來看看在我們的OkHttp中如何實現Cookie的快取。

OkHttp框架從3.0開始簡化了Cookie的使用,它提供了一個叫做cookieJar的API,只需要我們實現該API中的方法即可,一個簡單的使用方式如下:

builder = new OkHttpClient.Builder();
builder.cookieJar(new CookieJar() {
    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        cookieStore.put(url.host(), cookies);
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        List<Cookie> cookies = cookieStore.get(url.host());
        return cookies != null ? cookies : new ArrayList<Cookie>();
    }
});
okHttpClient = builder.build();

該介面中有兩個回撥方法,一個是儲存Cookie,一個是讀取Cookie,我將Cookie儲存在一個HashMap中,儲存和讀取都在這個HashMap中操作。設定了Cookie之後,當我再次登入,登入成功之後獲取私信時就沒有任何問題了。可是存在Map中的東東一旦我的應用退出之後,這個東西就又沒了,再次進來還是要登入,那麼有什麼辦法可以實現Cookie的持久化呢?當然可以。Cookie持久化,你可以將Cookie儲存 在資料庫中,也可以將Cookie儲存在SharedPreferences中,都行,我這裡以儲存在SharedPreferences中,具體程式碼參考AsyncHttpClient相關類,程式碼較長,我這裡就不貼了,大家可以在文字下載Project,Cookie持久化使用方式如下:
builder = new OkHttpClient.Builder();
CookieJarImpl cookieJarImpl = new CookieJarImpl(new PersistentCookieStore(getApplicationContext()));
builder.cookieJar(cookieJarImpl);
okHttpClient = builder.build();

將Cookie持久化到本地之後,接下來我就可以在登入成功過一次之後,不斷的獲取私信內容了,如果使用者想退出登入,只需要將SharedPreferences中的Cookie資訊刪除即可,簡單吧!