一、Golang模擬使用者登陸,突破教務系統
1.1 請求登陸頁面
整個流程中的第一步是獲取登陸頁面,就像下圖這樣人為的通過瀏覽器訪問服務端,服務端返回反饋返回登陸頁面
訪問登陸頁面的目的上圖中標註出來了,為了獲取到Cookie,給真正發起登陸到請求方法使用。
下面的golang傳送http到get請求,獲取登陸頁面的程式碼:
// 訪問登陸也,獲取cookie
func GetCookieFromLoginhtml(url string) (cookie string, e error) {
res, err := http.Get(url)
if err != nil {
e = err
}
// 獲取cookie
cookie = res.Header.Get("Set-Cookie")
cookie = util.GetOneValueByPrefixAndSurfix("JSESSIONID=", "; Path=/", cookie)
res.Body.Close()
return
}
1.2 抓包分析登陸請求
輸入賬號賬號密碼後點選登陸,將向後端傳送登陸請求,如下圖:
分析向後端傳送到登陸請求都攜帶了哪些請求引數,攜帶了哪些請求頭資訊,以及需要通過Content-Type判斷,該如何處理form表單中的資料傳送到後臺。後臺才能正常響應。
在瀏覽器的控制檯中我們可以去看下登陸頁面原始碼
登陸頁面對應的js原始碼
1.3 golang使用js引擎合成salt
這一步也是必須的,所謂獲取salt,其實就是通過golang使用js引擎執行encodeInp(xxx)
, 這樣我們才能得到經過加密後的username和password,進一步獲取到encoded
import (
"github.com/robertkrimen/otto"
"io/ioutil"
)
func EncodeInp(input string)(result string,e error) {
jsfile := "js/encodeUriJs.js"
bytes, err := ioutil.ReadFile(jsfile)
if err != nil {
e = err
}
vm := otto.New()
_, err = vm.Run(string(bytes))
if err != nil {
e = err
}
enc,err :=vm.Call("encodeInp",nil,input)
if err != nil {
e = err
}
result = enc.String()
return
}
js部分的程式碼就不往外貼了,可以去下面的github地址中獲取
1.4 模擬表單提交,完成登陸
使用golang模擬登陸請求
// 模擬登陸
func login(salt, cookie string) (html string) {
req, err := http.NewRequest("POST", LoginUrl, strings.NewReader("encoded="+salt))
// 新增請求頭
req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36")
req.Header.Add("Cookie", "JSESSIONID="+cookie)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
//傳送請求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("無密版qlu教務系統登陸請求失敗 : %v", err)
return
}
// todo 根據狀態碼判斷下一步如何操作,如果狀態碼是302,表示操作成功
fmt.Println("resp.Status: ", resp.Status)
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("error : %v", err)
return
}
// 返回個人主頁的html
html = string(b)
// 手動關閉
resp.Body.Close()
return
}
這一步中值得注意的地方:
第一:我們傳送的請求的型別是POST請求
第二:我們應該如何處理form表單中的資料後,再傳送給後端,後端才能正常處理呢?
具體處理成什麼樣,是需要根據請求頭中的Content-Type決定的。
不知道大家知不知道常見的Content-Type的幾種型別:在form 表單中有一個屬性叫做 entype可以間接將資料處理成Content-Type指定資料格式, 比如我們可以像這樣設定:
-
enctype = text/plain
那麼form表單最終提交的格式就是: 用純文字的形式傳送。 -
enctype = application/x-www-form-urlencoded
- 表單中的enctype值如果不設定,則預設是application/x-www-form-urlencoded,它會將表單中的資料變為鍵值對的形式。
- 如果action為get,則將表單資料編碼為(name1=value1&name2=value2…),然後把這個字串加到url後面,中間用?分隔。
- 如果action為post,瀏覽器把form資料封裝到http body中,然後傳送到伺服器。
-
enctype = mutipart/form-data
- 上傳的是非文字內容,比如是個圖片,檔案,mp3。
根據這個知識點,結合我們當前的情況,method=post,Content-Type = application/x-www-form-urlencoded
所以,在選擇golang的api時,我們選擇下圖這個api使用
1.5 進入成績查詢頁,解析使用者成績
如果不出意外,經過上面的處理,我們已經完成登陸,並且獲取到後臺頁面的html原始碼了。
再之後我們就直奔成績查詢模組,還是使用如何的分析思路
func getAllScore(stuIdentify, cookie string) ([]mtStruct.Score, error) {
// 傳送查詢成績的請求
u := "http://jwxt.qlu.edu.cn/jsxsd/kscj/cjcx_list"
req, err := http.NewRequest("POST", u, strings.NewReader("kksj=&kcxz=&kcmc=&xsfs=all"))
if err != nil {
fmt.Printf("error : %v", err)
return nil, err
}
req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36")
req.Header.Add("Cookie", "JSESSIONID="+cookie)
req.Header.Add("Referer", "http://jwxt.qlu.edu.cn/jsxsd/kscj/cjcx_query?Ves632DSdyV=NEW_XSD_XJCJ")
req.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*;q=0.8,application/signed-exchange;v=b3;q=0.9")
client := &http.Client{}
resp, err := client.Do(req)
...
}
程式碼詳情可以去github上檢視。
二、植入微信公共號後臺
上面的功能實現後再結合Golang開發微信公眾號就能實現一款好玩的應用。
讓使用者通過微信公共號平臺和後端進行資料的互動,我們獲取到使用者的資訊,拿著使用者的資訊幫使用者監聽教務系統的成績單的狀態。一旦有成績第一時間推送給使用者。
專案GitHub地址:https://github.com/zhuchangwu/golang-wechat-backend