150 lines
3.9 KiB
Go
150 lines
3.9 KiB
Go
package login
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"likedream-goframe/internal/application/cookiejar"
|
|
"likedream-goframe/internal/application/cpdaily/cryptoutil"
|
|
"likedream-goframe/internal/application/cpdaily/school"
|
|
"likedream-goframe/internal/application/httpclient"
|
|
"likedream-goframe/internal/common/errors"
|
|
"likedream-goframe/internal/utils"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
|
"github.com/samber/oops"
|
|
"resty.dev/v3"
|
|
)
|
|
|
|
// 南宁师范大学登录实现
|
|
type CpdailyNnnuLogin struct {
|
|
client *resty.Client
|
|
}
|
|
|
|
func (c CpdailyNnnuLogin) Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) {
|
|
jar := cookiejar.NewPersistCookieJar()
|
|
|
|
c.client.SetCookieJar(jar).SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
|
|
|
|
// 获取合法的登录URL
|
|
loginUrl := getCasValidLoginUrl(schoolDetail)
|
|
|
|
// 请求登录页面并获取真正的登录URL
|
|
loginPageResponse, err := c.client.R().
|
|
WithContext(ctx).
|
|
Get(loginUrl)
|
|
|
|
defer loginPageResponse.Body.Close()
|
|
loginPageBytes, err := io.ReadAll(loginPageResponse.Body)
|
|
utils.OkOrPanic(err)
|
|
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
|
|
// 关闭client的重定向
|
|
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
|
|
// 解析登录页面
|
|
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
|
|
utils.OkOrPanic(err)
|
|
// 判断登录类型
|
|
var (
|
|
allInputs = map[string]string{}
|
|
form = loginPage.Find("form").First()
|
|
)
|
|
inputs := form.Find("input")
|
|
|
|
for _, input := range inputs.EachIter() {
|
|
name, exists := input.Attr("name")
|
|
if !exists {
|
|
continue
|
|
}
|
|
value, _ := input.Attr("value")
|
|
allInputs[name] = value
|
|
}
|
|
|
|
allInputs["username"] = username
|
|
|
|
// 处理密码
|
|
allInputs["password"] = cryptoutil.MD5(password)
|
|
|
|
// 解决常规图片验证码
|
|
captchaUrl := getAhjzuCaptchaUrl(loginResponseUrl)
|
|
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
|
|
|
|
allInputs["validateCode"] = captchaResult
|
|
|
|
// 登录提交
|
|
loginPostResponse, err := c.client.R().
|
|
SetFormData(allInputs).
|
|
WithContext(ctx).
|
|
Post(loginResponseUrl.String())
|
|
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.
|
|
WithContext(ctx).
|
|
With("loginResponseUrl", loginResponseUrl).
|
|
With("allInputs", allInputs).
|
|
Wrap(err)
|
|
})
|
|
|
|
defer loginPostResponse.Body.Close()
|
|
|
|
switch loginPostResponse.StatusCode() {
|
|
case 302:
|
|
handleCasLoginSuccess(ctx, loginPostResponse, schoolDetail, username, password, c.client)
|
|
case 200:
|
|
fallthrough
|
|
case 401:
|
|
doc, err := goquery.NewDocumentFromReader(loginPostResponse.Body)
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.
|
|
WithContext(ctx).
|
|
With("loginResponseUrl", loginResponseUrl).
|
|
With("allInputs", allInputs).
|
|
Wrap(err)
|
|
})
|
|
|
|
if isCasNeedKickOut(doc) {
|
|
kickoutResponse := submitCasKickOut(ctx, loginResponseUrl.String(), doc, c.client)
|
|
handleCasLoginSuccess(ctx, kickoutResponse, schoolDetail, username, password, c.client)
|
|
return
|
|
}
|
|
|
|
message := getCasErrorMessage(doc)
|
|
|
|
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("今日校园登录失败: %s", message)), func(err error) error {
|
|
return oops.
|
|
WithContext(ctx).
|
|
With("loginResponseUrl", loginResponseUrl).
|
|
With("allInputs", allInputs).
|
|
With("errorMessage", message).
|
|
Wrap(errors.NewBusinessError(503, message))
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func getNuuuCaptchaUrl(url *url.URL) string {
|
|
return fmt.Sprintf("%s://%s/sso/code/code.jsp", url.Scheme, url.Host)
|
|
}
|
|
|
|
func (c CpdailyNnnuLogin) GetCookieJar() http.CookieJar {
|
|
return c.client.CookieJar()
|
|
}
|
|
|
|
func (c CpdailyNnnuLogin) GetCookieString() string {
|
|
cookieJar := c.GetCookieJar()
|
|
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
|
|
return serializableJar.Serialize()
|
|
}
|
|
|
|
func (c CpdailyNnnuLogin) GetClient() *resty.Client {
|
|
return c.client
|
|
}
|
|
|
|
func NewNnnu(ctx context.Context) CpdailyLogin {
|
|
return &CpdailyNnnuLogin{
|
|
client: httpclient.NewCpdailyClient(),
|
|
}
|
|
}
|