diff --git a/app/apis/library/libraryApi.go b/app/apis/library/libraryApi.go index 87ec806..738aea9 100644 --- a/app/apis/library/libraryApi.go +++ b/app/apis/library/libraryApi.go @@ -1,9 +1,19 @@ package library -import "funnel/app/apis" +// OAuth +const ( + OAuthChaoXingInit = "https://tyrzfw.chaoxing.com/auth3/zjut/cas/init?isLogout=0&refer=https://opac.lib.zjut.edu.cn:8013/find/sso/login/zjut/0" + OAuthBaseUrl = "https://oauth.zjut.edu.cn/cas" + OAuthPublicKey = OAuthBaseUrl + "/v2/getPubKey" + OAuthLogin = OAuthBaseUrl + "/login" +) -var LibraryLogin = apis.LIBRARY_URL + "login.aspx" +// Library +const ( + LoginFromOAuth = OAuthLogin + "?service=http://tyrzfw.chaoxing.com/auth3/zjut/cas/index" -var LibraryBorrowHistory = apis.LIBRARY_URL + "BorrowHistory.aspx" - -var LibraryBorrowing = apis.LIBRARY_URL + "Borrowing.aspx" + BaseUrl = "https://opac.lib.zjut.edu.cn:8013" + CurrentLoanList = BaseUrl + "/find/loanInfo/loanList" + BorrowHistoryList = BaseUrl + "/find/loanInfo/loanHistoryList" + UserInfo = BaseUrl + "/oga/userinfo" +) diff --git a/app/apis/rootUrls.go b/app/apis/rootUrls.go index 56d0c19..162bca0 100644 --- a/app/apis/rootUrls.go +++ b/app/apis/rootUrls.go @@ -12,7 +12,7 @@ import ( //var ZF_Main_URL = os.Getenv("ZF_URL") //var ZF_BK_URL = os.Getenv("ZF_URL_BK") -var LIBRARY_URL string = config.Config.GetString("library") +//var LIBRARY_URL string = config.Config.GetString("library") var CAPTCHA_BREAKER_URL string = config.Config.GetString("captcha.breaker_url") var CAPTCHA_NEW_BREAKER_URL string = config.Config.GetString("captcha.new_breaker_url") diff --git a/app/controller/libraryController/borrowHistory.go b/app/controller/libraryController/borrowHistory.go new file mode 100644 index 0000000..9a07872 --- /dev/null +++ b/app/controller/libraryController/borrowHistory.go @@ -0,0 +1,29 @@ +package libraryController + +import ( + "funnel/app/controller" + "funnel/app/service/libraryService" + "funnel/app/utils" + "github.com/gin-gonic/gin" +) + +type borrowHistoryData struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + Page int `json:"page" default:"1"` +} + +// LibraryBorrowHistory 图书馆历史借书记录 +func LibraryBorrowHistory(c *gin.Context) { + var data borrowHistoryData + if err := c.ShouldBind(&data); err != nil { + controller.ErrorHandle(c, err) + return + } + currentBorrow, err := libraryService.GetBorrowHistory(data.Username, data.Password, data.Page) + if err != nil { + controller.ErrorHandle(c, err) + return + } + utils.ContextDataResponseJson(c, utils.SuccessResponseJson(currentBorrow)) +} diff --git a/app/controller/libraryController/currentBorrow.go b/app/controller/libraryController/currentBorrow.go new file mode 100644 index 0000000..a2770c3 --- /dev/null +++ b/app/controller/libraryController/currentBorrow.go @@ -0,0 +1,29 @@ +package libraryController + +import ( + "funnel/app/controller" + "funnel/app/service/libraryService" + "funnel/app/utils" + "github.com/gin-gonic/gin" +) + +type currentBorrowData struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + Page int `json:"page" default:"1"` +} + +// LibraryCurrentBorrow 图书馆当前借书记录 +func LibraryCurrentBorrow(c *gin.Context) { + var data currentBorrowData + if err := c.ShouldBind(&data); err != nil { + controller.ErrorHandle(c, err) + return + } + currentBorrow, err := libraryService.GetCurrentBorrow(data.Username, data.Password, data.Page) + if err != nil { + controller.ErrorHandle(c, err) + return + } + utils.ContextDataResponseJson(c, utils.SuccessResponseJson(currentBorrow)) +} diff --git a/app/controller/libraryController/libraryController.go b/app/controller/libraryController/libraryController.go deleted file mode 100644 index 35b491b..0000000 --- a/app/controller/libraryController/libraryController.go +++ /dev/null @@ -1,115 +0,0 @@ -package libraryController - -import ( - "funnel/app/controller" - "funnel/app/errors" - "funnel/app/service/libraryService" - "funnel/app/utils" - "github.com/gin-gonic/gin" -) - -// LibraryBorrowHistory -// -// @Summary 图书馆历史借书记录 -// @Description 图书馆借书记录(暂时只支持10本) -// @Tags 图书馆 -// @Produce json -// @Param username body string true "用户名" -// @Param password body string true "密码" -// @Param type body string true "登录类型" -// @Success 200 json {"code":200,"data":[{...}],"msg":"OK"} -// @Failure 400 json {"code":400,"data":null,"msg":""} -// @Router /student/libraryService/history/0 [post] -func LibraryBorrowHistory(context *gin.Context) { - - user, err := controller.LoginHandle(context, libraryService.GetUser, false) - if err != nil { - return - } - books, err := libraryService.GetBorrowHistory(user) - - if err == errors.ERR_Session_Expired { - user, err = controller.LoginHandle(context, libraryService.GetUser, false) - books, err = libraryService.GetBorrowHistory(user) - } - - if err != nil { - controller.ErrorHandle(context, err) - return - } - utils.ContextDataResponseJson(context, utils.SuccessResponseJson(books)) -} - -// LibraryCurrentBorrow -// -// @Summary 图书馆当前借书记录 -// @Description 图书馆当前借书记录 -// @Tags 图书馆 -// @Produce json -// @Param username body string true "用户名" -// @Param password body string true "密码" -// @Param type body string true "登录类型" -// @Success 200 json {"code":200,"data":[{...}],"msg":"OK"} -// @Failure 400 json {"code":400,"data":null,"msg":""} -// @Router /student/libraryService/current [post] -func LibraryCurrentBorrow(context *gin.Context) { - user, err := controller.LoginHandle(context, libraryService.GetUser, false) - if err != nil { - return - } - books, err := libraryService.GetCurrentBorrow(user) - - if err == errors.ERR_Session_Expired { - user, err = controller.LoginHandle(context, libraryService.GetUser, false) - books, err = libraryService.GetCurrentBorrow(user) - } - - if err != nil { - controller.ErrorHandle(context, err) - return - } - utils.ContextDataResponseJson(context, utils.SuccessResponseJson(books)) -} - -// LibraryReBorrow -// -// @Summary 图书馆续借 -// @Description 图书馆续借 -// @Tags 图书馆 -// @Produce json -// @Param username body string true "用户名" -// @Param password body string true "密码" -// @Param type body string true "登录类型" -// @Param libraryID body string true "图书id" -// @Success 200 json {"code":200,"data":[{...}],"msg":"OK"} -// @Failure 400 json {"code":400,"data":null,"msg":""} -// @Router /student/libraryService/reborrow [post] -func LibraryReBorrow(context *gin.Context) { - isValid := utils.CheckPostFormEmpty( - context, - []string{"username", "password", "type", "libraryID"}, - ) - - if !isValid { - utils.ContextDataResponseJson(context, utils.FailResponseJson(errors.InvalidArgs, nil)) - return - } - - user, err := controller.LoginHandle(context, libraryService.GetUser, false) - if err != nil { - return - } - - err = libraryService.DoReBorrow(user, context.PostForm("libraryID")) - - if err == errors.ERR_Session_Expired { - user, err = controller.LoginHandle(context, libraryService.GetUser, false) - err = libraryService.DoReBorrow(user, context.PostForm("libraryID")) - } - - if err != nil { - controller.ErrorHandle(context, err) - return - } - utils.ContextDataResponseJson(context, utils.SuccessResponseJson(nil)) -} diff --git a/app/controller/libraryController/reBorrow.go b/app/controller/libraryController/reBorrow.go new file mode 100644 index 0000000..c0f753f --- /dev/null +++ b/app/controller/libraryController/reBorrow.go @@ -0,0 +1,14 @@ +package libraryController + +import ( + "funnel/app/utils" + "github.com/gin-gonic/gin" +) + +func LibraryReBorrow(c *gin.Context) { + utils.ContextDataResponseJson(c, utils.ResponseJsonMessage{ + Code: 500, + Message: "功能仍未上架,敬请期待", + Data: interface{}(nil), + }) +} diff --git a/app/service/libraryService/borrowHistory.go b/app/service/libraryService/borrowHistory.go new file mode 100644 index 0000000..a8a6ea4 --- /dev/null +++ b/app/service/libraryService/borrowHistory.go @@ -0,0 +1,32 @@ +package libraryService + +import ( + "funnel/app/apis/library" + "funnel/app/errors" + "funnel/app/service/libraryService/request" +) + +// GetBorrowHistory 获取图书馆当前借书记录 +func GetBorrowHistory(username string, password string, page int) (interface{}, error) { + var ret Result + cookies, err := OAuthLogin(username, password) + if err != nil { + return nil, errors.ERR_WRONG_PASSWORD + } + + client := request.New() + + _, err = client.Request(). + SetBody(map[string]interface{}{ + "page": page, + "rows": 10, + "searchType": 1, + "searchContent": "", + "sortType": 0, + "startDate": nil, + "endDate": nil}). + SetCookies(cookies). + SetResult(&ret). + Post(library.BorrowHistoryList) + return ret.Data, nil +} diff --git a/app/service/libraryService/currentBorrow.go b/app/service/libraryService/currentBorrow.go new file mode 100644 index 0000000..6c08782 --- /dev/null +++ b/app/service/libraryService/currentBorrow.go @@ -0,0 +1,30 @@ +package libraryService + +import ( + "funnel/app/apis/library" + "funnel/app/errors" + "funnel/app/service/libraryService/request" +) + +// GetCurrentBorrow 获取图书馆当前借书记录 +func GetCurrentBorrow(username string, password string, page int) (interface{}, error) { + var ret Result + cookies, err := OAuthLogin(username, password) + if err != nil { + return nil, errors.ERR_WRONG_PASSWORD + } + client := request.New() + _, err = client.Request(). + SetBody(map[string]interface{}{ + "page": page, + "rows": 10, + "searchType": 1, + "searchContent": "", + "sortType": 0, + "startDate": nil, + "endDate": nil}). + SetCookies(cookies). + SetResult(&ret). + Post(library.CurrentLoanList) + return ret.Data, nil +} diff --git a/app/service/libraryService/dataGen.go b/app/service/libraryService/dataGen.go deleted file mode 100644 index b4fdf6c..0000000 --- a/app/service/libraryService/dataGen.go +++ /dev/null @@ -1,150 +0,0 @@ -package libraryService - -import ( - "funnel/app/model" - "github.com/PuerkitoBio/goquery" - "net/url" - "strings" -) - -func genLoginData(doc *goquery.Document, username, password string) url.Values { - return url.Values{ - "__VIEWSTATE": {doc.Find("#__VIEWSTATE").AttrOr("value", "")}, - "__VIEWSTATEGENERATOR": {doc.Find("#__VIEWSTATEGENERATOR").AttrOr("value", "")}, - "__EVENTVALIDATION": {doc.Find("#__EVENTVALIDATION").AttrOr("value", "")}, - "TextBox1": {username}, - "TextBox2": {password}, - "ImageButton1.x": {"29"}, - "ImageButton1.y": {"8"}} -} - -func genBorrowHistoryForm(doc *goquery.Document) url.Values { - return url.Values{ - "__EVENTTARGET": {"ctl00$ContentPlaceHolder1$GridView1"}, - "__EVENTARGUMENT": {"Page$Next"}, - "ctl00_TreeView1_ExpandState": {doc.Find("#ctl00_TreeView1_ExpandState").AttrOr("value", "")}, - "ctl00_TreeView1_SelectedNode": {doc.Find("#ctl00_TreeView1_SelectedNode").AttrOr("value", "")}, - "ctl00_TreeView1_PopulateLog": {doc.Find("#ctl00_TreeView1_PopulateLog").AttrOr("value", "")}, - "__VIEWSTATE": {doc.Find("#__VIEWSTATE").AttrOr("value", "")}, - "__VIEWSTATEGENERATOR": {doc.Find("#__VIEWSTATEGENERATOR").AttrOr("value", "")}, - "__EVENTVALIDATION": {doc.Find("#__EVENTVALIDATION").AttrOr("value", "")}} -} - -func genCurrentBorrowFormFirst(doc *goquery.Document) url.Values { - return url.Values{ - "ctl00$ScriptManager1": {"ctl00$ContentPlaceHolder1$UpdatePanel1|ctl00$ContentPlaceHolder1$GridView1"}, - "__EVENTTARGET": {"ctl00$ContentPlaceHolder1$GridView1"}, - "__EVENTARGUMENT": {"Page$Next"}, - "ctl00_TreeView1_ExpandState": {"eennnnnnennnnnnnennnnennen"}, - "ctl00_TreeView1_SelectedNode": {""}, - "ctl00_TreeView1_PopulateLog": {""}, - "__LASTFOCUS": {doc.Find("#__LASTFOCUS").AttrOr("value", "")}, - "__VIEWSTATE": {doc.Find("#__VIEWSTATE").AttrOr("value", "")}, - "__VIEWSTATEGENERATOR": {doc.Find("#__VIEWSTATEGENERATOR").AttrOr("value", "")}, - "__VIEWSTATEENCRYPTED": {doc.Find("#__VIEWSTATEENCRYPTED").AttrOr("value", "")}, - "__EVENTVALIDATION": {doc.Find("#__EVENTVALIDATION").AttrOr("value", "")}, - "__ASYNCPOST": {"true"}} -} - -func genCurrentBorrowForm(doc *goquery.Document) url.Values { - var value []string - hidden := strings.Split(doc.Text(), "|hiddenField|") - for i, v := range hidden { - if i != 0 { - str := strings.Split(v, "|") - value = append(value, str[1]) - } - } - return url.Values{ - "ctl00$ScriptManager1": {"ctl00$ContentPlaceHolder1$UpdatePanel1|ctl00$ContentPlaceHolder1$GridView1"}, - "__EVENTTARGET": {"ctl00$ContentPlaceHolder1$GridView1"}, - "__EVENTARGUMENT": {"Page$Next"}, - "ctl00_TreeView1_ExpandState": {"eennnnnnennnnnnnennnnennen"}, - "ctl00_TreeView1_SelectedNode": {""}, - "ctl00_TreeView1_PopulateLog": {""}, - "__LASTFOCUS": {value[2]}, - "__VIEWSTATE": {value[3]}, - "__VIEWSTATEGENERATOR": {value[4]}, - "__VIEWSTATEENCRYPTED": {value[5]}, - "__EVENTVALIDATION": {value[6]}, - "__ASYNCPOST": {"true"}} -} - -func genReBorrowForm(doc *goquery.Document, tdID string) url.Values { - return url.Values{ - "__VIEWSTATE": {doc.Find("#__VIEWSTATE").AttrOr("value", "")}, - "__VIEWSTATEGENERATOR": {doc.Find("#__VIEWSTATEGENERATOR").AttrOr("value", "")}, - "__VIEWSTATEENCRYPTED": {""}, - "__EVENTVALIDATION": {doc.Find("#__EVENTVALIDATION").AttrOr("value", "")}, - "ctl00$ContentPlaceHolder1$XuJieBt": {"续借"}, - "ctl00$ContentPlaceHolder1$GridView1$" + tdID + "$CheckBox1": {"on"}, - } -} - -func getReBorrowedBookInfoIDFromDoc(doc *goquery.Document, bookid string) string { - tkID := "" - doc.Find("#ctl00_ContentPlaceHolder1_GridView1").Each(func(i int, s *goquery.Selection) { - s.Find("table").Each(func(i int, s *goquery.Selection) { - bookName := strings.Trim(s.Find("a").Text(), " \r\n") - if bookName == "" { - return - } - token := s.Find("span").Nodes - libraryID := strings.Trim(token[0].FirstChild.Data, " \r\n") - if bookid == libraryID { - tok := strings.Split(token[0].Attr[0].Val, "_") - id := tok[len(tok)-2] - tkID = id - return - } - }) - }) - return tkID -} -func genBorrowedBookInfoFromDoc(doc *goquery.Document) ([]model.BorrowedBookInfo, bool) { - var books []model.BorrowedBookInfo - doc.Find("#ctl00_ContentPlaceHolder1_GridView1").Each(func(i int, s *goquery.Selection) { - s.Find("table").Each(func(i int, s *goquery.Selection) { - bookName := strings.Trim(s.Find("a").Text(), " \r\n") - if bookName == "" { - return - } - token := s.Find("span").Nodes - libraryID := strings.Trim(token[0].FirstChild.Data, " \r\n") - libraryPlace := strings.Trim(token[1].FirstChild.Data, " \r\n") - renewalsTimes := strings.Trim(token[2].FirstChild.Data, " \r\n") - time := strings.Trim(token[3].FirstChild.Data, " \r\n") - returnTime := strings.Trim(token[4].FirstChild.Data, " \r\n") - - isExtended := strings.Trim(token[5].FirstChild.Data, " \r\n") - book := model.BorrowedBookInfo{ - Name: bookName, - LibraryID: libraryID, - LibraryPlace: libraryPlace, - Time: time, - ReturnTime: returnTime, - RenewalsTimes: renewalsTimes, - OverdueTime: isExtended} - books = append(books, book) - }) - }) - html, _ := doc.Html() - return books, strings.Contains(html, "pic/NextPage.png") -} - -func genBorrowedBookHistoryInfoFromDoc(doc *goquery.Document) ([]model.BorrowedBookInfo, bool) { - var books []model.BorrowedBookInfo - doc.Find("tr[onmouseout=\"this.style.backgroundColor=c\"]").Each(func(i int, s *goquery.Selection) { - onmouseout, _ := s.Attr("onmouseout") - if onmouseout == "this.style.backgroundColor=c" { - bookName := strings.Trim(s.Find("a").Text(), " \n\r") - bookId := strings.Trim(s.Find("td").Last().Prev().Prev().Text(), " \r\n") - borrowTime := strings.Trim(s.Find("td").Last().Prev().Text(), " \r\n") - returnTime := strings.Trim(s.Find("td").Last().Text(), " \r\n") - book := model.BorrowedBookInfo{Name: bookName, LibraryID: bookId, Time: borrowTime, ReturnTime: returnTime} - books = append(books, book) - } - }) - html, _ := doc.Html() - return books, strings.Contains(html, "pic/NextPage.png") -} diff --git a/app/service/libraryService/libraryService.go b/app/service/libraryService/libraryService.go deleted file mode 100644 index 44793f6..0000000 --- a/app/service/libraryService/libraryService.go +++ /dev/null @@ -1,120 +0,0 @@ -package libraryService - -import ( - "funnel/app/apis/library" - "funnel/app/errors" - "funnel/app/model" - "funnel/app/service" - "funnel/app/utils/fetch" - "github.com/PuerkitoBio/goquery" - "net/url" - "strings" -) - -func GetBorrowHistory(user *model.User) ([]model.BorrowedBookInfo, error) { - f := fetch.Fetch{} - f.Init() - f.Cookie = append(f.Cookie, &user.Session) - response, err := f.GetRaw(library.LibraryBorrowHistory) - if response != nil && response.StatusCode != 200 { - service.ForgetUserByUsername(service.LibraryPrefix, user.Username) - return nil, errors.ERR_Session_Expired - } - - if err != nil { - return nil, err - } - - doc, err := goquery.NewDocumentFromReader(response.Body) - if err != nil { - return nil, err - } - - books, next := genBorrowedBookHistoryInfoFromDoc(doc) - for next { - form := genBorrowHistoryForm(doc) - response, _ = f.PostFormRaw(library.LibraryBorrowHistory, form) - doc, err = goquery.NewDocumentFromReader(response.Body) - booksNew, nextNew := genBorrowedBookHistoryInfoFromDoc(doc) - books = append(books, booksNew...) - next = nextNew - } - return books, nil -} - -func GetCurrentBorrow(user *model.User) ([]model.BorrowedBookInfo, error) { - f := fetch.Fetch{} - f.Init() - f.Cookie = append(f.Cookie, &user.Session) - response, err := f.GetRaw(library.LibraryBorrowing) - if response != nil && response.StatusCode != 200 { - service.ForgetUserByUsername(service.LibraryPrefix, user.Username) - return nil, errors.ERR_Session_Expired - } - if err != nil { - return nil, err - } - - doc, err := goquery.NewDocumentFromReader(response.Body) - if err != nil { - return nil, err - } - - books, next := genBorrowedBookInfoFromDoc(doc) - - var form = url.Values{} - if next { - form = genCurrentBorrowFormFirst(doc) - response, _ = f.PostFormRawAsynchronous(library.LibraryBorrowing, form) - doc, err = goquery.NewDocumentFromReader(response.Body) - booksNew, nextNew := genBorrowedBookInfoFromDoc(doc) - books = append(books, booksNew...) - next = nextNew - } - for next { - form = genCurrentBorrowForm(doc) - response, _ = f.PostFormRawAsynchronous(library.LibraryBorrowing, form) - doc, err = goquery.NewDocumentFromReader(response.Body) - booksNew, nextNew := genBorrowedBookInfoFromDoc(doc) - books = append(books, booksNew...) - next = nextNew - } - return books, nil -} - -func DoReBorrow(user *model.User, bookid string) error { - f := fetch.Fetch{} - f.Init() - f.Cookie = append(f.Cookie, &user.Session) - response, err := f.GetRaw(library.LibraryBorrowing) - if response != nil && response.StatusCode != 200 { - service.ForgetUserByUsername(service.LibraryPrefix, user.Username) - return errors.ERR_Session_Expired - } - if err != nil { - return err - } - - doc, err := goquery.NewDocumentFromReader(response.Body) - - if err != nil { - return err - } - - tkID := getReBorrowedBookInfoIDFromDoc(doc, bookid) - - if tkID == "" { - return errors.ERR_INVALID_ARGS - } - q := genReBorrowForm(doc, tkID) - print(q.Encode()) - raw, err := f.PostForm(library.LibraryBorrowing, q) - if err != nil { - return err - } - print(string(raw)) - if strings.Contains(string(raw), "续借成功!") { - return nil - } - return errors.ERR_INVALID_ARGS -} diff --git a/app/service/libraryService/login.go b/app/service/libraryService/login.go index ade4841..9846f54 100644 --- a/app/service/libraryService/login.go +++ b/app/service/libraryService/login.go @@ -1,41 +1,81 @@ package libraryService import ( + "bytes" "funnel/app/apis/library" - "funnel/app/controller" "funnel/app/errors" - "funnel/app/model" - "funnel/app/service" - "funnel/app/utils/fetch" + "funnel/app/service/libraryService/request" "github.com/PuerkitoBio/goquery" + "net/http" + "net/http/cookiejar" + "net/url" ) -func GetUser(username, password string, loginType controller.LoginType, typeFlag bool) (*model.User, error) { - // TODO 这里登录类型尚未使用 - user, err := service.GetUser(service.LibraryPrefix, username, password) - if err != nil || typeFlag { - return login(username, password) +// OAuthLogin 统一登陆 +func OAuthLogin(username string, password string) ([]*http.Cookie, error) { + client := request.New() + // 使用cookieJar管理cookie + cookieJar, _ := cookiejar.New(nil) + client.SetCookieJar(cookieJar) + + // 1. 初始化请求 + if _, err := client.Request(). + Get(library.OAuthChaoXingInit); err != nil { + return nil, err + } + + // 2. 登陆参数生成 + resp, err := client.Request(). + Get(library.LoginFromOAuth) + if err != nil { + return nil, err } - return user, err -} -func login(username string, password string) (*model.User, error) { - f := fetch.Fetch{} - f.Init() + // 解析execution + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body())) + if err != nil { + return nil, err + } + execution := doc. + Find("input[type=hidden][name=execution]"). + AttrOr("value", "") + + // 密码加密 + encPwd, err := GetEncryptedPwd(client, password) - res, err := f.GetRaw(library.LibraryLogin) + loginParams := map[string]string{ + "username": username, + "mobileCode": "", + "password": encPwd, + "authcode": "", + "execution": execution, + "_eventId": "submit", + } + // 3. 发送登陆请求 + resp, err = client.Request(). + SetFormData(loginParams). + Post(library.LoginFromOAuth) if err != nil { return nil, err } - doc, _ := goquery.NewDocumentFromReader(res.Body) - loginData := genLoginData(doc, username, password) - _, err = f.PostForm(library.LibraryLogin, loginData) + // 4. 处理重定向 + // 这里我们需要手动的去处理位于js中的重定向 + // resty只能自动处理header.Location中的重定向 + redirect := GetRedirectLocation(resp.String()) + + resp, err = client.Request(). + Get(redirect) if err != nil { return nil, err } - if len(f.Cookie) == 0 || err != nil { - return nil, errors.ERR_WRONG_PASSWORD + + // 5. 提取指定域名下的session并构造cookie列表 + u, _ := url.Parse(library.BaseUrl) + for _, cookie := range cookieJar.Cookies(u) { + if cookie.Name == "SESSION" { + return []*http.Cookie{cookie}, nil + } } - return service.SetUser(service.LibraryPrefix, username, password, f.Cookie[0], nil) + return nil, errors.ERR_WRONG_PASSWORD } diff --git a/app/service/libraryService/request/client.go b/app/service/libraryService/request/client.go new file mode 100644 index 0000000..39e39eb --- /dev/null +++ b/app/service/libraryService/request/client.go @@ -0,0 +1,25 @@ +package request + +import ( + "funnel/app/service/libraryService/request/midware" + "github.com/go-resty/resty/v2" +) + +type Client struct { + *resty.Client +} + +func New() Client { + s := Client{ + Client: resty.New(), + } + // 利用中间件实现请求日志 + s.OnAfterResponse(midware.LogMiddleware) + return s +} + +func (s Client) Request() *resty.Request { + return s.R(). + EnableTrace(). + SetHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36") +} diff --git a/app/service/libraryService/request/midware/logMidware.go b/app/service/libraryService/request/midware/logMidware.go new file mode 100644 index 0000000..8f09079 --- /dev/null +++ b/app/service/libraryService/request/midware/logMidware.go @@ -0,0 +1,19 @@ +package midware + +import ( + "fmt" + "github.com/go-resty/resty/v2" +) + +// LogMiddleware 日志中间件 +func LogMiddleware(client *resty.Client, resp *resty.Response) error { + fmt.Println("==================================================================") + method := resp.Request.Method + url := resp.Request.URL + fmt.Printf("%s %s \n", method, url) + //fmt.Println(resp.Request.Header) + //fmt.Println(resp) + fmt.Printf("Time Spent: %v\n", resp.Time()) + + return nil +} diff --git a/app/service/libraryService/sign.go b/app/service/libraryService/sign.go new file mode 100644 index 0000000..26f5b66 --- /dev/null +++ b/app/service/libraryService/sign.go @@ -0,0 +1,31 @@ +package libraryService + +import ( + "funnel/app/apis/library" + "funnel/app/service/libraryService/request" + "funnel/app/utils/security" +) + +// OauthLoginGetPublicKey 获取加密密钥 +func OauthLoginGetPublicKey(client request.Client) ([]byte, error) { + resp, err := client.Request(). + Get(library.OAuthPublicKey) + if err != nil { + return []byte{}, err + } + return resp.Body(), nil +} + +// GetEncryptedPwd 密码加密 +func GetEncryptedPwd(client request.Client, password string) (string, error) { + key, err := OauthLoginGetPublicKey(client) + if err != nil { + return "", err + } + + encrypted, err := security.GetEncryptPassword(key, password) + if err != nil { + return "", err + } + return encrypted, nil +} diff --git a/app/service/libraryService/userInfo.go b/app/service/libraryService/userInfo.go new file mode 100644 index 0000000..bfd9b66 --- /dev/null +++ b/app/service/libraryService/userInfo.go @@ -0,0 +1,35 @@ +package libraryService + +import ( + "funnel/app/apis/library" + "funnel/app/service/libraryService/request" + "net/http" +) + +type UserInfo struct { + Success bool `json:"success"` + Message string `json:"message"` + ErrCode int `json:"errCode"` + ErrorCode interface{} `json:"errorCode"` + Data interface{} `json:"data"` +} + +func GetUserInfo(cookies []*http.Cookie) (UserInfo, error) { + var userInfo UserInfo + session := request.New() + _, err := session.Request(). + SetCookies(cookies). + SetResult(&userInfo). + Post(library.UserInfo) + + return userInfo, err +} + +// CheckCookie 判断cookie是否还有效 +func CheckCookie(cookies []*http.Cookie) bool { + userInfo, err := GetUserInfo(cookies) + if err != nil { + return false + } + return userInfo.Success +} diff --git a/app/service/libraryService/utils.go b/app/service/libraryService/utils.go new file mode 100644 index 0000000..5114c44 --- /dev/null +++ b/app/service/libraryService/utils.go @@ -0,0 +1,44 @@ +package libraryService + +import ( + "net/url" + "regexp" + "strings" +) + +// Result 交由Resty实现response的自动反序列化 +type Result struct { + Success bool `json:"success"` + Message string `json:"message"` + ErrCode int `json:"errCode"` + ErrorCode interface{} `json:"errorCode"` + Data struct { + SearchResult []interface{} `json:"searchResult"` + NumFound int `json:"numFound"` + } `json:"data"` +} + +// GetRedirectLocation 获取js中的重定向连接 +func GetRedirectLocation(html string) string { + re := regexp.MustCompile(`window\.location\s*=\s*"([^"]+)"`) + matches := re.FindStringSubmatch(html) + if len(matches) > 1 { + redirectURL := matches[1] + // 去除转义 + redirectURL = regexp.MustCompile(`\\/`).ReplaceAllString(redirectURL, "/") + + // 处理unicode + redirectURL = strings.Replace(redirectURL, `\u0026`, "&", -1) + + // 从redirectURL解析出refer + u, err := url.Parse(redirectURL) + if err != nil { + return "" + } + queries, _ := url.ParseQuery(u.RawQuery) + refer := queries["refer"][0] + + return refer + } + return "" +} diff --git a/app/service/zfService/login.go b/app/service/zfService/login.go index 77bc1a4..31df9d6 100644 --- a/app/service/zfService/login.go +++ b/app/service/zfService/login.go @@ -43,7 +43,7 @@ func login(username string, password string) (*model.User, error) { } else { URL = apis.CAPTCHA_BREAKER_URL } - captcha, err := f.Get(URL + "?session=" + f.Cookie[0].Value + "&route=" + f.Cookie[1].Value) + captcha, err := f.Get(URL + "?request=" + f.Cookie[0].Value + "&route=" + f.Cookie[1].Value) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 83ad983..9d4b0b8 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,10 @@ require ( github.com/PuerkitoBio/goquery v1.6.0 github.com/gin-gonic/gin v1.6.3 github.com/go-redis/redis v6.15.9+incompatible + github.com/go-resty/resty/v2 v2.16.2 github.com/spf13/viper v1.19.0 - golang.org/x/net v0.23.0 - golang.org/x/text v0.14.0 + golang.org/x/net v0.27.0 + golang.org/x/text v0.16.0 ) require ( @@ -40,9 +41,9 @@ require ( github.com/ugorji/go/codec v1.1.13 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.22.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 4750753..477c59b 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/go-playground/validator/v10 v10.4.0 h1:72qIR/m8ybvL8L5TIyfgrigqkrw7kV github.com/go-playground/validator/v10 v10.4.0/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= +github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -125,8 +127,8 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -135,8 +137,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -149,13 +151,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= diff --git a/router/routers.go b/router/routers.go index 8c0250d..2cb87b0 100644 --- a/router/routers.go +++ b/router/routers.go @@ -24,7 +24,7 @@ func SetupRouter(r *gin.Engine) *gin.Engine { zf.POST("/room", zfController.GetRoomInfo) zf.POST("/program", zfController.GetProgInfo) } - library := student.Group("/library", midware.CheckUsernamePassword) + library := student.Group("/library") { library.POST("/borrow/history", libraryController.LibraryBorrowHistory) library.POST("/borrow/current", libraryController.LibraryCurrentBorrow)