一、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