Golang 百度雲掃碼登入
阿新 • • 發佈:2021-01-05
技術標籤:Go
檔案結構:
http_client.go程式碼如下:
package client
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
)
// HTTPClient http client
type HTTPClient struct {
*http.Client
UserAgent string
}
var (
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
)
func NewHTTPClient() *HTTPClient {
j,_ := cookiejar.New(nil)
h := &HTTPClient{
Client: &http.Client{
Timeout: 30 * time.Second,
Jar:j,
},
UserAgent: UserAgent,
}
return h
}
// SetUserAgent 設定 UserAgent 瀏覽器標識
func (h *HTTPClient) SetUserAgent(ua string) {
h.UserAgent = ua
}
// SetTimeout 設定 http 請求超時時間, 預設30s
func (h *HTTPClient) SetTimeout(t time.Duration) {
h.Client.Timeout = t
}
func(h *HTTPClient) Fetch(webUrl string,method string,headers map[string]string,data interface{})(respBody []byte,err error){
var req *http.Request
var obody io.Reader
if data != nil {
switch value := data.(type) {
case io.Reader:
obody = value
case map[string]string:
query := url.Values{}
for k := range value {
query.Set(k, value[k])
}
obody = strings.NewReader(query.Encode())
case map[string]interface{}:
query := url.Values{}
for k := range value {
query.Set(k, fmt.Sprint(value[k]))
}
obody = strings.NewReader(query.Encode())
case map[interface{}]interface{}:
query := url.Values{}
for k := range value {
query.Set(fmt.Sprint(k), fmt.Sprint(value[k]))
}
obody = strings.NewReader(query.Encode())
case string:
obody = strings.NewReader(value)
case []byte:
obody = bytes.NewReader(value[:])
default:
return nil, fmt.Errorf("requester.Req: unknown post type: %s", value)
}
}
req, err = http.NewRequest(method, webUrl, obody)
if err!=nil{
return
}
if headers!=nil{
for k,v:=range headers{
req.Header.Set(k,v)
}
}
if method==http.MethodPost{
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
req.Header.Set("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36")
resp,err:=h.Client.Do(req)
if err!=nil{
return
}
defer resp.Body.Close()
//fmt.Println("res Cookies is :",resp.Cookies())
//u,_:= url.Parse(webUrl)
//h.Client.Jar.SetCookies(u,resp.Cookies())
//fmt.Println("cookie jar is:",h.Client.Jar.Cookies(u))
respBody,err=ioutil.ReadAll(resp.Body)
return
}
main.go程式碼如下:
package main
import (
"baidu_qr/client"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
"time"
"github.com/robertkrimen/otto"
)
type QrImageData struct {
ImageUrl string `json:"imgurl"`
Errno int64 `json:"errno"`
Sign string `json:"sign"`
}
type QueryData struct {
ChannelV string `json:"channel_v"`
}
type QrCodeLogin struct {
httpClient *client.HTTPClient
}
func (q *QrCodeLogin) initClient() {
q.httpClient = client.NewHTTPClient()
}
func (*QrCodeLogin) generateGid() (gid string, err error) {
vm := otto.New()
vm.Run(`
function u(x, K) {
x += '';
for (var N = [], T = 0; T < K.length; T++) N[T % 4] ^= K.charCodeAt(T);
var U = ['EC', 'OK'],
V = [];
V[0] = x >> 24 & 255 ^ U[0].charCodeAt(0);
V[1] = x >> 16 & 255 ^ U[0].charCodeAt(1);
V[2] = x >> 8 & 255 ^ U[1].charCodeAt(0);
V[3] = x & 255 ^ U[1].charCodeAt(1);
U = [];
for (T = 0; T < 8; T++) U[T] = T % 2 == 0 ? N[T >> 1] : V[T >> 1];
N = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
V = '';
for (T = 0; T < U.length; T++) {
V += N[U[T] >> 4 & 15];
V += N[U[T] & 15]
}
return V
}
function generate_gid() {
return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(e) {
var t = 16 * Math.random() | 0
var n = "x" === e ? t : 3 & t | 8;
return n.toString(16)
}).toUpperCase()
}
//var abc = '1'+'2'
`)
//使用vm.Call(函式名,nil,傳遞的引數,如果後面有多個引數,用逗號隔開就可以了)
//value, _ := vm.Call("u", nil, "12345678", "234")
//value, _ := vm.Get("abc")
var value otto.Value
value, err = vm.Call("generate_gid", nil)
if err != nil {
fmt.Println("generate gid error,error is:", err.Error())
return
}
fmt.Println(value)
gid = value.String()
return
}
func (q *QrCodeLogin) getQrCode(gid string) (callBack string, respBody []byte, err error) {
t := int64(time.Now().Unix() * 1000)
t1 := t + 21232
t2 := t1 + 4
callBack = fmt.Sprintf("tangram_guid_%d", t)
webUrl := fmt.Sprintf("https://passport.baidu.com/v2/api/getqrcode?lp=pc&qrloginfrom=pc&gid=%s&callback=%s&apiver=v3&tt=%d&tpl=netdisk&_=%d", gid, callBack, t1, t2)
headers := map[string]string{
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
"Connection": "keep-alive",
"Host": "passport.baidu.com",
"Referer": "https://pan.baidu.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
if err != nil {
fmt.Println("get image url error,error is:", err.Error())
return
}
fmt.Println("res is:", string(respBody))
return
}
func (q *QrCodeLogin) downloadQrCode(imageUrl string) (err error) {
headers := map[string]string{
"Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
"Connection": "keep-alive",
"Host": "passport.baidu.com",
"Referer": "https://pan.baidu.com/",
"Sec-Fetch-Dest": "image",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
respBody, err := q.httpClient.Fetch(imageUrl, http.MethodGet, headers, nil)
if err!=nil {
panic(err)
}
file, err := ioutil.TempFile("", "*.png")
if err != nil {
panic(err)
}
//defer os.Remove(file.Name())
_, err = file.Write(respBody)
if err!=nil {
panic(err)
}
file.Close()
fmt.Println("qrcode image file path is:",file.Name())
//imageFile, err := os.Create("F:\\test\\baidu.png")
//defer imageFile.Close()
//
//if err != nil {
// return
//}
//
//imageFile.Write(respBody)
return
}
func (q *QrCodeLogin) queryQrCode(channelId string, gid string) (v string, err error) {
for ; ; {
time.Sleep(2 * time.Second)
t := time.Now().Unix() * 1000
callBack := fmt.Sprintf("tangram_guid_%d", t)
t1 := t + 5
t2 := t1 + 5
webUrl := fmt.Sprintf("https://passport.baidu.com/channel/unicast?channel_id=%s&tpl=netdisk&gid=%s&callback=%s&apiver=v3&tt=%d&_=%d", channelId, gid, callBack, t1, t2)
headers := map[string]string{
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
"Connection": "keep-alive",
"Host": "passport.baidu.com",
"Referer": "https://pan.baidu.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
var respBody []byte
respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\S]+?)\\)", callBack))
res := reg.FindAllSubmatch(respBody, -1)
if len(res) <= 0 {
return
}
respBody = res[0][1]
d := &QueryData{}
fmt.Println("res is:", string(respBody))
err = json.Unmarshal(respBody, d)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
channelV := d.ChannelV
fmt.Println("channel v is:", channelV)
var tempMap map[string]interface{}
err = json.Unmarshal([]byte(channelV), &tempMap)
if err != nil {
fmt.Println("error is:", err.Error())
continue
}
var ok bool
v, ok = tempMap["v"].(string)
if ok {
fmt.Println("tempMap v is:", v)
return
}
}
return
}
func (q *QrCodeLogin) parseCallBackData(callBack string,respBody []byte) (parsedBody []byte,err error){
if callBack == ""{
return
}
reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\s\\S]+?)\\)", callBack))
res := reg.FindAllSubmatch(respBody, -1)
if len(res) <= 0 {
err = errors.New("CallBackError")
return
}
parsedBody = res[0][1]
return
}
func (q *QrCodeLogin) login(fakeBduss string) (bduss string,err error) {
t := time.Now().Unix() * 1000
t1 := t + 225
callBack:="bd__cbs__ay6xvs"
webUrl := fmt.Sprintf("https://passport.baidu.com/v3/login/main/qrbdusslogin?v=%d&bduss=%s&loginVersion=v4&qrcode=1&tpl=netdisk&apiver=v3&tt=%d&traceid=&time=%d&alg=v3&callback=%s", t, fakeBduss, t, t1, callBack)
webUrl = webUrl + "&u=https%253A%252F%252Fpan.baidu.com%252Fdisk%252Fhome"
headers := map[string]string{
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Host": "passport.baidu.com",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66",
}
var respBody []byte
respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
respBody,err = q.parseCallBackData(callBack,respBody)
if err!=nil{
fmt.Println("parse callback data error",err.Error())
return
}
fmt.Println("-----------------------------------------------------------------------------------------------")
text := string(respBody)
fmt.Println("res is:", text)
// "Accept-Encoding": "deflate, br", 去掉了gzip 解碼有點問題
text = strings.ReplaceAll(text, "'", "\"") // 返回的格式中 'data' 可能是單引號
text = strings.ReplaceAll(text, " ", "")
text = strings.ReplaceAll(text, "\\", "")
fmt.Println("text is :", text)
d := &LoginData{}
err = json.Unmarshal([]byte(text), d)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
bduss = d.Data.Session.Bduss
fmt.Println("-----------------------------------------------------------------------------------------------")
fmt.Println("bduss is:", bduss) // 只需要bduss的可以下車了
fmt.Println("-----------------------------------------------------------------------------------------------")
return
}
type LoginData struct {
Data SessionData `json:"data"`
}
type SessionData struct {
Session SessionInfo `json:"session"`
}
type SessionInfo struct {
Bduss string `json:"bduss"`
Stoken string `json:"stoken"`
Ptoken string `json:"ptoken"`
}
func (q *QrCodeLogin) getHomePage() {
webUrl := "https://pan.baidu.com/disk/home"
headers := map[string]string{
"Host": "pan.baidu.com",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Dest": "document",
"Referer": "https://pan.baidu.com/",
"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
}
respBody,err:=q.httpClient.Fetch(webUrl,http.MethodGet,headers,nil)
if err!=nil{
fmt.Println("error is:",err.Error())
return
}
fmt.Println("home page res is",string(respBody))
}
func (q *QrCodeLogin)getBdstoken()(bdstoken string,err error){
webUrl := "https://tongxunlu.baidu.com"
respBody,err:=q.httpClient.Fetch(webUrl,http.MethodGet,nil,nil)
fmt.Println("bdstoken res is",string(respBody))
if err != nil {
fmt.Println("error is",err.Error())
return
}
reg := regexp.MustCompile(`var bdstoken = '(.*?)'`)
res := reg.FindAllSubmatch(respBody, -1)
//fmt.Println("bdstoken is", string(res[0][1]))
if len(res)>0{
if len(res[0])>1{
bdstoken=string(res[0][1])
}
}
if bdstoken==""{
err=errors.New("bdstoken not found")
}
return
}
func main() {
q := &QrCodeLogin{}
q.initClient()
gid, err := q.generateGid()
if err != nil {
return
}
fmt.Println("gid is:", gid)
callBack, respBody, err := q.getQrCode(gid)
if err != nil {
return
}
reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\S]+?)\\)", callBack))
res := reg.FindAllSubmatch(respBody, -1)
if len(res) <= 0 {
return
}
respBody = res[0][1]
d := &QrImageData{}
err = json.Unmarshal(respBody, d)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
sign := d.Sign
imageUrl := fmt.Sprintf("https://%s", d.ImageUrl)
fmt.Println("image url is:", imageUrl)
q.downloadQrCode(imageUrl)
channelV, err := q.queryQrCode(sign, gid)
if err != nil {
fmt.Println("error is:", err.Error())
return
}
fmt.Println("channelV is:", channelV)
q.login(channelV)
q.getHomePage()
fmt.Println("------------------------------------------------------------")
bdstoken,err:=q.getBdstoken()
if err!=nil{
fmt.Println("failed to get bdstoken,error is:",err.Error())
return
}
fmt.Println("bdstoken is:",bdstoken)
}
找到輸出的路徑並用百度雲APP掃碼,確認登入就行,二維碼路徑在臨時目錄中: