181 lines
5.9 KiB
Go
181 lines
5.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"
|
|
)
|
|
|
|
var RSA_KEY_MAP = map[string]string{
|
|
"public_exponent": "010001",
|
|
"private_exponent": "413798867d69babed22e0dd3d4031c635f3e9dbca0fa50a32974a0e230787b7f7ba78caefbee828a051c690357a8cc31dba8efc738b4db22e887571ef1ec5a5a55b6d866f6a67527f6a7d78a127c9f687008bb540228b50aa2d1ca5a4ff71107234f936b611ac46432a26da9c302eaa7180820df70593353b3f8c0247fe97a45",
|
|
"modulus": "00b5eeb166e069920e80bebd1fea4829d3d1f3216f2aabe79b6c47a3c18dcee5fd22c2e7ac519cab59198ece036dcf289ea8201e2a0b9ded307f8fb704136eaeb670286f5ad44e691005ba9ea5af04ada5367cd724b5a26fdb5120cc95b6431604bd219c6b7d83a6f8f24b43918ea988a76f93c333aa5a20991493d4eb1117e7b1",
|
|
}
|
|
|
|
// 淮南联合大学登录实现 RSA加密 & 验证码必出(加法验证码)
|
|
type CpdailyHnuuLogin struct {
|
|
client *resty.Client
|
|
}
|
|
|
|
func (c CpdailyHnuuLogin) 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)
|
|
|
|
utils.OkOrPanic(err)
|
|
|
|
defer loginPageResponse.Body.Close()
|
|
loginPageBytes, err := io.ReadAll(loginPageResponse.Body)
|
|
utils.OkOrPanic(err)
|
|
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
|
|
// 解析登录页面
|
|
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
|
|
utils.OkOrPanic(err)
|
|
// 判断登录类型
|
|
var (
|
|
allInputs = map[string]string{}
|
|
formNode = loginPage.Find(".ant-form").First()
|
|
)
|
|
inputs := formNode.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
|
|
pair := cryptoutil.NewNutRSAKeyPair(RSA_KEY_MAP["public_exponent"], "", RSA_KEY_MAP["modulus"])
|
|
// 处理密码
|
|
allInputs["password"] = pair.EncryptedString(password)
|
|
|
|
// 解决验证码
|
|
// 解决常规图片验证码
|
|
captchaUrl := getHnuuCaptchaUrl(loginResponseUrl)
|
|
code, uid := ResolveCalcCaptcha(ctx, captchaUrl, c.client)
|
|
|
|
allInputs["code"] = code
|
|
allInputs["id"] = uid
|
|
jar = cookiejar.NewPersistCookieJar()
|
|
c.client.SetCookieJar(jar)
|
|
// 登录提交
|
|
loginPostResponse, err := c.client.R().
|
|
SetFormData(allInputs).
|
|
WithContext(ctx).
|
|
Post(getHnuuLoginPostUrl(loginResponseUrl))
|
|
utils.OkOrPanic(err, func(err error) error {
|
|
return oops.
|
|
WithContext(ctx).
|
|
With("loginResponseUrl", loginResponseUrl).
|
|
With("allInputs", allInputs).
|
|
Wrap(err)
|
|
})
|
|
defer loginPostResponse.Body.Close()
|
|
|
|
type LoginResponse struct {
|
|
Meta struct {
|
|
Success bool `json:"success"`
|
|
StatusCode int `json:"statusCode"`
|
|
Message string `json:"message"`
|
|
} `json:"meta"`
|
|
Data struct {
|
|
Code string `json:"code"`
|
|
} `json:"data"`
|
|
Tgt string `json:"tgt"`
|
|
Ticket string `json:"ticket"`
|
|
}
|
|
json := utils.ReadJson[LoginResponse](loginPostResponse.Body)
|
|
if json.Tgt != "" {
|
|
handleHnuuLoginSuccess(ctx, c.client, loginResponseUrl.Query().Get("service"), json.Ticket, json.Tgt, schoolDetail.WecloudUrl)
|
|
} else {
|
|
errMsg := ""
|
|
if json.Data.Code == "CODEFALSE" {
|
|
errMsg = "验证码错误"
|
|
} else if json.Data.Code == "FALSE" {
|
|
errMsg = "请检查账号密码是否正确"
|
|
} else if json.Data.Code != "PASSERROR" {
|
|
errMsg = "密码错误"
|
|
} else if json.Data.Code != "NOUSER" {
|
|
errMsg = "账号不存在"
|
|
} else if json.Data.Code != "USERLOCK" {
|
|
errMsg = "账号锁定"
|
|
} else {
|
|
errMsg = "未知错误" + json.Data.Code
|
|
}
|
|
utils.OkOrPanic(errors.NewBusinessError(500, errMsg))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func handleHnuuLoginSuccess(ctx context.Context, client *resty.Client, url, ticket string, tgt string, host string) {
|
|
redirectUrl := fmt.Sprintf("%s&ticket=%s", url, ticket)
|
|
client.R().SetHeader("Referer", "https://cas.hnuu.edu.cn/").Get(redirectUrl)
|
|
response, err2 := client.R().SetHeader("Referer", "https://cas.hnuu.edu.cn/").Get("https://hnuu.campusphere.net/iap/login")
|
|
utils.OkOrPanic(err2)
|
|
newUrl := response.RawResponse.Request.URL
|
|
ticketUrl := fmt.Sprintf("https://cas.hnuu.edu.cn/lyuapServer/v1/tickets/%s", tgt)
|
|
get, err := client.R().SetFormData(map[string]string{
|
|
"service": url,
|
|
"loginToken": "loginToken",
|
|
}).Post(ticketUrl)
|
|
utils.OkOrPanic(err)
|
|
defer get.Body.Close()
|
|
ticket = utils.ReadString(get.Body)
|
|
redirectUrl = fmt.Sprintf("%s&ticket=%s", newUrl.Query().Get("service"), ticket)
|
|
client.R().SetHeader("Referer", "https://cas.hnuu.edu.cn/").Get(redirectUrl)
|
|
ActivateSession(ctx, host, client)
|
|
}
|
|
|
|
func getHnuuLoginPostUrl(url *url.URL) string {
|
|
// https://cas.hnuu.edu.cn/lyuapServer/v1/tickets
|
|
return fmt.Sprintf("%s://%s/lyuapServer/v1/tickets", url.Scheme, url.Host)
|
|
}
|
|
|
|
func getHnuuCaptchaUrl(url *url.URL) string {
|
|
// https://cas.hnuu.edu.cn/lyuapServer/kaptcha?_t=1766803822&uid=
|
|
return fmt.Sprintf("%s://%s/lyuapServer/kaptcha?_t=%d&uid=", url.Scheme, url.Host, utils.GetCurrentTimeMillis())
|
|
}
|
|
|
|
func (c CpdailyHnuuLogin) GetCookieJar() http.CookieJar {
|
|
return c.client.CookieJar()
|
|
}
|
|
|
|
func (c CpdailyHnuuLogin) GetCookieString() string {
|
|
cookieJar := c.GetCookieJar()
|
|
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
|
|
return serializableJar.Serialize()
|
|
}
|
|
|
|
func (c CpdailyHnuuLogin) GetClient() *resty.Client {
|
|
return c.client
|
|
}
|
|
|
|
func NewHnuu(ctx context.Context) CpdailyLogin {
|
|
return &CpdailyHnuuLogin{
|
|
client: httpclient.NewCpdailyClient(),
|
|
}
|
|
}
|