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()