ckLogin.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package login
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "log"
  8. "strings"
  9. "time"
  10. "git.listensoft.net/tool/jspkit/common"
  11. "git.listensoft.net/tool/jspkit/common/models"
  12. "git.listensoft.net/tool/jspkit/logger"
  13. "git.listensoft.net/tool/jspkit/taxerr"
  14. "github.com/forgoer/openssl"
  15. "github.com/go-rod/rod"
  16. "github.com/go-rod/rod/lib/proto"
  17. "github.com/go-rod/rod/lib/utils"
  18. "github.com/tidwall/gjson"
  19. )
  20. func CheckSessionAndLogin(ctx context.Context, p *rod.Page, BaseUri string, info models.CompanyInfo) (err error) {
  21. //return taxerr.NewUser("税局代理机构登录存在风险,预计6号恢复,请等待税局修复后使用")//12.6 09:20左右 确认完了
  22. if info.Dlfs == "代理登录" && info.IdNo == "" {
  23. return taxerr.DlTaxNoError
  24. }
  25. return AutoCheckSessionAndLogin(ctx, p, BaseUri, info)
  26. }
  27. func LoadCookie(sessionInfos models.SsoSession, p *rod.Page, BaseUri string, info models.CompanyInfo) (name string, err error) {
  28. err = rod.Try(func() {
  29. defer AddErr()
  30. p.Navigate("")
  31. _ = p.Timeout(common.ClickTimeOut).MustNavigate(areaURLmap[info.Area+".pro"]).StopLoading() // 此时必须得去tpass域名 要不然写入cookie后进不去发票
  32. p.MustSetCookies(sessionInfos.Cookies...)
  33. {
  34. p.MustEval(`() => localStorage.clear()`)
  35. utils.Sleep(1)
  36. // 设置 localStorage 在第二个页面中,逐条设置
  37. logger.Info("Setting localStorage in second browser...")
  38. for key, value := range sessionInfos.LocalStorage {
  39. if key == "logConfig" {
  40. continue
  41. }
  42. script := fmt.Sprintf(`() => localStorage.setItem('%s', '%s')`, key, value)
  43. _, err := p.Eval(script)
  44. if err != nil {
  45. log.Printf("Error setting localStorage %s=%s: %v", key, value, err)
  46. }
  47. }
  48. }
  49. utils.Sleep(1)
  50. if info.Area == "shaanxi" {
  51. //陕西地区必须带后面/才能登录上去
  52. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb/")
  53. } else {
  54. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb")
  55. }
  56. utils.Sleep(4)
  57. _ = rod.Try(func() {
  58. p.Timeout(common.LoadTimeout * 2).MustWaitStable()
  59. p.Timeout(common.LoadTimeout * 2).MustWaitDOMStable()
  60. })
  61. //p.MustStopLoading()
  62. logger.Info("加载完成")
  63. if info.Area == "hebei" && !common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) {
  64. logger.Info("河北重新访问loginb")
  65. p.Timeout(common.ClickTimeOut).MustNavigate(`https://tpass.hebei.chinatax.gov.cn:8443/#/login?redirect_uri=https%3A%2F%2Fetax.hebei.chinatax.gov.cn%3A8443%2Fmhzx%2Fapi%2Fmh%2Ftpass%2Fcode&client_id=ap5bfdavp66a49979apa5848fpaa5353&response_type=code&state=8f8f3db7ecb948f08b1da177cda40854&client_pid=ap5bfdavp66a49979apa5848fpaa5353`)
  66. utils.Sleep(4)
  67. p.MustWaitStable()
  68. p.MustWaitDOMStable()
  69. }
  70. if common.MustHasXV(p, "//div[text()=' 关于电子税务局“新办智能开业”的通知 ']") {
  71. rod.Try(func() {
  72. p.Timeout(common.ClickTimeOut).MustSearch("//span[text()='我知道了']").MustClick()
  73. utils.Sleep(1)
  74. common.MustElementXV(p, "//span[text()='确认']").MustClick()
  75. utils.Sleep(40) //要转很久
  76. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb")
  77. })
  78. }
  79. if p.MustHasX(`//div[@class='leftMain']//div[contains(@class,'title')]`) {
  80. _ = p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).WaitStable(300 * time.Millisecond)
  81. if *p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).MustAttribute(`title`) == "" {
  82. logger.Info("公司名空刷新")
  83. p.Reload()
  84. utils.Sleep(4)
  85. p.MustWaitDOMStable()
  86. }
  87. }
  88. if common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) {
  89. name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`)
  90. if name == "" {
  91. err = taxerr.New("代理登录切换公司后异常公司名")
  92. return
  93. }
  94. return
  95. }
  96. DlSy := false //登录完是代理登录的首页
  97. if common.MustHasXV(p, `//a[@title="单户办理"]`) {
  98. common.MustElementX(p, `//a[@title="单户办理"]`).MustClick()
  99. DlSy = true
  100. utils.Sleep(3)
  101. }
  102. if !strings.Contains(p.MustInfo().URL, "tpass") {
  103. panic(taxerr.New("cookie失效"))
  104. }
  105. if !p.MustHasR(`[class="s_Breadcrumb"]`, "企业办税") {
  106. panic(taxerr.New("cookie失效"))
  107. }
  108. if !DlSy {
  109. r := p.MustWaitRequestIdle()
  110. p.Timeout(common.ClickTimeOut).MustSearch("返回").MustClick()
  111. r()
  112. } else {
  113. //这里需要手动切换到首页
  114. common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, info.TaxNo)
  115. common.MustElementX(p, `//span[text()="查询"]`).MustClick()
  116. utils.Sleep(2)
  117. tbody := common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`)
  118. if len(tbody) == 0 {
  119. common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, "")
  120. p.Timeout(common.ClickTimeOut).MustElementX(`//input[@placeholder="请输入纳税人名称"]`).MustInput(info.ComName)
  121. common.MustElementX(p, `//span[text()="查询"]`).MustClick()
  122. utils.Sleep(2)
  123. tbody = common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`)
  124. if len(tbody) == 0 {
  125. panic(taxerr.NewUserV3("未在当前账号下["+info.Zzridno+"]找到该企业", "请检查手机号与企业的关联关系"))
  126. }
  127. panic(taxerr.NewUserV3("该公司账号跟公司名称不匹配", "请检查后重新发起"))
  128. } else if len(tbody) == 1 {
  129. common.MustElementX(p, `//span[text()='进入']`).MustClick()
  130. if common.MustHasXV(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`) {
  131. common.MustElementX(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`).MustClick()
  132. time.Sleep(5 * time.Second)
  133. }
  134. } else {
  135. panic(taxerr.NewSystemV3("未在当前税号下["+info.TaxNo+"]找到多家企业", "请联系运维处理"))
  136. }
  137. }
  138. utils.Sleep(2)
  139. p.MustWaitDOMStable()
  140. name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`)
  141. })
  142. // 这是panic的失效
  143. if err != nil {
  144. rod.Try(func() {
  145. patjh := common.GeneratePath("png")
  146. p.MustScreenshot(patjh)
  147. logger.Info("代理登录切换公司后异常公司名或判定为失效截图:" + patjh)
  148. })
  149. logger.Info(err.Error())
  150. return "", err
  151. }
  152. if (err != nil) && name == "" {
  153. logger.Info("cookie失效,当前url:" + p.MustInfo().URL)
  154. return "", taxerr.NewSystemV3("税局加载异常", "系统将于30分钟后重试")
  155. }
  156. return
  157. }
  158. func LoadCookieFast(sessionInfos models.SsoSession, p *rod.Page, BaseUri string, info models.CompanyInfo) (name string, err error) {
  159. err = rod.Try(func() {
  160. defer AddErr()
  161. p.Navigate("")
  162. p.Timeout(common.ClickTimeOut).MustNavigate(areaURLmap[info.Area+".pro"]).MustStopLoading() // 此时必须得去tpass域名 要不然写入cookie后进不去发票
  163. p.MustSetCookies(sessionInfos.Cookies...)
  164. {
  165. p.MustEval(`() => localStorage.clear()`)
  166. //utils.Sleep(1)
  167. // 设置 localStorage 在第二个页面中,逐条设置
  168. logger.Info("Setting localStorage in second browser...")
  169. for key, value := range sessionInfos.LocalStorage {
  170. if key == "logConfig" {
  171. continue
  172. }
  173. script := fmt.Sprintf(`() => localStorage.setItem('%s', '%s')`, key, value)
  174. _, err := p.Eval(script)
  175. if err != nil {
  176. log.Printf("Error setting localStorage %s=%s: %v", key, value, err)
  177. }
  178. }
  179. }
  180. utils.Sleep(0.5)
  181. if info.Area == "shaanxi" {
  182. //陕西地区必须带后面/才能登录上去
  183. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb/")
  184. } else {
  185. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb")
  186. }
  187. utils.Sleep(1)
  188. _ = rod.Try(func() {
  189. p.Timeout(2 * time.Second).MustWaitStable()
  190. p.Timeout(2 * time.Second).MustWaitDOMStable()
  191. })
  192. //p.MustStopLoading()
  193. logger.Info("加载完成")
  194. if info.Area == "hebei" && !common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) {
  195. logger.Info("河北重新访问loginb")
  196. p.Timeout(common.ClickTimeOut).MustNavigate(`https://tpass.hebei.chinatax.gov.cn:8443/#/login?redirect_uri=https%3A%2F%2Fetax.hebei.chinatax.gov.cn%3A8443%2Fmhzx%2Fapi%2Fmh%2Ftpass%2Fcode&client_id=ap5bfdavp66a49979apa5848fpaa5353&response_type=code&state=8f8f3db7ecb948f08b1da177cda40854&client_pid=ap5bfdavp66a49979apa5848fpaa5353`)
  197. utils.Sleep(4)
  198. p.MustWaitStable()
  199. p.MustWaitDOMStable()
  200. }
  201. if common.MustHasXV(p, "//div[text()=' 关于电子税务局“新办智能开业”的通知 ']") {
  202. rod.Try(func() {
  203. p.Timeout(common.ClickTimeOut).MustSearch("//span[text()='我知道了']").MustClick()
  204. utils.Sleep(1)
  205. common.MustElementXV(p, "//span[text()='确认']").MustClick()
  206. utils.Sleep(40) //要转很久
  207. p.Timeout(common.ClickTimeOut).MustNavigate(BaseUri + "/loginb")
  208. })
  209. }
  210. if p.MustHasX(`//div[@class='leftMain']//div[contains(@class,'title')]`) {
  211. _ = p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).WaitStable(300 * time.Millisecond)
  212. if *p.Timeout(common.ClickTimeOut).MustElementX(`//div[@class='leftMain']//div[contains(@class,'title')]`).MustAttribute(`title`) == "" {
  213. logger.Info("公司名空刷新")
  214. p.Reload()
  215. utils.Sleep(4)
  216. p.MustWaitDOMStable()
  217. }
  218. }
  219. if common.MustHasXV(p, `//div[@class='leftMain']//div[contains(@class,'title')]`) {
  220. name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`)
  221. if name == "" {
  222. err = taxerr.New("代理登录切换公司后异常公司名")
  223. return
  224. }
  225. return
  226. }
  227. DlSy := false //登录完是代理登录的首页
  228. if common.MustHasXV(p, `//a[@title="单户办理"]`) {
  229. common.MustElementX(p, `//a[@title="单户办理"]`).MustClick()
  230. DlSy = true
  231. utils.Sleep(3)
  232. }
  233. if !strings.Contains(p.MustInfo().URL, "tpass") {
  234. panic(taxerr.New("cookie失效"))
  235. }
  236. if !p.MustHasR(`[class="s_Breadcrumb"]`, "企业办税") {
  237. panic(taxerr.New("cookie失效"))
  238. }
  239. if !DlSy {
  240. r := p.MustWaitRequestIdle()
  241. p.Timeout(common.ClickTimeOut).MustSearch("返回").MustClick()
  242. r()
  243. } else {
  244. //这里需要手动切换到首页
  245. common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, info.TaxNo)
  246. common.MustElementX(p, `//span[text()="查询"]`).MustClick()
  247. utils.Sleep(2)
  248. tbody := common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`)
  249. if len(tbody) == 0 {
  250. common.MustInputX(p, `//input[@placeholder="请输入统一社会信用代码"]`, "")
  251. p.Timeout(common.ClickTimeOut).MustElementX(`//input[@placeholder="请输入纳税人名称"]`).MustInput(info.ComName)
  252. common.MustElementX(p, `//span[text()="查询"]`).MustClick()
  253. utils.Sleep(2)
  254. tbody = common.MustElementsX(p, `//div[@class="el-table__body-wrapper is-scrolling-none"]//table//tr`)
  255. if len(tbody) == 0 {
  256. panic(taxerr.NewUserV3("未在当前账号下["+info.Zzridno+"]找到该企业", "请检查手机号与企业的关联关系"))
  257. }
  258. panic(taxerr.NewUserV3("该公司账号跟公司名称不匹配", "请检查后重新发起"))
  259. } else if len(tbody) == 1 {
  260. common.MustElementX(p, `//span[text()='进入']`).MustClick()
  261. if common.MustHasXV(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`) {
  262. common.MustElementX(p, `//span[text()="身份切换"]/../../..//span[contains(text(),"确定")]`).MustClick()
  263. time.Sleep(5 * time.Second)
  264. }
  265. } else {
  266. panic(taxerr.NewSystemV3("未在当前税号下["+info.TaxNo+"]找到多家企业", "请联系运维处理"))
  267. }
  268. }
  269. utils.Sleep(0.5)
  270. p.MustWaitDOMStable()
  271. name = *common.WaitElementX(p, `//div[@class='leftMain']//div[contains(@class,'title')]`, 0.5).MustAttribute(`title`)
  272. logger.Info(name)
  273. })
  274. // 这是panic的失效
  275. if err != nil {
  276. rod.Try(func() {
  277. patjh := common.GeneratePath("png")
  278. p.MustScreenshot(patjh)
  279. logger.Info("代理登录切换公司后异常公司名或判定为失效截图:" + patjh)
  280. })
  281. logger.Info(err.Error())
  282. return "", err
  283. }
  284. if (err != nil) && name == "" {
  285. logger.Info("cookie失效,当前url:" + p.MustInfo().URL)
  286. return "", taxerr.NewSystemV3("税局加载异常", "系统将于30分钟后重试")
  287. }
  288. if name == "" {
  289. logger.Info("公司名加载异常,当前url:" + p.MustInfo().URL)
  290. path := common.GeneratePath("png")
  291. p.MustScreenshot(path)
  292. logger.Info(path)
  293. }
  294. return
  295. }
  296. func AddErr() {
  297. if err := recover(); err != nil {
  298. e := err.(error)
  299. if _, ok := err.(*taxerr.UserErr); ok {
  300. err = taxerr.NewUser(e.Error() + "(代理登录失败)")
  301. }
  302. if _, ok := err.(*taxerr.SystemErr); ok {
  303. err = taxerr.New(e.Error() + "(代理登录失败)")
  304. }
  305. panic(err)
  306. }
  307. }
  308. func AutoCheckSessionAndLogin(ctx context.Context, p *rod.Page, BaseUri string, info models.CompanyInfo) (err error) {
  309. if info.Area == "hubei" && info.Dlfs == "代理登录" && len(info.Tel) != 11 {
  310. return taxerr.NewUserV3("湖北代理登录需要接受验证码", "请在登录信息中修改为手机号")
  311. }
  312. if info.Zzrmm == "" {
  313. return taxerr.PasswdEmpty
  314. }
  315. var LoginResponse []byte
  316. var Session models.SsoSession
  317. // 直接登录获取cookie
  318. {
  319. logger.Info("开始代理登陆获取cookie")
  320. LoginData := MakeLoginData(ctx, info)
  321. a, _ := json.Marshal(LoginData)
  322. logger.Info("代理登录加密前参数" + string(a))
  323. R := AesECBEncrypt(LoginData)
  324. headder := map[string]string{
  325. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
  326. "content-type": "application/json",
  327. "x-token": XToken,
  328. }
  329. marshal, _ := json.Marshal(LoginData)
  330. logger.Info(fmt.Sprintf("PostLoin参数%s" + string(marshal)))
  331. LoginResponse, err = PostLoin(R, headder, info)
  332. logger.Info("代理登陆获取cookie成功")
  333. if err != nil {
  334. if info.Dlfs == "代理登录" {
  335. e := err.(error)
  336. if _, ok := err.(*taxerr.UserErr); ok {
  337. err = taxerr.NewUser(e.Error() + "(代理登录失败)")
  338. }
  339. if _, ok := err.(*taxerr.SystemErr); ok {
  340. err = taxerr.New(e.Error() + "(代理登录失败)")
  341. }
  342. }
  343. return err
  344. }
  345. }
  346. // 整理cookie
  347. {
  348. gdata := gjson.GetBytes(LoginResponse, "data")
  349. cookies := []*proto.NetworkCookieParam{}
  350. domain := ""
  351. gdata.Get("cookies").ForEach(func(key, value gjson.Result) bool {
  352. if key.String() == "lzkqow38189" {
  353. domain = "dppt." + info.Area + ".chinatax.gov.cn"
  354. } else if key.String() == "token" {
  355. domain = "tpass." + info.Area + ".chinatax.gov.cn"
  356. } else {
  357. domain = ".chinatax.gov.cn"
  358. }
  359. cookies = append(cookies, &proto.NetworkCookieParam{
  360. Name: key.String(),
  361. Value: value.String(),
  362. Domain: domain,
  363. Path: "/",
  364. })
  365. return true
  366. })
  367. localStorage := map[string]string{}
  368. gdata.Get("localStorage").ForEach(func(key, value gjson.Result) bool {
  369. localStorage[key.String()] = value.String()
  370. return true
  371. })
  372. Session.Cookies = cookies
  373. Session.LocalStorage = localStorage
  374. //dpptURL = gdata.Get("redirectUri").String()
  375. }
  376. //if ctxValue := ctx.Value("website"); ctxValue != nil { //只有发票任务会传website
  377. // // 写入浏览器
  378. // name, err := LoadCookieFast(Session, p, BaseUri, info)
  379. // if err != nil {
  380. // fmt.Println(err.Error())
  381. // return err
  382. // }
  383. // if name != info.ComName {
  384. // return taxerr.CompanyNameError(name)
  385. // }
  386. //} else {
  387. // 写入浏览器
  388. name, err := LoadCookie(Session, p, BaseUri, info)
  389. if err != nil {
  390. return err
  391. }
  392. if name != info.ComName {
  393. return taxerr.CompanyNameError(name)
  394. }
  395. //}
  396. return
  397. }
  398. func AesECBEncrypt(data map[string]any) []byte {
  399. jsonData, _ := json.Marshal(data)
  400. fmt.Println(string(jsonData))
  401. padded := make([]byte, 16)
  402. copy(padded, key)
  403. encryptData, err := openssl.AesECBEncrypt(jsonData, []byte(padded), openssl.PKCS5_PADDING)
  404. if err != nil {
  405. return []byte("")
  406. }
  407. jsonData, _ = json.Marshal(map[string]string{"data": base64.StdEncoding.EncodeToString(encryptData)})
  408. return jsonData
  409. }