parent
35ec37b5b0
commit
999421326b
|
@ -0,0 +1,8 @@
|
||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.12 (E5-ReLogin)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.12 (pythonProject)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (E5-ReLogin)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/E5-ReLogin.iml" filepath="$PROJECT_DIR$/.idea/E5-ReLogin.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -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("<h1 style='font-family: Arial; color: #2b2b2b;'>登录成功!请返回启动器</h1>".encode('utf-8'))
|
||||||
|
print(f"Received code: {code}") # 调试日志
|
|
@ -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()
|
Loading…
Reference in New Issue