目录

发一个golang的记录cookie自动登陆web的部分代码


目录

前言

这段代码是很早以前为了自动抢公司oa上的福利写的,就跟阿里那抢月饼福利类似的,后来我需要的福利不用抢了,这代码也就废弃好久不用了,最近go写的很少,怕把语法都忘了,所以写下来记录一下以后备用。

帖代码

代码的基本中心思想就是,以跨域跳转来判断登陆是否成功,都是一些http库的基本用法,包括了请求,回包处理,跳转回调之类,可以参考一下

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

//带cookie的http请求
func QueryContentWithCookie(QueryURL string) *strings.Reader {
	client := &http.Client{}

	req, err := http.NewRequest("GET", QueryURL, nil)
	if err != nil {
		fmt.Println(err)
		return nil
	}
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

/*
    cookiejar的作用是自动cookie,在response过程服务器增加减少,
    会带来一些cookie上的变化
*/
	client.Jar, _ = cookiejar.New(nil) 
	cookie := GetFileCookie(req.Host)  //从文件记录中获取cookie
	if cookie != nil {
		for _, v := range cookie {
			req.AddCookie(v)
		}
	}

	resp, err := client.Do(req)

	if err != nil {
		fmt.Println(err)
		return nil
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	return strings.NewReader(string(body))
}

//不带cookie要求输入用户名及密码的请求
func QueryContent(QueryURI string) (err error, js *simplejson.Json) {
	for loop := true; loop; {
		var bIsRedirect bool
		var RedirectReq *http.Request
		client := &http.Client{}

		client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			bIsRedirect = true
			RedirectReq = req
			return nil
		}

		req, err := http.NewRequest("GET", QueryURI, nil)
		if err != nil {
			fmt.Println(err)
			return err, nil
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		//	client.Transport = LogRedirects{Domain:req.Host}
		client.Jar, _ = cookiejar.New(nil)

		cookie := GetFileCookie(req.Host)
		if cookie != nil {
			for _, v := range cookie {
				req.AddCookie(v)
			}
		}

		resp, err := client.Do(req)

		if err != nil {
			fmt.Println(err)
			return err, nil
		}

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			// handle error
		}
		//	fmt.Print(string(body))

		if bIsRedirect && RedirectReq.URL.Host != req.URL.Host {
			//如果有redirect,并且跨域,刚可以简单的认为cookie失效,删除cookie重新验证一次
			var password2 string

			for passloop := false; passloop == false; {
				fmt.Println("Please enter your password: ")
				fmt.Scanf("%s\n", &password2)

				password = password + password2

				file_path := current_dir + "/" + req.URL.Host + ".txt"
				os.Remove(file_path)

				passloop = PassOA(string(body), req.URL.Host, RedirectReq.URL.Scheme+"://"+RedirectReq.URL.Host+RedirectReq.URL.Path, password)

			}
        }
        //服务器返回的是json,这里解析成一个simplejson对象,看看用法就好
        //通用的代码还需要大量修改
		if bIsRedirect == false {
			jsContent, jerr := simplejson.NewJson([]byte(body))
			if jerr == nil {
				js = jsContent
			}
			loop = false
		}

	}
	return
}


func GetFileCookie(DomainName string) []*http.Cookie {

	if len(sessionCookie[DomainName]) != 0 {
		return sessionCookie[DomainName]
	}

	cookiefile := current_dir + "/" + DomainName + ".txt"
	_, ferr := os.Stat(cookiefile)
	if ferr != nil && ferr.Error() == os.ErrNotExist.Error() {
		fmt.Println("cookie save file is not exit")
		return nil
	} else {
		f, ferr1 := os.Open(cookiefile)
		if ferr1 == nil {
			defer f.Close()
			br := bufio.NewReader(f)
			var cookiearr []string
			for {
				a, _, c := br.ReadLine()
				if c == io.EOF {
					break
				}
				cookiearr = append(cookiearr, string(a))
			}
			newHeader := make(http.Header)
			newHeader["Set-Cookie"] = cookiearr
			resp := http.Response{Header: newHeader}
			sessionCookie[DomainName] = resp.Cookies()
		}
	}
	return sessionCookie[DomainName]
}

type LogRedirects struct {
	Transport http.RoundTripper
	Domain    string
}

/*
这个回调的作用是在请求前做一轮处理,先是调用原默认回调,
判断返回的resp如果成功登陆就保存cookie到文件,
靠请求和最终跳转成功的domain比对判断登陆是否成功,
这应该不算是个好方法,但是可用
*/

func (l LogRedirects) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	t := l.Transport
	if t == nil {
		t = http.DefaultTransport
	}
	resp, err = t.RoundTrip(req)
	if err != nil {
		return
	}
	//fmt.Println(req.Host+req.URL.Path + "?" + req.URL.RawQuery)

	if req.URL.Host == l.Domain {
		//fmt.Print(resp.Header["Set-Cookie"])
		file_path := current_dir + "/" + l.Domain + ".txt"
		f, err := os.OpenFile(file_path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err == nil {
			defer f.Close()
			savecookie := resp.Cookies()
			for _, v := range savecookie {
				//				fmt.Print(v.String())
				f.WriteString(v.String())
				f.WriteString("\n")
			}
		}
	}

	return
}

func PassOA(HttpReferConent string, OrigDomain string, QueryURL string, Password string) (bRet bool) {

	var PassOK bool
	jar, _ := cookiejar.New(nil)
	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer([]byte(HttpReferConent)))

	var mapQueryParam = make(map[string][]string)
	client := &http.Client{Transport: LogRedirects{Domain: OrigDomain}, Jar: jar}

	if err != nil {
		fmt.Println("error code:", err)
    }
    
    //其实通用的方法应该是找到form再找到submit的input输入,
    //但是如果是ajax的方法这就没法通用了

	node := doc.Find("form")
	if node == nil {
		fmt.Println("no form find")
	}
	posturi, _ := node.Attr("action")

	node.Find("input").Each(func(i int, s *goquery.Selection) {
		var valueArray []string
		name, _ := s.Attr("name")
		value, _ := s.Attr("value")
		valueArray = append(valueArray, value)
		mapQueryParam[name] = valueArray
	})

    //这个是硬编码
	mapQueryParam["LoginName"] = []string{userName}
	mapQueryParam["Password"] = []string{Password}

	client.CheckRedirect = func(newreq *http.Request, via []*http.Request) error {
		//懒得判断多次跳转了,两次302跳转了就是验证成功了
		switch len(via) {
		case 2:
			PassOK = true
			//	case 3:
			//		cookies := newreq.Header["Cookies"]
			//		fmt.Println(cookies)
		}

		return nil
	}

	NewQueryURL := QueryURL[:strings.LastIndex(QueryURL, "/")+1] + posturi
	req, err := http.NewRequest("POST", NewQueryURL, strings.NewReader(url.Values(mapQueryParam).Encode()))
	if err != nil {
		fmt.Println("requst buffer failed")
		return
    }
    
    //这里也是硬编码
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Cookie", "login_username="+userName)

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer resp.Body.Close()
	if PassOK {
		fmt.Println("pass OA verification successful!")

	} else {
		//		body, err := ioutil.ReadAll(resp.Body)
		//		if err != nil {
		// handle error
		//		}
		//		fmt.Println(string(body))
	}

	return PassOK
}