第25講:你有許可權嗎?解析模擬登錄基本原理

2020-08-10 11:54:27

在很多情況下,一些網站的頁面或資源我們通常需要登錄才能 纔能看到。比如存取 GitHub 的個人設定頁面,如果不登錄是無法檢視的;比如 12306 買票提交訂單的頁面,如果不登錄是無法提交訂單的;再比如要發一條微博,如果不登錄是無法發送的。

我們之前學習的案例都是爬取的無需登錄即可存取的站點,但是諸如上面例子的情況非常非常多,那假如我們想要用爬蟲來存取這些頁面,比如用爬蟲修改 GitHub 的個人設定,用爬蟲提交購票訂單,用爬蟲發微博,能做到嗎?

答案是可以,這裏就需要用到一些模擬登錄相關的技術了。那麼本課時我們就先來了解模擬登錄的一些基本原理和實現吧。

網站登錄驗證的實現

我們要實現模擬登錄,那就得首先瞭解網站登錄驗證的實現。

登錄一般需要兩個內容,使用者名稱和密碼,有的網站可能是手機號和驗證碼,有的是微信掃碼,有的是 OAuth 驗證等等,但根本上來說,都是把一些可供認證的資訊提交給了伺服器。

比如這裏我們就拿使用者名稱和密碼來舉例吧。使用者在一個網頁表單裏面輸入了內容,然後點選登錄按鈕的一瞬間,瀏覽器用戶端就會向伺服器發送一個登錄請求,這個請求裏面肯定就包含了使用者名稱和密碼資訊,這時候,伺服器需要處理這些資訊,然後返回給用戶端一個類似「憑證」的東西,有了這個「憑證」以後呢,用戶端拿着這個「憑證」再去存取某些需要登錄才能 纔能檢視的頁面,伺服器自然就能「放行」了,然後返回對應的內容或執行對應的操作就好了。

形象地說,我們以登錄發微博和買票坐火車這兩件事來類比。發微博就好像要坐火車,沒票是沒法坐火車的吧,要坐火車怎麼辦呢?當然是先買票了,我們拿錢去火車站買了票,有了票之後,進站口查驗一下,沒問題就自然能去坐火車了,這個票就是坐火車的「憑證」。

發微博也一樣,我們有使用者名稱和密碼,請求下伺服器,獲得一個「憑證」,這就相當於買到了火車票,然後在發微博的時候拿着這個「憑證」去請求伺服器,伺服器校驗沒問題,自然就把微博發出去了。

那麼問題來了,這個「憑證「」到底是怎麼生成和驗證的呢?目前比較流行的實現方式有兩種,一種是基於 Session + Cookies 的驗證,一種是基於 JWT(JSON Web Token)的驗證,下面 下麪我們來介紹下。

Session 和 Cookies

我們在模組一瞭解了 Session 和 Cookies 的基本概念。簡而言之,Session 就是存在伺服器端的,裏面儲存了使用者此次存取的對談資訊,Cookies 則是儲存在使用者本地瀏覽器的,它會在每次使用者存取網站的時候發送給伺服器,Cookies 會作爲 Request Headers 的一部分發送給伺服器,伺服器根據 Cookies 裏面包含的資訊判斷找出其 Session 物件,不同的 Session 物件裏面維持了不同存取使用者的狀態,伺服器可以根據這些資訊決定返回 Response 的內容。

我們以使用者登錄的情形來舉例,其實不同的網站對於使用者的登錄狀態的實現可能是不同的,但是 Session 和 Cookies 一定是相互配合工作的。

梳理如下:

  • 比如,Cookies 裏面可能只存了 Session ID 相關資訊,伺服器能根據 Cookies 找到對應的 Session,使用者登錄之後,伺服器會在對應的 Session 裏面標記一個欄位,代表已登錄狀態或者其他資訊(如角色、登錄時間)等等,這樣使用者每次存取網站的時候都帶着 Cookies 來存取,伺服器就能找到對應的 Session,然後看一下 Session 裏面的狀態是登錄狀態,就可以返回對應的結果或執行某些操作。
  • 當然 Cookies 裏面也可能直接存了某些憑證資訊。比如說使用者在發起登錄請求之後,伺服器校驗通過,返回給用戶端的 Response Headers 裏面可能帶有 Set-Cookie 欄位,裏面可能就包含了類似憑證的資訊,這樣用戶端會執行 Set Cookie 的操作,將這些資訊儲存到 Cookies 裏面,以後再存取網頁時攜帶這些 Cookies 資訊,伺服器拿着這裏面的資訊校驗,自然也能實現登錄狀態檢測了。

以上兩種情況幾乎能涵蓋大部分的 Session 和 Cookies 登錄驗證的實現,具體的實現邏輯因伺服器而異,但 Session 和 Cookies 一定是需要相互配合才能 纔能實現的。

JWT

Web 開發技術是一直在發展的,近幾年前後端分離的趨勢越來越火,很多 Web 網站都採取了前後端分離的技術來實現。而且傳統的基於 Session 和 Cookies 的校驗也存在一定問題,比如伺服器需要維護登錄使用者的 Session 資訊,而且不太方便分佈式部署,也不太適合前後端分離的專案。

所以,JWT 技術應運而生。JWT,英文全稱叫作 JSON Web Token,是爲了在網路應用環境間傳遞宣告而執行的一種基於 JSON 的開放標準。實際上就是每次登錄的時候通過一個 Token 字串來校驗登錄狀態。

JWT 的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其他業務邏輯所必須的宣告資訊,所以這個 Token 也可直接被用於認證,也可傳遞一些額外資訊。

有了 JWT,一些認證就不需要藉助於 Session 和 Cookies 了,伺服器也無需維護 Session 資訊,減少了伺服器的開銷。伺服器只需要有一個校驗 JWT 的功能就好了,同時也可以做到分佈式部署和跨語言的支援。

JWT 通常就是一個加密的字串,它也有自己的標準,類似下面 下麪的這種格式:

eyJ0eXAxIjoiMTIzNCIsImFsZzIiOiJhZG1pbiIsInR5cCI6IkpXVCIsImFsZyI6IkhTMjU2In0.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiIsImV4cCI6MTU1MjI4Njc0Ni44Nzc0MDE4fQ.pEgdmFAy73walFonEm2zbxg46Oth3dlT02HR9iVzXa8

可以發現中間有兩個「.」來分割開,可以把它看成是一個三段式的加密字串。它由三部分構成,分別是 HeaderPayloadSignature

  • Header,宣告瞭 JWT 的簽名演算法,如 RSA、SHA256 等等,也可能包含 JWT 編號或型別等數據,然後整個資訊 Base64 編碼即可。
  • Payload,通常用來存放一些業務需要但不敏感的資訊,如 UserID 等,另外它也有很多預設的欄位,如 JWT 簽發者、JWT 接受者、JWT 過期時間等等,Base64 編碼即可。
  • Signature,這個就是一個簽名,是把 Header、Payload 的資訊用祕鑰 secret 加密後形成的,這個 secret 是儲存在伺服器端的,不能被輕易泄露。這樣的話,即使一些 Payload 的資訊被篡改,伺服器也能通過 Signature 判斷出來是非法請求,拒絕服務。

這三部分通過「.」組合起來就形成了 JWT 的字串,就是使用者的存取憑證。

所以這個登錄認證流程也很簡單了,使用者拿着使用者名稱密碼登錄,然後伺服器生成 JWT 字串返回給用戶端,用戶端每次請求都帶着這個 JWT 就行了,伺服器會自動判斷其有效情況,如果有效,那自然就返回對應的數據。但 JWT 的傳輸就多種多樣了,可以放在 Request Headers,也可以放在 URL 裡,甚至有的網站也放在 Cookies 裡,但總而言之,能傳給伺服器校驗就好了。

好,到此爲止呢,我們就已經瞭解了網站登錄驗證的實現了。

模擬登錄

好,瞭解了網站登錄驗證的實現後,模擬登錄自然就有思路了。下面 下麪我們也是分兩種認證方式來說明。

Session 和 Cookies

基於 Session 和 Cookies 的模擬登錄,如果我們要用爬蟲實現的話,其實最主要的就是把 Cookies 的資訊維護好,因爲爬蟲就相當於用戶端瀏覽器,我們模擬好瀏覽器做的事情就好了。

那一般情況下,模擬登錄一般可以怎樣實現呢,我們結合之前所講的技術來總結一下:

  • 第一,如果我們已經在瀏覽器裏面登錄了自己的賬號,我們要想用爬蟲模擬的話,可以直接把 Cookies 複製過來交給爬蟲就行了,這也是最省事省力的方式。這就相當於,我們用瀏覽器手動操作登錄了,然後把 Cookies 拿過來放到程式碼裏面,爬蟲每次請求的時候把 Cookies 放到 Request Headers 裏面,就相當於完全模擬了瀏覽器的操作,伺服器會通過 Cookies 校驗登錄狀態,如果沒問題,自然可以執行某些操作或返回某些內容了。
  • 第二,如果我們不想有任何手工操作,可以直接使用爬蟲來模擬登錄過程。登錄的過程其實多數也是一個 POST 請求,我們用爬蟲提交使用者名稱密碼等資訊給伺服器,伺服器返回的 Response Headers 裏面可能帶了 Set-Cookie 的欄位,我們只需要把這些 Cookies 儲存下來就行了。所以,最主要的就是把這個過程中的 Cookies 維護好就行了。當然這裏可能會遇到一些困難,比如登錄過程還伴隨着各種校驗參數,不好直接模擬請求,也可能網站設定 Cookies 的過程是通過 JavaScript 實現的,所以可能還得仔細分析下其中的一些邏輯,尤其是我們用 requests 這樣的請求庫進行模擬登錄的時候,遇到的問題可能比較多。
  • 第三,我們也可以用一些簡單的方式來實現模擬登錄,即把人在瀏覽器中手工登錄的過程自動化實現,比如我們用 Selenium 或 Pyppeteer 來驅動瀏覽器模擬執行一些操作,如填寫使用者名稱、密碼和表單提交等操作,等待登錄成功之後,通過 Selenium 或 Pyppeteer 獲取當前瀏覽器的 Cookies 儲存起來即可。然後後續的請求可以攜帶 Cookies 的內容請求,同樣也能實現模擬登錄。

以上介紹的就是一些常用的爬蟲模擬登錄的方案,其目的就是維護好用戶端的 Cookies 資訊,然後每次請求都攜帶好 Cookies 資訊就能實現模擬登錄了。

JWT

基於 JWT 的真實情況也比較清晰了,由於 JWT 的這個字串就是使用者存取的憑證,那麼模擬登錄只需要做到下面 下麪幾步即可:

  • 第一,模擬網站登錄操作的請求,比如攜帶使用者名稱和密碼資訊請求登錄介面,獲取伺服器返回結果,這個結果中通常包含 JWT 字串的資訊,儲存下來即可。
  • 第二,後續的請求攜帶 JWT 存取即可,一般情況在 JWT 不過期的情況下都能正常存取和執行對應的操作。攜帶方式多種多樣,因網站而異。
  • 第三,如果 JWT 過期了,可能需要重複步驟一,重新獲取 JWT。當然這個模擬登錄的過程也肯定帶有其他的一些加密參數,需要根據實際情況具體分析

優化方案

如果爬蟲要求爬取的數據量比較大或爬取速度比較快,而網站又有單賬號併發限制或者存取狀態檢測並反爬的話,可能我們的賬號就會無法存取或者面臨封號的風險了。這時候一般怎麼辦呢?

我們可以使用分流的方案來解決,比如某個網站一分鐘之內檢測一個賬號只能存取三次或者超過三次就封號的話,我們可以建立一個賬號池,用多個賬號來隨機存取或爬取,這樣就能數倍提高爬蟲的併發量或者降低被封的風險了。

比如在存取某個網站的時候,我們可以準備 100 個賬號,然後 100 個賬號都模擬登錄,把對應的 Cookies 或 JWT 存下來,每次存取的時候隨機取一個來存取,由於賬號多,所以每個賬號被取用的概率也就降下來了,這樣就能避免單賬號併發過大的問題,也降低封號風險。

以上,我們就介紹完了模擬登錄的基本原理和實現以及優化方案,希望你可以好好理解。