From 999421326b19f97b01b9caba98b39bf6c2fd5edd Mon Sep 17 00:00:00 2001 From: sishenjieshuo Date: Sun, 9 Feb 2025 22:46:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=BE=AE=E8=BD=AF=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E4=BB=A5=E5=8F=8A=E9=80=80=E5=87=BA=EF=BC=8C=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E4=B8=BA=E6=89=8B=E5=8A=A8=E7=99=BB=E5=BD=95=E5=92=8C?= =?UTF-8?q?=E9=80=80=E5=87=BA=20TODO:=E8=87=AA=E5=8A=A8=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=80=E5=87=BA=E8=B0=83=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/E5-ReLogin.iml | 10 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + auth_handler.py | 14 ++ main.py | 137 ++++++++++++++++++ 8 files changed, 196 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/E5-ReLogin.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 auth_handler.py create mode 100644 main.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/E5-ReLogin.iml b/.idea/E5-ReLogin.iml new file mode 100644 index 0000000..8e75147 --- /dev/null +++ b/.idea/E5-ReLogin.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7d1607b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9cf8123 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/auth_handler.py b/auth_handler.py new file mode 100644 index 0000000..e2a1c38 --- /dev/null +++ b/auth_handler.py @@ -0,0 +1,14 @@ +from http.server import BaseHTTPRequestHandler +from urllib.parse import parse_qs, urlparse + +class AuthHandler(BaseHTTPRequestHandler): + def do_GET(self): + query = urlparse(self.path).query + params = parse_qs(query) + if "code" in params: + code = params["code"][0] + self.send_response(200) + self.send_header("Content-type", "text/html; charset=utf-8") + self.end_headers() + self.wfile.write("

登录成功!请返回启动器

".encode('utf-8')) + print(f"Received code: {code}") # 调试日志 \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..3e9a058 --- /dev/null +++ b/main.py @@ -0,0 +1,137 @@ +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() \ No newline at end of file