137 lines
4.9 KiB
Python
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() |