package login import ( "context" "crypto/tls" "fmt" "git.listensoft.net/tool/jspkit/common" "git.listensoft.net/tool/jspkit/common/lxhttp" "git.listensoft.net/tool/jspkit/common/models" "git.listensoft.net/tool/jspkit/logger" "git.listensoft.net/tool/jspkit/taxerr" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/tidwall/gjson" "net/http" "net/url" "strings" "time" ) // MakeLoginData 生成登录信息 func MakeLoginData(ctx context.Context, info models.CompanyInfo) (jsonData map[string]interface{}) { loginMethod := "QRCODE" TaxNo := info.TaxNo if info.Dlfs == "代理登录" { loginMethod = "MOBILE" } website := "etax" if ctxValue := ctx.Value("website"); ctxValue != nil { if websiteStr, ok := ctxValue.(string); ok { website = websiteStr } } // 拆分登陆方式 switch info.Area { case "hubei", "guangdong", "zhejiang", "tianjin": if info.Dlfs == "新版登录" { loginMethod = "ACCOUNT" } if info.Dlfs == "代理登录" && (info.Area == "guangdong" || info.Area == "zhejiang" || info.Area == "tianjin") { loginMethod = "ACCOUNT" } default: if info.Dlfs == "新版登录" { loginMethod = "QRCODE" } } changeCreditCode := "" loginType := "QIYE" if info.Dlfs == "代理登录" { changeCreditCode = info.TaxNo info.TaxNo = info.IdNo loginType = "DAILI" TaxNo = info.IdNo } jsonData = map[string]interface{}{ "userInfo": map[string]string{ "ComName": info.ComName, "Name": info.Area + ".pro", "CreditCode": TaxNo, "Account": info.Tel, "Password": info.Zzrmm, "dailiCreditCode": "", "changeCreditCode": changeCreditCode, }, "areaName": info.Area, "loginMethod": loginMethod, "loginType": loginType, "website": website, "source": "taxRobot", //"proxies": proxies, } return } // PostLoin 登录函数 func PostLoin(jsonR []byte, headder map[string]string, info models.CompanyInfo) (response []byte, err error) { f := 0 //begin: //logger.Info(string(jsonR)) c := lxhttp.NewHttpClient() //http://dppt.jsptax.com:81 if info.Dlfs == "代理登录" { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //忽略证书校验 } c = &http.Client{ Timeout: time.Duration(400 * time.Second), Transport: tr, } } response, err = lxhttp.PostByteReader(c, ETaxServe+"/api/login", jsonR, headder, nil) if err != nil { logger.Error(err.Error()) if f < 6 { utils.Sleep(5) f++ //goto begin } return response, taxerr.NewWebStuckTitle(info.Cscsts) } if strings.Contains(string(response), "Sorry, the page you are looking for is currently unavailable") || strings.Contains(string(response), "二维码已失效") || strings.Contains(string(response), "ReadTimeoutError") || strings.Contains(string(response), "ProtocolError") { if f <= 3 { f++ //goto begin } return nil, taxerr.New("新版税局登陆超时") } // 登录繁忙等30s if strings.Contains(string(response), "税局服务繁忙,请5分钟后重试") { utils.Sleep(30) if f <= 3 { f++ //goto begin } } if (gjson.GetBytes(response, "msg").String() != "" && gjson.GetBytes(response, "msg").String() != "[登录成功~]") || (gjson.GetBytes(response, "status").String() != "ok") { Str := gjson.GetBytes(response, "error").String() if Str == "" { // {"status":"false","msg":"[切换身份失败, 不存在用户91370203MA3RCTAU0X~]"} // {"status":"false","msg":"[切换身份失败, 连续认证错误次数过多,您的账号已被锁定。建议您直接使用“忘记密码”修改密码后重新登录或等待次日零时自动解锁。~]"} Str = gjson.GetBytes(response, "msg").String() } if Str == "" { Str = "新版税局登陆超时2" } if strings.Contains(Str, `请调用发送验证码`) && info.Dlfs == "新版登录" { common.DeleteTpassCookie(info) } if strings.Contains(Str, `请调用发送验证码`) && info.Dlfs == "代理登录" { err = taxerr.NewUserV3(`代理登录已掉线`, "请保证设备或app在线后重新发起任务") return } // 税局登录失败['bool' object is not subscriptable] 这个错误直接返回连续认证错误次数过多 if strings.Contains(Str, `税局登录失败['bool' object is not subscriptable]`) { err = taxerr.FormatLoginError("税局登录失败[连续认证错误次数过多,您的账号已被锁定。建议您直接使用“忘记密码”修改密码后重新登录或等待次日零时自动解锁。]") return } err = taxerr.FormatLoginError(Str) return } return } func RsCookie(url string) (ck []*http.Cookie, uri string) { return nil, url if lxhttp.NeedRsvmp(url) { ck, _ = lxhttp.Rsvmp(url) return ck, url } return nil, url } // RefreshCookieBase 刷新增值税cookie和财报cookie func refreshCookieBase(client *http.Client, area, BaseIndex, api, new_key16, token string) { f := 0 begin: headers := map[string]string{ "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "zh-CN,zh;q=0.9", "Accept": "application/json, text/plain, */*", "Content-Type": "application/json", "deviceIdentyNo": "740qhjjFG7xSNQoCFtSHA7AMzikTKeJ5", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "X-TICKET-ID": "null", "X-NATURE-IP": "", "X-SM4-INFO": "0", "X-TEMP-INFO": "null", "hUid": "", "X-APP-CLIENTID": "", "X-LANG-ID": "null", "Authorization": token, } logger.Info("埋点1") rst := lxhttp.Get_etax_clientId_redirectUri(client, api, RsCookie) logger.Info("埋点2") if rst == nil { return } logger.Info("埋点3") client_id := rst["client_id"].(string) redirect_uri := rst["redirect_uri"].(string) redirect_uri, _ = url.QueryUnescape(redirect_uri) code := lxhttp.VerifyLogin(client, area, BaseIndex, client_id, redirect_uri, new_key16, headers, RsCookie) logger.Info("埋点4") queryParams := url.Values{} queryParams.Add("code", code) if !strings.Contains(api, "invoice-query/invoice-query") { queryParams.Add("state", rst["state"].(string)) } queryParams.Add("response_type", rst["response_type"].(string)) hasQueryParams := strings.Contains(redirect_uri, "?") var finalRedirectURI string if hasQueryParams { finalRedirectURI = fmt.Sprintf("%s&%s", redirect_uri, queryParams.Encode()) } else { finalRedirectURI = fmt.Sprintf("%s?%s", redirect_uri, queryParams.Encode()) } fmt.Println("刷新cookie-uri:", finalRedirectURI) res, err := lxhttp.GET(client, finalRedirectURI, map[string]string{}, headers) if err != nil { _ = rod.Try(func() { logger.Info("finalRedirectURI失败:" + string(res)) logger.Info("finalRedirectURI失败:" + err.Error()) }) if strings.Contains(BaseIndex, "neimeng") && f < 7 { f++ goto begin } } fmt.Println(string(res)) return } // RefreshCookieForAfterLogin RefreshCookie 主函数-对外开放 可以刷新两个cookie func RefreshCookieForAfterLogin(cookies map[string]string, area, BaseIndex, new_key16, token string, cwbb bool) (client *http.Client) { client = lxhttp.NewHttpClientForNoRedirects("", "") var cks []*http.Cookie for k, v := range cookies { cks = append(cks, &http.Cookie{ Name: k, Value: v, }) } u, _ := url.Parse(BaseIndex) client.Jar.SetCookies(u, cks) uri := BaseIndex + `/szc/szzh/sbss/ssmx/public/v1/checkLoginState` if !cwbb { uri = BaseIndex + "/szc/szzh/sjswszzh/sy/redirect?pageName=zhgl" } err := rod.Try(func() { refreshCookieBase(client, area, BaseIndex, uri, new_key16, token) }) if err != nil { logger.Info(err.Error()) return nil } return client } // RefreshCookieForAfterLoginRod rod使用替代afterlogin func RefreshCookieForAfterLoginRod(page *rod.Page, proxy, proxyAuth, area, BaseIndex string, cwbb bool) (client *http.Client, err error) { Tpassuri := fmt.Sprintf(`https://tpass.%s.chinatax.gov.cn:8443/#/identitySwitch/enterprise`, area) client = lxhttp.NewHttpClientForNoRedirects(proxy, proxyAuth) cookies := page.Browser().MustGetCookies() var cookiesArr []*http.Cookie for _, v := range cookies { var cookie http.Cookie cookie.Name = v.Name cookie.Value = v.Value cookie.Path = v.Path cookie.Domain = v.Domain cookiesArr = append(cookiesArr, &cookie) } u, _ := url.Parse(BaseIndex) client.Jar.SetCookies(u, cookiesArr) new_key16, token, err := GetTpassToken(page, Tpassuri) if err != nil { return } logger.Info("GetTpassToken完成") Api := BaseIndex + `/szc/szzh/sbss/ssmx/public/v1/checkLoginState` if !cwbb { Api = BaseIndex + "/szc/szzh/sjswszzh/sy/redirect?pageName=zhgl" } if area == "shanxi" { Api = `https://etax.shanxi.chinatax.gov.cn:8443/szc/szzh/sjswszzh/spHandler?cdlj=/szzh/szzh/` } err = rod.Try(func() { logger.Info("开始refreshCookieBase") refreshCookieBase(client, area, BaseIndex, Api, new_key16, token) logger.Info("结束refreshCookieBase") }) if err != nil { logger.Info(err.Error()) } return client, err } // RefreshCookieForAfterLogin RefreshCookie 主函数-对外开放 可以刷新两个cookie func RefreshDppt(page *rod.Page, area string) { e := rod.Try(func() { BaseIndex := GetBaseUri(area) client := lxhttp.NewHttpClientForNoRedirects("", "") { var cks []*http.Cookie for _, v := range page.Browser().MustGetCookies() { cks = append(cks, &http.Cookie{ Name: v.Name, Value: v.Value, }) } u, _ := url.Parse(strings.ReplaceAll(BaseIndex, "etax", "dppt")) client.Jar.SetCookies(u, cks) u, _ = url.Parse(BaseIndex) client.Jar.SetCookies(u, cks) } TpassUri := fmt.Sprintf(`https://tpass.%s.chinatax.gov.cn:8443/#/identitySwitch/enterprise`, area) new_key16, token, _ := GetTpassToken(page, TpassUri) if new_key16 == "" { return } uri := strings.ReplaceAll(GetBaseUri(area), "etax", "dppt") + "/szzhzz/spHandler?cdlj=third-menu/invoice-query/invoice-query" refreshCookieBase(client, area, BaseIndex, uri, new_key16, token) var rodCookies []*proto.NetworkCookieParam u, _ := url.Parse(BaseIndex) for _, v := range client.Jar.Cookies(u) { rodCookies = append(rodCookies, &proto.NetworkCookieParam{ Name: v.Name, Value: v.Value, Domain: ".chinatax.gov.cn", Path: "/", }, ) } logger.Info("xxxx4") _ = page.Browser().SetCookies(rodCookies) }) if e != nil { logger.Info(e.Error()) } return } func GetTpassToken(page *rod.Page, TpassUrl string) (string, string, error) { var new_key16, token string err := rod.Try(func() { home := page.MustInfo().URL logger.Info(home) cookies := page.Browser().MustGetCookies() Ck := map[string]string{} for _, cookie := range cookies { Ck[cookie.Name] = cookie.Value } // 避免进不去 for range "..." { page.MustNavigate(TpassUrl) utils.Sleep(2) // 加校验 //span[text()="身份切换"] page.MustWaitLoad() if common.MustHasXV(page, `//span[text()="身份切换"]`) { break } else { utils.Sleep(1.5) } } if !common.MustHasXV(page, `//span[text()="身份切换"]`) { panic(taxerr.New("获取企业所得税超时,系统将于30分钟后重试(可在\"通用设置\"关闭)")) } _ = rod.Try(func() { new_key16 = page.MustEval(`()=>localStorage.getItem('new_key16')`).String() }) _ = rod.Try(func() { token = page.MustEval(`()=>localStorage.getItem('token')`).String() }) logger.Info("New_key16:" + new_key16) logger.Info("Token:" + token) page.Navigate(home) }) if new_key16 == "" { err = taxerr.NewWebStuckTitle(common.InfoCscsts) } return new_key16, token, err }