Spaces:
BG5
/
Running

BG5 commited on
Commit
9dc0bc5
·
verified ·
1 Parent(s): a80732c

Delete unibo_auth2_get_AuthCode_RefreshToken_sucsses.py

Browse files
unibo_auth2_get_AuthCode_RefreshToken_sucsses.py DELETED
@@ -1,391 +0,0 @@
1
- import concurrent.futures
2
- import json
3
- import os
4
- import re
5
- import threading
6
- from datetime import datetime
7
-
8
- import httpx
9
- from bs4 import BeautifulSoup
10
- from urllib.parse import urljoin, urlencode
11
- from loguru import logger
12
-
13
- # logger.stop()
14
- # 定义线程安全计数器和锁
15
- success_counter = 0
16
- error_counter = 0
17
- # 定义文件名 filename = user-当前月份-日期.txt
18
- current_date = datetime.now().strftime("%m-%d")
19
- # filename = f'user-{current_date}.txt'
20
- counter_lock = threading.Lock()
21
- success_file_lock = threading.Lock()
22
- error_file_lock = threading.Lock()
23
-
24
-
25
- class OAuth2Authenticator:
26
- def __init__(self, username, password):
27
- self.username = username
28
- self.password = password
29
- self.client_id = '9e5f94bc-e8a4-4e73-b8be-63364c29d753'
30
- self.session = httpx.Client(timeout=30.0, follow_redirects=True, verify=False)
31
- self.base_urls = {
32
- 'microsoft': 'https://login.microsoftonline.com',
33
- 'idp': 'https://idp.unibo.it'
34
- }
35
- self.current_state = {} # 用于存储流程中的临时数据
36
-
37
- def _extract_input_value(self, html, name):
38
- """从HTML中提取指定名称的input值"""
39
- soup = BeautifulSoup(html, 'html.parser')
40
- element = soup.find('input', {'name': name})
41
- return element['value'] if element else None
42
-
43
- def _make_request(self, method, url, data=None, **kwargs):
44
- """封装请求方法,统一处理异常"""
45
- try:
46
- # 设置默认的请求头
47
- self.session.headers = {
48
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Thunderbird/137.0',
49
- # 'Accept': 'application/json, text/javascript, */*; q=0.01',
50
- # 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
51
- # 'X-Requested-With': 'XMLHttpRequest'
52
- }
53
-
54
- response = self.session.request(method, url, data=data, **kwargs)
55
- url = response.url
56
- print(f"请求的URL: {url}")
57
- # response.raise_for_status()
58
- return response
59
- except httpx.ConnectError as e:
60
- # print(f"连接失败的目标URL: {e.request.url}")
61
- # print(f"错误详情: {e}")
62
- raise ValueError(e.request.url)
63
- except Exception as e:
64
- print(f"其他错误: {e}")
65
- raise
66
-
67
- def _build_auth_url(self):
68
- """构建初始认证URL"""
69
- params = {
70
- 'response_type': 'code',
71
- 'client_id': self.client_id,
72
- 'redirect_uri': 'https://localhost',
73
- 'scope': 'https://outlook.office.com/EWS.AccessAsUser.All https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access',
74
- 'login_hint': self.username
75
- }
76
- return f"{self.base_urls['microsoft']}/common/oauth2/v2.0/authorize?{urlencode(params)}"
77
-
78
- def step1_get_initial_login_page(self):
79
- """第一步: 获取初始登录页面并提取SAML参数"""
80
- logger.info("执行第一步: 获取初始登录页面")
81
- auth_url = self._build_auth_url()
82
- response = self._make_request('GET', auth_url)
83
-
84
- # self.current_state['relay_state'] = self._extract_input_value(response.text, 'RelayState')
85
- # self.current_state['saml_request'] = self._extract_input_value(response.text, 'SAMLRequest')
86
-
87
- # if not all([self.current_state['relay_state'], self.current_state['saml_request']]):
88
- # raise ValueError("无法从登录页面提取必要的SAML参数")
89
- config_match = re.search(r'\$Config=({.*?//])', response.text, re.DOTALL)
90
- if config_match:
91
- config_value = config_match.group(1).replace('//]', '').strip()
92
- self.current_state['config'] = json.loads(config_value[:-1])
93
- else:
94
- raise ValueError("无法从响应中提取配置信息")
95
- return response
96
-
97
- def step2_submit_saml_request(self):
98
- """第二步: 提交SAML请求到IDP"""
99
- logger.info("执行第二步: 提交SAML请求到IDP")
100
-
101
- data = {
102
- "UserName": self.username,
103
- "Password": self.password,
104
- "AuthMethod": "FormsAuthentication"
105
- }
106
- idp_sso_url = self.current_state['config'].get('bsso').get('failureRedirectUrl')
107
- logger.info(f"IDP SSO URL: {idp_sso_url}")
108
- response = self._make_request('POST', idp_sso_url, data=data)
109
- # 提取配置信息
110
- logger.info(f"LAST URL: {response.url}")
111
-
112
- self.current_state['wa'] = self._extract_input_value(response.text, 'wa')
113
- self.current_state['wresult'] = self._extract_input_value(response.text, 'wresult')
114
- self.current_state['wctx'] = self._extract_input_value(response.text, 'wctx')
115
- # if not self.current_state['csrf_token']:
116
- # raise ValueError("无法从响应中提取CSRF令牌")
117
-
118
- return response
119
-
120
- def step3_submit_loginsrf_response(self):
121
- """第三步: 提交登录响应"""
122
- logger.info("执行第三步: 提交登录响应")
123
-
124
- response = self._make_request('POST',
125
- urljoin(self.base_urls['microsoft'], '/login.srf'),
126
- data={
127
- "wa": self.current_state['wa'],
128
- "wresult": self.current_state['wresult'],
129
- "wctx": self.current_state['wctx']
130
- }
131
- )
132
- config_match = re.search(r'\$Config=({.*?//])', response.text, re.DOTALL)
133
- if config_match:
134
- config_value = config_match.group(1).replace('//]', '').strip()
135
- self.current_state['config'] = json.loads(config_value[:-1])
136
- else:
137
- raise ValueError("无法从响应中提取配置信息")
138
- return response
139
-
140
- def step6_submit_saml_response(self):
141
- """第六步: 处理授权同意"""
142
- logger.info("执行第六步: 处理授权同意")
143
- if 'config' not in self.current_state:
144
- raise ValueError("缺少配置信息")
145
-
146
- config = self.current_state['config']
147
- response = self._make_request('POST',
148
- urljoin(self.base_urls['microsoft'], '/appverify'),
149
- data={
150
- "ContinueAuth": True,
151
- "ctx": config.get("sCtx"),
152
- "hpgrequestid": config.get("sessionId"),
153
- "flowToken": config.get("sFT"),
154
- "iscsrfspeedbump": True,
155
- "canary": config.get("canary"),
156
- "i19": 492026
157
- }
158
- )
159
-
160
- # 提取配置信息
161
- config_match = re.search(r'\$Config=({.*?//])', response.text, re.DOTALL)
162
- if config_match:
163
- config_value = config_match.group(1).replace('//]', '').strip()
164
- self.current_state['config'] = json.loads(config_value[:-1])
165
- else:
166
- raise ValueError("无法从响应中提取配置信息")
167
-
168
- return response
169
-
170
- def step7_handle_consent(self):
171
- """第七步: 处理授权同意"""
172
- logger.info("执行第七步: 处理授权同意")
173
- if 'config' not in self.current_state:
174
- raise ValueError("缺少配置信息")
175
-
176
- config = self.current_state['config']
177
- response = self._make_request('POST',
178
- urljoin(self.base_urls['microsoft'], '/common/Consent/Set'),
179
- data={
180
- "acceptConsent": True,
181
- "ctx": config.get("sCtx"),
182
- "hpgrequestid": config.get("sessionId"),
183
- "flowToken": config.get("sFT"),
184
- "canary": config.get("canary"),
185
- "i19": 958761
186
- }
187
- )
188
-
189
- if 'localhost/?code=' in response.url:
190
- self.current_state['auth_code'] = self._extract_auth_code(response.url)
191
- return response
192
-
193
- raise ValueError("未能成功获取授权码")
194
-
195
- def _extract_auth_code(self, url):
196
- """从URL中提取授权码"""
197
- logger.info(f'提取授权码: {url}')
198
- url = str(url)
199
- match = re.search(r'code=([^&]+)', url)
200
- return match.group(1) if match else None
201
-
202
- def get_refresh_token(self, code):
203
- """获取刷新令牌"""
204
- url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
205
- data = {
206
- 'client_id': self.client_id,
207
- 'grant_type': 'authorization_code',
208
- 'redirect_uri': 'https://localhost',
209
- 'code': code
210
- }
211
- response = self._make_request('POST', url, data=data)
212
- response_data = response.json()
213
- if 'error' in response_data:
214
- raise ValueError(f"获取刷新令牌失败: {response_data['error_description']}")
215
- refresh_token = response_data.get('refresh_token')
216
- access_token = response_data.get('access_token')
217
- return refresh_token, access_token
218
-
219
- def execute_flow(self):
220
- """执行完整的OAuth2认证流程"""
221
- try:
222
- # 更新每一步的last_url
223
- response = self.step1_get_initial_login_page()
224
- self.current_state['last_url'] = response.url
225
- response = self.step2_submit_saml_request()
226
- self.current_state['last_url'] = response.url
227
- response = self.step3_submit_loginsrf_response()
228
- self.current_state['last_url'] = response.url
229
- response = self.step6_submit_saml_response()
230
- self.current_state['last_url'] = response.url
231
-
232
- response = self.step7_handle_consent()
233
- self.current_state['last_url'] = response.url
234
-
235
- if 'auth_code' in self.current_state:
236
- return self.current_state['auth_code']
237
-
238
- raise Exception("认证流程未完成")
239
-
240
- except Exception as e:
241
- code = self._extract_auth_code(e)
242
- if code:
243
- # logger.info(f"提取到的授权码: {code}")
244
- return code
245
- else:
246
- logger.info(f"认证流程出错: {e}")
247
- logger.info("未能提取到授权码")
248
- raise
249
-
250
-
251
- def handle_success(username, original_line_new):
252
- """处理成功账号,线程安全地写入文件并更新计数器"""
253
- global success_counter
254
- try:
255
- with success_file_lock:
256
- with open(success_file, 'a', encoding='utf-8') as file:
257
- file.write(original_line_new + '\n')
258
-
259
- with counter_lock:
260
- success_counter += 1
261
- current_count = success_counter
262
-
263
- logger.info(f"{username} 已写入成功文件,当前成功数: {success_counter},当前失败数: {error_counter}")
264
- except Exception as e:
265
- logger.error(f"写入成功账号 {username} 时出错: {str(e)}")
266
-
267
-
268
- def handle_failure(username, original_line):
269
- """处理失败账号,线程安全地写入文件并更新计数器"""
270
- global error_counter
271
-
272
- try:
273
- with error_file_lock:
274
- with open(error_file, 'a', encoding='utf-8') as file:
275
- file.write(original_line + '\n')
276
-
277
- with counter_lock:
278
- error_counter += 1
279
- current_count = error_counter
280
-
281
- logger.info(f"{username} 已写入失败文件,当前成功数: {success_counter},当前失败数: {error_counter}")
282
- except Exception as e:
283
- logger.error(f"写入失败账号 {username} 时出错: {str(e)}")
284
-
285
-
286
- def read_user_credentials(filepath='user.txt'):
287
- """从user.txt文件中读取用户名和密码"""
288
- credentials = []
289
- try:
290
- with open(filepath, 'r', encoding='utf-8') as file:
291
- for line in file:
292
- parts = line.strip().split('---')
293
- if len(parts) >= 3: # 至少需要时间、用户名和密码
294
- timestamp = parts[0]
295
- username = parts[1]
296
- password = parts[2]
297
- email = parts[3] if len(parts) > 3 else ""
298
- credentials.append((username, password, email, line.strip()))
299
- logger.info(f"成功从{filepath}读取了{len(credentials)}个账号信息")
300
- return credentials
301
- except Exception as e:
302
- logger.error(f"读取凭据文件时出错: {str(e)}")
303
- return []
304
-
305
-
306
- def ensure_files_exist():
307
- """确保输出文件存在"""
308
-
309
- files = [success_file, error_file]
310
- for file in files:
311
- try:
312
- if not os.path.exists(file):
313
- with open(file, 'w', encoding='utf-8') as f:
314
- pass
315
- logger.info(f"创建文件 {file}")
316
- except Exception as e:
317
- logger.error(f"创建文件 {file} 时出错: {str(e)}")
318
- return False
319
- return True
320
- def main_threaded(username, password, original_line):
321
- authenticator = OAuth2Authenticator(
322
- username=username,
323
- password=password, # 实际使用时应从安全来源获取密码
324
- )
325
-
326
- try:
327
- auth_code = authenticator.execute_flow()
328
- refresh_token, access_token = authenticator.get_refresh_token(auth_code)
329
- print(f"成功获取授权码:\n{auth_code}\n{refresh_token}\n{access_token}\n")
330
- # 报错
331
- original_line_new = f'{original_line}---{refresh_token}'
332
- handle_success(username, original_line_new)
333
- except Exception as e:
334
- print(f"认证失败: {e}")
335
- handle_failure(username, original_line)
336
-
337
- # 使用示例
338
- if __name__ == "__main__":
339
-
340
- # authenticator = OAuth2Authenticator(
341
- # username='[email protected]',
342
- # password='W17M&HQK^x1q7h.', # 实际使用时应从安全来源获取密码
343
- # )
344
- #
345
- # try:
346
- # auth_code = authenticator.execute_flow()
347
- # refresh_token, access_token = authenticator.get_refresh_token(auth_code)
348
- # print(f"成功获取授权码:\n{auth_code}\n{refresh_token}\n{access_token}\n")
349
- # except Exception as e:
350
- # print(f"认证失败: {e}")
351
- filename = 'user-4-21-success.txt'
352
- success_file = filename.replace('success.txt', 'refresh-success.txt')
353
- error_file = filename.replace('error.txt', 'refresh-error.txt')
354
- filepath = filename
355
- credentials = read_user_credentials(filepath)
356
- if not credentials:
357
- logger.error("没有读取到有效凭据,退出程序")
358
- exit(1)
359
-
360
- # 确保输出文件存在
361
- if not ensure_files_exist():
362
- logger.error("创建输出文件失败,退出程序")
363
- exit(1)
364
-
365
- # 设置最大线程数
366
- max_workers = 1 # 可以根据需要调整线程数量
367
-
368
- logger.info(f"开始多线程处理账号,最大并发数: {max_workers}")
369
-
370
- # 使用线程池处理账号
371
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
372
- # 提交所有任务
373
- futures = []
374
- for username, password, email, original_line in credentials:
375
- # 提交任务到线程池
376
- future = executor.submit(
377
- main_threaded,
378
- username,
379
- password,
380
- original_line
381
- )
382
- futures.append(future)
383
-
384
- # 等待所有任务完成
385
- for future in concurrent.futures.as_completed(futures):
386
- try:
387
- future.result() # 获取结果,但我们不需要处理
388
- except Exception as e:
389
- logger.error(f"执行任务时发生异常: {str(e)}")
390
-
391
- logger.info(f"处理完成,共成功 {success_counter} 个账号,失败 {error_counter} 个账号。")