E5-ReLogin/main.py

137 lines
4.9 KiB
Python

import requests
import webbrowser
import time
import pyperclip
from http.server import HTTPServer
from urllib.parse import parse_qs, urlparse
import threading
from tkinter import Tk, Toplevel, Label, Button, Frame # 使用标准 Tkinter
class AuthHandler:
def __init__(self):
pass # 如果需要额外处理,可以在这里实现
class MicrosoftAuth:
def __init__(self, launcher):
self.launcher = launcher
self.login_window = None
def start_auth_server(self):
server_address = ("", 8080)
httpd = HTTPServer(server_address, AuthHandler)
server_thread = threading.Thread(target=httpd.serve_forever)
server_thread.daemon = True
server_thread.start()
def start_microsoft_login(self):
device_url = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"
device_response = requests.post(device_url, data={
"client_id": "23c6a66b-9584-4cc7-831c-ad3785d0fc47",
"scope": "offline_access user.read mail.read"
})
if device_response.status_code != 200:
print(f"获取设备代码失败: {device_response.status_code}")
return
device_data = device_response.json()
user_code = device_data["user_code"]
verification_url = device_data["verification_uri"]
self.show_login_instruction(verification_url, user_code)
threading.Thread(target=self.poll_device_code, args=(device_data["device_code"],)).start()
def show_login_instruction(self, verification_url, user_code):
self.login_window = Toplevel()
self.login_window.geometry("400x200")
self.login_window.title("微软登录")
webbrowser.open(verification_url)
pyperclip.copy(user_code)
Label(self.login_window, text=f"访问 {verification_url} 并输入代码: {user_code}").pack(pady=10)
btn_frame = Frame(self.login_window)
btn_frame.pack(pady=10)
Button(btn_frame, text="打开网页", command=lambda: webbrowser.open(verification_url)).pack(side="left", padx=5)
Button(btn_frame, text="复制代码", command=lambda: pyperclip.copy(user_code)).pack(side="right", padx=5)
def poll_device_code(self, device_code):
token_url = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
while True:
token_response = requests.post(token_url, data={
"client_id": "23c6a66b-9584-4cc7-831c-ad3785d0fc47",
"device_code": device_code,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code"
})
if token_response.status_code == 200:
token_data = token_response.json()
access_token = token_data["access_token"]
profile_url = "https://graph.microsoft.com/v1.0/me"
headers = {"Authorization": f"Bearer {access_token}"}
profile_response = requests.get(profile_url, headers=headers)
profile = profile_response.json()
self.launcher.update_user_info(profile["displayName"])
self.launcher.save_tokens(token_data)
if self.login_window is not None and self.login_window.winfo_exists():
self.login_window.destroy()
print("登录成功!")
return
elif token_response.status_code == 400:
error = token_response.json().get("error")
if error != "authorization_pending":
print(f"登录失败: {error}")
return
time.sleep(5)
class Launcher:
def __init__(self):
self.root = Tk()
self.root.geometry("300x200")
self.root.title("Launcher")
self.user_info_label = Label(self.root, text="未登录")
self.user_info_label.pack(pady=20)
self.microsoft_auth = MicrosoftAuth(self)
self.add_buttons()
def update_user_info(self, display_name):
self.user_info_label.configure(text=f"已登录: {display_name}")
def save_tokens(self, token_data):
print("令牌已保存:", token_data)
def start_login(self):
# 启动微软登录逻辑
self.microsoft_auth.start_auth_server()
self.microsoft_auth.start_microsoft_login()
def logout(self):
# 退出登录逻辑
self.user_info_label.configure(text="未登录")
if self.microsoft_auth.login_window is not None and self.microsoft_auth.login_window.winfo_exists():
self.microsoft_auth.login_window.destroy()
def add_buttons(self):
button_frame = Frame(self.root)
button_frame.pack(pady=10)
Button(button_frame, text="开始登录", command=self.start_login).pack(side="left", padx=5)
Button(button_frame, text="退出登录", command=self.logout).pack(side="right", padx=5)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
launcher = Launcher()
launcher.run()