diff --git a/internal/application/cookiejar/jar.go b/internal/application/cookiejar/jar.go index 01bf17c..9481ffd 100644 --- a/internal/application/cookiejar/jar.go +++ b/internal/application/cookiejar/jar.go @@ -48,22 +48,8 @@ func (p *PersistCookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) { delete(globalCookies, cookie.Name) } - // 查找是否存在相同 name, path 和 domain 的 cookie - existingCookies := p.hostMap[host][cookie.Name] - found := false - for i, existing := range existingCookies { - if existing.Path == cookie.Path && existing.Domain == cookie.Domain { - // 找到完全匹配的 cookie,替换它 - existingCookies[i] = cookie - found = true - break - } - } - - if !found { - // 没有找到匹配的,添加新的 cookie - p.hostMap[host][cookie.Name] = append(p.hostMap[host][cookie.Name], cookie) - } + // 同一 host 下同名 cookie 直接覆盖,避免多个同名 cookie 导致旧值被选中 + p.hostMap[host][cookie.Name] = []*http.Cookie{cookie} } } diff --git a/internal/application/cpdaily/login/sms.go b/internal/application/cpdaily/login/sms.go index ebf7718..231e827 100644 --- a/internal/application/cpdaily/login/sms.go +++ b/internal/application/cpdaily/login/sms.go @@ -15,6 +15,7 @@ import ( "likedream-goframe/internal/utils" "net/http" "net/url" + "strings" "github.com/bytedance/sonic" "github.com/samber/oops" @@ -284,25 +285,88 @@ func (s *SmsLoginHandler) GetCookieString() string { // RefreshByTgc 使用保存的完整 cookie(包含 CASTGC 等长期凭证)重新去学校换取 CAS cookie。 // tgc 参数是 SetLoginCookie 之后序列化的完整 cookie jar,包含 CASTGC(有效期10年)、 -// sessionToken 等认证信息,可以直接用来重新激活 session。 +// sessionToken 等认证信息。流程:先用 sessionToken 调用 getImUserToken 换取新的 CAS cookie, +// 再激活 session。 func (s *SmsLoginHandler) RefreshByTgc(ctx context.Context, tgc string, schoolId string) (cookie string, newTgc string) { jar := cookiejar.NewPersistCookieJar() jar.Deserialize(tgc) + // 合并所有 host 的 cookie 到 global,消除因多 host 存储导致的重复 cookie + jar.RemoveCookieInfo() s.client = httpclient.NewCpdailyClient() s.client.SetCookieJar(jar) detail := school.GetSchoolDetail(ctx, schoolId) - logger.Logger.Info("[TGC] RefreshByTgc 开始用CASTGC重新激活session", + logger.Logger.Info("[TGC] RefreshByTgc 开始用TGC换取CAS cookie", zap.String("schoolId", schoolId), zap.String("wecloudUrl", detail.WecloudUrl), zap.Int("tgcLen", len(tgc)), ) - // 用保存的 cookie(含 CASTGC)重新激活 session - ActivateSession(ctx, detail.WecloudUrl, s.client) + // 从 cookie jar 中提取 sessionToken,用于换取新的 CAS cookie + schoolUrl, _ := url.Parse(detail.WecloudUrl) + sessionToken := "" + for _, c := range jar.Cookies(schoolUrl) { + if c.Name == "sessionToken" { + sessionToken = c.Value + break + } + } - logger.Logger.Info("[TGC] RefreshByTgc ActivateSession完成") + if sessionToken != "" { + // 换取新的 CAS cookie(与 SetLoginCookie 相同的流程) + getCookieUrl := fmt.Sprintf("%s%s", detail.WecloudUrl, "/wec-im-group/group/imUserToken/getImUserToken") + + ampCookieMap := map[string]any{ + "AMP2": []map[string]any{ + {"path": "/", "value": sessionToken, "domain": "", "name": "sessionToken"}, + }, + "AMP1": []map[string]any{ + {"path": "/", "value": sessionToken, "domain": "", "name": "sessionToken"}, + }, + } + + ampTokenString, e := sonic.Marshal(ampCookieMap) + utils.OkOrPanic(e, oops.WithContext(ctx).Wrap) + + ampTokenEncrypted := cryptoutil.DesEncrypt(ampTokenString, cryptoutil.Normal) + ampToken := base64.StdEncoding.EncodeToString(ampTokenEncrypted) + + ctx = utils.NewCpdailySmsContextBuilder(). + WithDeviceID(s.deviceID). + WithTenantID(detail.Id). + WithSessionToken(sessionToken). + Build(ctx) + + // 使用不复制 Cookie 头的重定向策略,避免旧 Cookie 覆盖 jar 中更新的值 + s.client.SetRedirectPolicy(resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { + if len(via) >= 50 { + return fmt.Errorf("resty: stopped after 50 redirects") + } + // 只复制非 Cookie 头,让 jar 自动管理 Cookie + for key, val := range via[0].Header { + if !strings.EqualFold(key, "Cookie") { + req.Header[key] = val + } + } + return nil + })) + req := s.client.R(). + SetHeader("AmpCookies", ampToken). + SetContext(ctx) + + setSmsRequestHeaders(ctx, req) + + resp, e := req.Get(getCookieUrl) + utils.OkOrPanic(e, oops.WithContext(ctx).Wrap) + + respJson := utils.ReadJson[cpdailySmsCommonResponse[any]](resp.Body) + respJson.EnsureSuccess() + + logger.Logger.Info("[TGC] RefreshByTgc 换取CAS cookie完成") + } else { + logger.Logger.Warn("[TGC] RefreshByTgc 未找到sessionToken,跳过换取CAS cookie步骤") + } newTgc = tgc // TGC 保持不变(CASTGC 有效期10年) cookie = s.GetCookieString()