php session_destroy(),PHP: session_destroy - Manual

本文详细介绍了如何在PHP中使用session_destroy()函数清除当前会话的所有数据,包括何时及如何调用该函数,并讨论了销毁会话可能带来的并发问题。

Beschreibung

session_destroy

(

) : bool

session_destroy() löscht alle in Verbindung mit der

aktuellen Session stehenden Daten. Mit der Session zusammenhängende globale

Variablen und das Session-Cookie werden nicht gelöscht. Um wieder

Session-Variablen verwenden zu können, muss

Hinweis:

Normalerweise ist es nicht erforderlich session_destroy()

aufzurufen; statt dessen sollte das $_SESSION-Array bereinigt werden.

Um die Session komplett zu löschen, muss auch die Session-ID gelöscht

werden. Wenn zum Verfolgen der Session ein Cookie benutzt wird

(standardmäßige Einstellung), muss das Session-Cookie gelöscht werden.

Dafür kann

Ist session.use_strict_mode

aktiviert, dann ist es nicht nötig, veraltete Session-ID-Cookies zu löschen,

da das Session-Modul dann kein Session-ID-Cookie akzeptiert, wenn keine zu

dieser Session-ID gehörigen Daten vorhanden sind, so dass ein neues

Session-ID-Cookie gesetzt würde.

Das Aktivieren von session.use_strict_mode

wird grundsätzlich empfohlen.

Warnung

Das sofortige Löschen der Session kann zu unerwünschten Ergebnissen

führen. Wenn es gleichzeitige Anfragen gibt, können andere Verbindungen,

z.B. Anfragen von JavaScript und/oder Anfragen von URL-Links, plötzlich

Session-Daten verlieren.

Das aktuelle Session-Modul akzeptiert zwar keine leeres Session-ID-Cookie,

aber das sofortige Löschen der Sitzung kann aufgrund einer Client-

(Browser-) seitigen Race-Condition zu einem leeren Session-ID-Cookie

führen. Dies würde dazu führen, dass der Client unnötig viele Session-IDs

erzeugt.

Um diese zu vermeiden, müssen Sie den Löschzeitstempel auf $_SESSION

setzen und den Zugriff zu einem späteren Zeitpunkt verweigern. Oder

stellen Sie sicher, dass Ihre Anwendung keine gleichzeitige Anfragen hat.

Dies gilt auch für

为什么以下代码在锁屏后再次进入桌面就识别不到鼠标和键盘的活动了,动鼠标和键盘并不能解除锁屏倒计时,仅需写出修改部分的代码,不用给出全部 import tkinter as tk from tkinter import simpledialog, messagebox, scrolledtext, ttk import time import threading import json import os import platform from cryptography.fernet import Fernet from pynput import mouse, keyboard import sys import subprocess import csv # 配置参数(常量使用大写) IDLE_THRESHOLD = 5 # 3分钟空闲时间(秒) COUNTDOWN_DURATION = 10 # 锁屏倒计时(秒) KEY_FILE = "system_secret.key" # 加密密钥文件名 LOG_FILE = "secure_usage_log.log" # 操作日志文件路径 ADMIN_PASSWORD = "Admin@123" # 管理员密码 LOCK_SCREEN_BG = "#000000" # 锁屏背景色(黑色) LOGIN_BG = "#2c3e50" # 登录界面背景色(深蓝色) BUTTON_BG = "#3498db" # 按钮背景色(亮蓝色) USER_FILE = r"C:\Users\x00708\PycharmProjects\mission\practice_from_work_data\lock_monitor\培训通过人员名单.csv" # 用户名单文件路径 USER_MANAGEMENT_PASSWORD = ADMIN_PASSWORD # 用户管理密码设置为管理员密码 # 培训通过人员名单(工号:密码) # 修改为文件⭐ def load_trained_users(file_path): """ 从CSV文件加载培训通过人员名单 文件格式要求:工号,密码 返回字典 {工号: 密码} """ users = {} try: with open(file_path, 'r', newline='', encoding='utf-8') as file: reader = csv.reader(file) for row in reader: if row and len(row) >= 2: # 确保有至少两列 user_id = row[0].strip() password = row[1].strip() users[user_id] = password print(f"成功导入 {len(users)} 名用户") return users except FileNotFoundError: print(f"错误:用户文件 {file_path} 不存在") # 返回空字典但包含管理员账户 return {"ADMIN": ADMIN_PASSWORD} except Exception as e: print(f"读取用户文件错误: {str(e)}") # 返回空字典但包含管理员账户 return {"ADMIN": ADMIN_PASSWORD} # 添加保存用户文件的函数 def save_user_file(users, file_path): """保存用户字典到CSV文件""" try: with open(file_path, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) for user_id, password in users.items(): writer.writerow([user_id, password]) return True except Exception as e: print(f"保存用户文件时出错: {str(e)}") return False # 从文件加载培训通过人员名单 TRAINED_USERS = load_trained_users(USER_FILE) # 确保管理员账户存在 if "ADMIN" not in TRAINED_USERS: TRAINED_USERS["ADMIN"] = ADMIN_PASSWORD save_user_file(TRAINED_USERS, USER_FILE) class SecureDesktopMonitor: def __init__(self): # 创建主窗口 self.root = tk.Tk() # 创建主窗口对象 self.root.title("安全桌面监控系统") # 设置窗口标题 self.root.geometry("800x600") # 初始窗口尺寸 self.root.configure(bg=LOGIN_BG) # 设置背景色 # 设置窗口在最顶层 self.root.attributes("-topmost", True) # 系统状态变量,防止用户绕过监控 self.current_user = None # 当前登录用户(未登录时为None) self.login_time = 0 # 登陆时间(初始为0) self.last_activity = time.time() # 最后活动时间(初始化为当前时间) self.idle_timer = None # 空闲检测定时器(用于推迟锁屏) self.countdown_timer = None # 倒计时定时器(锁屏前提示) self.listening = False # 监听器状态标志 self.is_locked = False # 界面锁屏状态(初始为True,即未登录时锁定) # 创建鼠标键盘监听器 self.mouse_listener = mouse.Listener(on_move=self.activity_detected) # 鼠标移动触发活动检测 self.keyboard_listener = keyboard.Listener(on_press=self.activity_detected) # 键盘按键触发活动检测 # 显示初始登录界面 self.show_login_screen() # Tkinter事件循环(阻塞式,保持程序运行),mainloop()是Tkinter的核心,用于处理用户事件(如点击、输入) self.root.mainloop() def show_login_screen(self): """显示登录界面""" self.clear_screen() # 清空当前屏幕所有内容 self.is_locked = True # 标记系统为锁定状态 # 设置全屏模式(防止用户切换窗口) self.root.attributes('-fullscreen', True) # 创建居中主框架 main_frame = tk.Frame(self.root, bg=LOGIN_BG) # bg颜色深蓝色 main_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 使用place布局精确居中 # 系统标题标签 title = tk.Label( main_frame, text="安全桌面登录系统", font=("Arial", 24, "bold"), # 字体族、大小、粗体 fg="white", # 前景色(白色) bg=LOGIN_BG # 背景色与界面一致 ) title.pack(pady=20) # 打包布局,垂直间距20像素 # 登录表单框架(使用grid布局对齐输入框) form_frame = tk.Frame(main_frame, bg=LOGIN_BG) form_frame.pack(pady=20) # 工号输入标签和输入框 tk.Label( form_frame, # 框架格式 text="工号:", font=("Arial", 14), fg="white", bg=LOGIN_BG ).grid(row=0, column=0, padx=10, pady=10, sticky="e") self.id_entry = tk.Entry(form_frame, font=("Arial", 14), width=20) # 单行文本输入框 self.id_entry.grid(row=0, column=1, padx=10, pady=10) self.id_entry.focus_set() # 自动聚焦到工号输入框(提升用户体验) # 密码输入标签和输入框(内容显示为*号) tk.Label( form_frame, text="密码:", font=("Arial", 14), fg="white", bg=LOGIN_BG ).grid(row=1, column=0, padx=10, pady=10, sticky="e") self.pw_entry = tk.Entry(form_frame, show="*", font=("Arial", 14), width=20) # show="*" 隐藏输入 self.pw_entry.grid(row=1, column=1, padx=10, pady=10) # 放置位置 # 绑定回车键(用户输入密码后按回车可直接登录) self.pw_entry.bind("<Return>", lambda event: self.authenticate_user()) # 登录按钮 login_btn = tk.Button( form_frame, text="登 录", command=self.authenticate_user, # 点击时触发认证方法 font=("Arial", 14, "bold"), bg=BUTTON_BG, # 按钮背景色 fg="white", # 按钮文字颜色 width=15, height=2 ) login_btn.grid(row=2, column=0, columnspan=2, pady=20) # 放置位置跨两列居中 # 系统信息 sys_info = tk.Label( main_frame, text="用户使用时长监测系统 | 仅限授权人员使用", font=("Arial", 10), fg="#bdc3c7", bg=LOGIN_BG ) sys_info.pack(side=tk.BOTTOM, pady=10) def authenticate_user(self): """验证用户身份""" user_id = self.id_entry.get().strip() # 获取用户id和密码 password = self.pw_entry.get().strip() if user_id in TRAINED_USERS and TRAINED_USERS[user_id] == password: self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.start_desktop_session() # 开始桌面会话函数 else: messagebox.showerror("访问拒绝", "未授权操作!禁止访问系统!") # 清空密码框 self.pw_entry.delete(0, tk.END) def start_desktop_session(self): """开始桌面会话""" self.clear_screen() self.root.attributes('-fullscreen', False) self.root.geometry("600x600") self.root.title(f"安全桌面 - 用户: {self.current_user}") # 显示桌面内容 desktop_frame = tk.Frame(self.root) desktop_frame.pack(fill=tk.BOTH, expand=True) # 欢迎信息 welcome_msg = tk.Label( desktop_frame, text=f"欢迎, {self.current_user}", font=("Arial", 24, "bold"), pady=50 ) welcome_msg.pack() # ⭐管理员按钮组框架 admin_frame = tk.Frame(desktop_frame) admin_frame.pack(pady=10) # 管理员查看记录按钮 admin_btn = tk.Button( admin_frame, text="管理员查看记录", command=self.admin_view, font=("Arial", 12), bg="#9b59b6", fg="white" ) admin_btn.pack(side=tk.LEFT, padx=5) # 新增:管理用户名单按钮(仅管理员可见) if self.current_user == "ADMIN": manage_users_btn = tk.Button( admin_frame, text="管理用户名单", command=self.manage_users, font=("Arial", 12), bg="#e67e22", fg="white" ) manage_users_btn.pack(side=tk.LEFT, padx=5) # 状态信息 status_text = tk.Label( desktop_frame, text="桌面已解锁 - 工作中...", font=("Arial", 16), fg="green" ) status_text.pack(pady=20) # 使用时间显示 self.time_label = tk.Label( desktop_frame, text="使用时间: 00:00", font=("Arial", 14) ) self.time_label.pack(pady=10) # 手动锁定按钮 lock_btn = tk.Button( desktop_frame, text="锁定系统", command=self.lock_system, # 调用锁定系统函数 font=("Arial", 12), bg="#e74c3c", fg="white" ) lock_btn.pack(pady=20) # lock_btn为变量名对象,pack为tkinter的几何布局管理器,可自动排列控件 # 启动鼠标键盘事件监听 self.start_activity_monitoring() # 鼠标键盘监听函数 # 开始空闲检测 self.start_idle_monitor() # 开始更新使用时间显示 self.update_usage_time() def manage_users(self): """管理员管理用户名单""" # 验证管理员密码 password = simpledialog.askstring("管理员认证", "请输入管理员密码:", show='*') if password != ADMIN_PASSWORD: messagebox.showerror("认证失败", "密码错误!") return self.clear_screen() self.root.title("用户名单管理") self.root.geometry("800x600") # 标题 title = tk.Label( self.root, text="用户名单管理", font=("Arial", 24, "bold"), pady=20 ) title.pack() # 框架容器 container = tk.Frame(self.root) container.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) # 创建Treeview显示用户 columns = ("工号", "密码") self.user_tree = ttk.Treeview(container, columns=columns, show="headings", selectmode="browse") # 设置列标题 for col in columns: self.user_tree.heading(col, text=col) self.user_tree.column(col, width=100, anchor=tk.CENTER) # 添加滚动条 scrollbar = ttk.Scrollbar(container, orient=tk.VERTICAL, command=self.user_tree.yview) self.user_tree.configure(yscroll=scrollbar.set) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.user_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 刷新用户列表 self.refresh_user_list() # 按钮框架 btn_frame = tk.Frame(self.root) btn_frame.pack(pady=20) # 添加用户按钮 add_btn = tk.Button( btn_frame, text="添加用户", command=self.add_user, font=("Arial", 12), bg="#2ecc71", fg="white" ) add_btn.pack(side=tk.LEFT, padx=10) # 删除用户按钮 delete_btn = tk.Button( btn_frame, text="删除用户", command=self.delete_user, font=("Arial", 12), bg="#e74c3c", fg="white" ) delete_btn.pack(side=tk.LEFT, padx=10) # 返回按钮 back_btn = tk.Button( btn_frame, text="返回桌面", command=self.start_desktop_session, font=("Arial", 12), bg="#3498db", fg="white" ) back_btn.pack(side=tk.LEFT, padx=10) def refresh_user_list(self): """刷新用户列表显示""" # 清除现有数据 for item in self.user_tree.get_children(): self.user_tree.delete(item) # 添加用户数据 for user_id, password in TRAINED_USERS.items(): self.user_tree.insert("", tk.END, values=(user_id, password)) def add_user(self): """添加新用户""" dialog = tk.Toplevel(self.root) dialog.title("添加新用户") dialog.geometry("300x200") dialog.transient(self.root) dialog.grab_set() tk.Label(dialog, text="工号:").pack(pady=(10, 0)) id_entry = tk.Entry(dialog, width=20) id_entry.pack() tk.Label(dialog, text="密码:").pack(pady=(10, 0)) password_entry = tk.Entry(dialog, show="*", width=20) password_entry.pack() def save_new_user(): user_id = id_entry.get().strip() password = password_entry.get().strip() if not user_id or not password: messagebox.showerror("错误", "工号和密码不能为空", parent=dialog) return if user_id in TRAINED_USERS: messagebox.showerror("错误", "该工号已存在", parent=dialog) return # 添加到全局用户列表并保存到文件 TRAINED_USERS[user_id] = password save_user_file(TRAINED_USERS, USER_FILE) # 刷新显示 self.refresh_user_list() dialog.destroy() messagebox.showinfo("成功", f"用户 {user_id} 添加成功") tk.Button(dialog, text="保存", command=save_new_user).pack(pady=20) def delete_user(self): """删除选中用户""" selected = self.user_tree.selection() if not selected: messagebox.showerror("错误", "请先选择一个用户") return item = selected[0] values = self.user_tree.item(item, "values") user_id = values[0] if user_id == "ADMIN": messagebox.showerror("错误", "不能删除管理员账户") return if messagebox.askyesno("确认", f"确定要删除用户 {user_id} 吗?"): # 从全局用户列表中删除 if user_id in TRAINED_USERS: del TRAINED_USERS[user_id] save_user_file(TRAINED_USERS, USER_FILE) self.refresh_user_list() messagebox.showinfo("成功", f"用户 {user_id} 已删除") def update_usage_time(self): """更新使用时间显示""" if not self.is_locked: # 如果没有锁 usage_seconds = time.time() - self.login_time minutes, seconds = divmod(int(usage_seconds), 60) self.time_label.config(text=f"使用时间: {minutes:02d}:{seconds:02d}") self.root.after(1000, self.update_usage_time) # 1s后更新时间显示 def start_activity_monitoring(self): """启动鼠标键盘事件监听""" if not self.listening: mouse_thread = threading.Thread(target=self.mouse_listener.start) # 启动鼠标事件监听线程 keyboard_thread = threading.Thread(target=self.keyboard_listener.start) # 启动键盘事件监听线程 mouse_thread.daemon = True # daemon=True 将线程设为守护模式,意味着当主程序退出时,这两个线程会被强制终止,不会阻碍程序关闭 keyboard_thread.daemon = True mouse_thread.start() keyboard_thread.start() self.listening = True # 标记监听状态已激活,防止方法被重复调用 def stop_activity_monitoring(self): """停止鼠标键盘事件监听""" if self.listening: self.mouse_listener.stop() self.keyboard_listener.stop() self.listening = False def activity_detected(self, *args): # 当用户按下键盘或移动鼠标时,系统会调用此方法 """检测到用户活动""" if not self.is_locked: # 系统未被锁定 self.last_activity = time.time() # 更新最后活动时间,重新计算闲置周期 if self.countdown_timer: # 如果倒数计时器正在进行 self.root.after_cancel(self.countdown_timer) # 停止倒数,取消锁屏计划 self.countdown_timer = None # 清除锁屏提示界面,恢复正常工作环境 self.clear_screen() self.start_desktop_session() def start_idle_monitor(self): """监控空闲状态""" if not self.is_locked: idle_time = time.time() - self.last_activity if idle_time > IDLE_THRESHOLD: # 空闲超时 self.start_lock_countdown() else: # 每秒检查一次 self.idle_timer = self.root.after(1000, self.start_idle_monitor) def start_lock_countdown(self): """开始锁屏倒计时""" self.clear_screen() self.root.configure(bg=LOCK_SCREEN_BG) self.root.attributes('-fullscreen', True) # 倒计时显示 self.countdown = COUNTDOWN_DURATION self.countdown_label = tk.Label( self.root, text=f"系统将在 {self.countdown} 秒后锁定...", font=("Arial", 36, "bold"), fg="red", bg=LOCK_SCREEN_BG ) self.countdown_label.place(relx=0.5, rely=0.4, anchor=tk.CENTER) # 提示信息 prompt = tk.Label( self.root, text="检测到系统空闲,移动鼠标或按键取消锁定", font=("Arial", 20), fg="#3498db", bg=LOCK_SCREEN_BG ) prompt.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 用户信息 user_info = tk.Label( self.root, text=f"当前用户: {self.current_user}", font=("Arial", 16), fg="white", bg=LOCK_SCREEN_BG ) user_info.place(relx=0.5, rely=0.6, anchor=tk.CENTER) # 开始倒计时 self.update_countdown() def update_countdown(self): """更新倒计时显示""" self.countdown -= 1 self.countdown_label.config(text=f"系统将在 {self.countdown} 秒后锁定...") if self.countdown <= 0: self.lock_system() else: # 每秒检查一次 self.countdown_timer = self.root.after(1000, self.update_countdown) def lock_system(self, manual=False): """锁定系统并保存使用记录""" logout_time = time.time() usage_seconds = logout_time - self.login_time # 格式化使用时间 minutes, seconds = divmod(int(usage_seconds), 60) usage_time = f"{minutes:02d}:{seconds:02d}" # 创建记录 record = { "user_id": self.current_user, "login_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.login_time)), "logout_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(logout_time)), "usage_duration": usage_time, "lock_type": "手动" if manual else "自动" } # 加密保存记录 self.save_usage_record(record) # 停止监听和计时器 if self.idle_timer: self.root.after_cancel(self.idle_timer) if self.countdown_timer: self.root.after_cancel(self.countdown_timer) self.stop_activity_monitoring() # 重置用户状态 self.current_user = None self.is_locked = True # 显示登录界面 self.show_login_screen() def save_usage_record(self, record): """加密保存使用记录""" # 生成或加载加密密钥 if not os.path.exists(KEY_FILE): # 检查密钥文件是否存在 key = Fernet.generate_key() # 若不存在(首次运行),Fernet.generate_key()生成一个新的对称加密密钥(256位),并以二进制模式写入文件 with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: # 如果已存在,直接读取密钥内容 key = f.read() cipher = Fernet(key) # 创建Fernet加密器实例.Fernet是AES加密的封装库,提供简单易用的对称加密功能 # 读取已有记录,避免新记录覆盖旧数据 records = [] if os.path.exists(LOG_FILE): # 检查日志文件是否存在 with open(LOG_FILE, "rb") as f: # 如果存在,以二进制模式读取加密数据 encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) # 使用cipher.decrypt()解密 records = json.loads(decrypted_data) # 通过json.loads()将JSON字符串解析为Python列表 # 添加新记录 records.append(record) # 加密保存 encrypted_data = cipher.encrypt(json.dumps(records).encode()) # 将整个记录列表重新加密 with open(LOG_FILE, "wb") as f: f.write(encrypted_data) def admin_view(self): """管理员查看使用记录""" password = simpledialog.askstring("管理员认证", "请输入管理员密码:", show='*') if password == ADMIN_PASSWORD: records = self.get_usage_records() # 获取使用记录函数 self.display_records(records) # 显示使用记录函数 else: messagebox.showerror("认证失败", "密码错误!") # 密码框 def get_usage_records(self): """获取使用记录(解密)""" if not os.path.exists(KEY_FILE) or not os.path.exists(LOG_FILE): return [] try: with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) with open(LOG_FILE, "rb") as f: encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) return json.loads(decrypted_data) except: return [] def display_records(self, records): """显示使用记录""" self.clear_screen() self.root.title("使用记录 - 管理员视图") self.root.geometry("1000x700") # 标题 title = tk.Label( self.root, text="电脑使用记录 - 安全报告", font=("Arial", 24, "bold"), pady=20 ) title.pack() # 滚动文本框 text_area = scrolledtext.ScrolledText( self.root, wrap=tk.WORD, font=("Consolas", 12), width=120, height=30 ) text_area.pack(padx=20, pady=10, fill=tk.BOTH, expand=True) # 添加表头 header = "工号 登录时间 登出时间 使用时间 锁定类型\n" text_area.insert(tk.INSERT, header) text_area.insert(tk.INSERT, "-" * 65 + "\n") # 添加记录 for record in records: line = ( f"{record['user_id']:<8} " f"{record['login_time']:<20} " f"{record['logout_time']:<20} " f"{record['usage_duration']:<8} " f"{record.get('lock_type', '自动'):<8}\n" ) text_area.insert(tk.INSERT, line) text_area.configure(state='disabled') # 设为只读 # 返回按钮 tk.Button( self.root, text="返回登录界面", command=self.show_login_screen, font=("Arial", 12), bg=BUTTON_BG, fg="white", width=20 ).pack(pady=20) def clear_screen(self): """清除当前屏幕所有内容""" for widget in self.root.winfo_children(): widget.destroy() def lock_on_close(self): """关闭窗口时锁定系统""" if self.current_user: self.lock_system(manual=True) self.root.destroy() # 启动系统 if __name__ == "__main__": app = SecureDesktopMonitor() # 绑定窗口关闭事件 app.root.protocol("WM_DELETE_WINDOW", app.lock_on_close)
10-29
基于以下代码修改实现,桌面会话窗口不在显示的最顶层但锁屏倒计时和登陆界面必须要弹出在最顶层。仅给出需修改部分的代码即可。 import tkinter as tk from tkinter import simpledialog, messagebox, scrolledtext, ttk import time import threading import json import os import platform from cryptography.fernet import Fernet from pynput import mouse, keyboard import sys import subprocess import csv import pandas as pd # 获取当前脚本所在目录作为基础路径 if getattr(sys, 'frozen', False): # 打包后的可执行文件路径 BASE_DIR = os.path.dirname(sys.executable) else: # 脚本文件路径 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # 配置参数(常量使用大写) IDLE_THRESHOLD = 5 # 3分钟空闲时间(秒) COUNTDOWN_DURATION = 10 # 锁屏倒计时(秒) KEY_FILE = os.path.join(BASE_DIR, "system_secret.key") # 加密密钥文件名改为绝对路径 LOG_FILE = os.path.join(BASE_DIR, "secure_usage_log.log") # 操作日志文件路径改为绝对路径 ADMIN_PASSWORD = "123" # 管理员密码 LOCK_SCREEN_BG = "#000000" # 锁屏背景色(黑色) LOGIN_BG = "#2c3e50" # 登录界面背景色(深蓝色) BUTTON_BG = "#3498db" # 按钮背景色(亮蓝色) USER_FILE = os.path.join(BASE_DIR, "培训通过人员名单.csv") # 改为绝对路径 USER_MANAGEMENT_PASSWORD = ADMIN_PASSWORD # 用户管理密码设置为管理员密码 # 确保BASE_DIR存在 os.makedirs(BASE_DIR, exist_ok=True) # 培训通过人员名单(工号:密码) # 修改全局用户加载逻辑 def load_trained_users(file_path): """ 从CSV文件加载培训通过人员名单 文件格式要求:工号,密码 返回字典 {工号: 密码} """ users = {} try: # 检查文件是否存在 if not os.path.exists(file_path): print(f"用户文件 {file_path} 不存在,创建初始管理员账号") # 创建只包含管理员的初始用户名单 users = {"ADMIN": ADMIN_PASSWORD} # 保存加密的用户名单 save_user_file(users, file_path) return users # 加载加密密钥 if not os.path.exists(KEY_FILE): key = Fernet.generate_key() with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) # 解密用户数据 with open(file_path, "rb") as f: encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) return json.loads(decrypted_data) except Exception as e: print(f"加载用户名单错误: {str(e)}") # 返回只包含管理员的用户名单 return {"ADMIN": ADMIN_PASSWORD} # 添加保存用户文件的函数 def save_user_file(users, file_path): """保存用户字典到CSV文件""" try: # 确保密钥存在 if not os.path.exists(KEY_FILE): key = Fernet.generate_key() with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) # 加密用户数据 encrypted_data = cipher.encrypt(json.dumps(users).encode()) # 新加 os.makedirs(os.path.dirname(file_path) or '.', exist_ok=True) with open(file_path, "wb") as f: f.write(encrypted_data) return True except Exception as e: print(f"保存用户文件时出错: {str(e)}") return False # 从文件加载培训通过人员名单 TRAINED_USERS = load_trained_users(USER_FILE) # 确保管理员账户存在 if "ADMIN" not in TRAINED_USERS: TRAINED_USERS["ADMIN"] = ADMIN_PASSWORD save_user_file(TRAINED_USERS, USER_FILE) class SecureDesktopMonitor: def __init__(self): # 创建主窗口 self.root = tk.Tk() # 创建主窗口对象 self.root.title("安全桌面监控系统") # 设置窗口标题 self.root.geometry("800x600") # 初始窗口尺寸 self.root.configure(bg=LOGIN_BG) # 设置背景色 # 设置窗口在最顶层 self.root.attributes("-topmost", True) # 系统状态变量,防止用户绕过监控 self.current_user = None # 当前登录用户(未登录时为None) self.login_time = 0 # 登陆时间(初始为0) self.last_activity = time.time() # 最后活动时间(初始化为当前时间) self.idle_timer = None # 空闲检测定时器(用于推迟锁屏) self.countdown_timer = None # 倒计时定时器(锁屏前提示) self.listening = False # 监听器状态标志 self.is_locked = False # 界面锁屏状态(初始为True,即未登录时锁定) # 创建鼠标键盘监听器 self.mouse_listener = mouse.Listener(on_move=self.activity_detected) # 鼠标移动触发活动检测 self.keyboard_listener = keyboard.Listener(on_press=self.activity_detected) # 键盘按键触发活动检测 # 显示初始登录界面 self.show_login_screen() # Tkinter事件循环(阻塞式,保持程序运行),mainloop()是Tkinter的核心,用于处理用户事件(如点击、输入) self.root.mainloop() def show_login_screen(self): """显示登录界面""" self.clear_screen() # 清空当前屏幕所有内容 self.is_locked = True # 标记系统为锁定状态 # 设置全屏模式(防止用户切换窗口) self.root.attributes('-fullscreen', True) # 创建居中主框架 main_frame = tk.Frame(self.root, bg=LOGIN_BG) # bg颜色深蓝色 main_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 使用place布局精确居中 # 系统标题标签 title = tk.Label( main_frame, text="安全桌面登录", font=("Arial", 24, "bold"), # 字体族、大小、粗体 fg="white", # 前景色(白色) bg=LOGIN_BG # 背景色与界面一致 ) title.pack(pady=20) # 打包布局,垂直间距20像素 # 登录表单框架(使用grid布局对齐输入框) form_frame = tk.Frame(main_frame, bg=LOGIN_BG) form_frame.pack(pady=20) # 工号输入标签和输入框 tk.Label( form_frame, # 框架格式 text="工号:", font=("黑体", 14), fg="white", bg=LOGIN_BG ).grid(row=0, column=0, padx=10, pady=10, sticky="e") self.id_entry = tk.Entry(form_frame, font=("Arial", 14), width=20) # 单行文本输入框 self.id_entry.grid(row=0, column=1, padx=10, pady=10) self.id_entry.focus_set() # 自动聚焦到工号输入框(提升用户体验) # 密码输入标签和输入框(内容显示为*号) tk.Label( form_frame, text="密码:", font=("黑体", 14), fg="white", bg=LOGIN_BG ).grid(row=1, column=0, padx=10, pady=10, sticky="e") self.pw_entry = tk.Entry(form_frame, show="*", font=("Arial", 14), width=20) # show="*" 隐藏输入 self.pw_entry.grid(row=1, column=1, padx=10, pady=10) # 放置位置 # 绑定回车键(用户输入密码后按回车可直接登录) self.pw_entry.bind("<Return>", lambda event: self.authenticate_user()) # 登录按钮 login_btn = tk.Button( form_frame, text="登 录", command=self.authenticate_user, # 点击时触发认证方法 font=("黑体", 18, "bold"), bg=BUTTON_BG, # 按钮背景色 fg="white", # 按钮文字颜色 width=15, height=2 ) login_btn.grid(row=2, column=0, columnspan=2, pady=20) # 放置位置跨两列居中 # 系统信息 sys_info = tk.Label( main_frame, text="用户使用时长监测系统 | 仅限授权人员使用", font=("黑体", 10), fg="#bdc3c7", bg=LOGIN_BG ) sys_info.pack(side=tk.BOTTOM, pady=10) def authenticate_user(self): """验证用户身份(首次登录设置密码)""" user_id = self.id_entry.get().strip() # 获取用户id和密码 password = self.pw_entry.get().strip() # 检查是否是管理员登录且用户名单只有管理员 is_only_admin = len(TRAINED_USERS) == 1 and "ADMIN" in TRAINED_USERS # 检查用户是否存在 if user_id in TRAINED_USERS: # 如果是管理员且用户名单为空,直接进入用户管理界面 if is_only_admin and user_id == "ADMIN": self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.manage_users() # 直接进入用户管理 return # 处理首次登录(密码为空) if TRAINED_USERS[user_id] == "": self.set_initial_password(user_id) return # 如果id和密码匹配,则正常启动桌面会话 if TRAINED_USERS[user_id] == password: self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.start_desktop_session() # 开始桌面会话函数 return else: messagebox.showerror("访问拒绝", "输入密码有误,请重新输入!") self.pw_entry.delete(0, tk.END) return # 如果用户名单只有管理员,给出特定提示 if is_only_admin: messagebox.showerror("访问拒绝", "当前只有管理员可以登录系统,请使用管理员账号登录") else: messagebox.showerror("访问拒绝", "未授权操作!禁止访问系统!") # 清空密码框 self.pw_entry.delete(0, tk.END) # 新用户首次登陆设置密码 def set_initial_password(self, user_id): """新用户首次登录设置密码""" dialog = tk.Toplevel(self.root) dialog.title("设置初始密码") dialog.geometry("300x200") dialog.transient(self.root) dialog.grab_set() # 窗口居中设置 screen_width = dialog.winfo_screenwidth() screen_height = dialog.winfo_screenheight() x = (screen_width - 300) // 2 y = (screen_height - 200) // 2 dialog.geometry(f"300x200+{x}+{y}") tk.Label(dialog, text=f"欢迎新用户 {user_id}").pack(pady=(10, 0)) tk.Label(dialog, text="请设置您的初始密码:").pack(pady=(10, 0)) password_entry = tk.Entry(dialog, show="*", width=20) password_entry.pack() tk.Label(dialog, text="确认密码:").pack(pady=(10, 0)) confirm_entry = tk.Entry(dialog, show="*", width=20) confirm_entry.pack() def save_password(): password = password_entry.get().strip() confirm = confirm_entry.get().strip() if not password: messagebox.showerror("错误", "密码不能为空", parent=dialog) return if password != confirm: messagebox.showerror("错误", "两次输入的密码不一致", parent=dialog) return # 更新密码 TRAINED_USERS[user_id] = password save_user_file(TRAINED_USERS, USER_FILE) # 完成设置 dialog.destroy() messagebox.showinfo("成功", "密码设置成功!") # 自动登录 self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.start_desktop_session() tk.Button(dialog, text="保存", command=save_password).pack(pady=10) def start_desktop_session(self): """开始桌面会话""" self.clear_screen() self.root.attributes('-fullscreen', False) # 设置悬浮窗口属性 self.root.overrideredirect(True) # 移除窗口边框和标题栏 self.root.attributes("-topmost", False) # 取消置顶显示 self.root.geometry("300x300") self.root.title(f"使用中 - 用户: {self.current_user}") # 关键修改:设置悬浮窗口背景色和透明度 self.root.configure(bg="#f0f0f0") # 浅灰色背景 # 移除窗口关闭功能 self.root.protocol("WM_DELETE_WINDOW", lambda: None) # 停止可能存在的旧监听器 self.stop_activity_monitoring() # 启动新的监听 self.start_activity_monitoring() # 显示桌面内容 desktop_frame = tk.Frame(self.root, bg="#f0f0f0") # 设置内部框架同样背景色 desktop_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=50) # expand可将组件由其势力范围扩大到扩展范围 # 欢迎信息(简化显示) welcome_msg = tk.Label( desktop_frame, text=f"用户: {self.current_user}", font=("Arial", 14, "bold"), pady=10 ) welcome_msg.pack() # 使用时间显示 self.time_label = tk.Label( desktop_frame, text="使用时间: 00:00", font=("黑体", 10) ) self.time_label.pack(pady=5) # 手动锁定按钮 lock_btn = tk.Button( desktop_frame, text="锁定系统", command=self.lock_system, font=("黑体", 10), bg="#e74c3c", fg="white" ) lock_btn.pack(pady=10) # ⭐管理员按钮组框架 admin_frame = tk.Frame(desktop_frame) admin_frame.pack(pady=5) # 管理员查看记录按钮(仅管理员可见) if self.current_user == "ADMIN": admin_btn = tk.Button( admin_frame, text="查看使用记录", command=self.admin_view, font=("黑体", 10), bg="#F5FFFA", fg="#e74c3c" ) admin_btn.pack(side=tk.LEFT, padx=5) # 新增:管理用户名单按钮(仅管理员可见) manage_users_btn = tk.Button( admin_frame, text="管理用户名单", command=self.manage_users, font=("黑体", 10), bg="#F5FFFA", fg="#e74c3c" ) manage_users_btn.pack(side=tk.LEFT, padx=5) # 启动鼠标键盘事件监听 self.start_activity_monitoring() # 鼠标键盘监听函数 # 开始空闲检测 self.start_idle_monitor() # 开始更新使用时间显示 self.update_usage_time() # 管理员管理培训通过名单⭐ def manage_users(self): """管理员管理用户名单""" # 如果是首次登录(只有管理员),跳过密码验证 if len(TRAINED_USERS) > 1 or "ADMIN" not in TRAINED_USERS: # 验证管理员密码 password = simpledialog.askstring("❗", "请输入管理员密码:", show='*') if password != ADMIN_PASSWORD: messagebox.showerror("认证失败", "密码错误!") return # 如果是首次登录(只有管理员),显示特殊提示 is_only_admin = len(TRAINED_USERS) == 1 and "ADMIN" in TRAINED_USERS self.clear_screen() self.root.title("用户名单管理") self.root.geometry("800x600") # 标题 title = tk.Label( self.root, text="用户名单管理", font=("Arial", 24, "bold"), pady=20, bg=LOGIN_BG, fg="white" ) title.pack() # 如果是首次登录,显示提示信息 if is_only_admin: prompt = tk.Label( self.root, text="首次使用请添加至少一个普通用户账号", font=("黑体", 14), fg="red", pady=10 ) prompt.pack() # 框架容器 container = tk.Frame(self.root) container.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) # both指定容器在X和Y两个方向上填充父容器分配的空间, expand容器会随着父容器的扩大而扩展, pady设置容器的外间距(边距) # 创建Treeview显示用户(只显示工号) columns = ("工号",) # 单元素元组需要加逗号 self.user_tree = ttk.Treeview(container, columns=columns, show="headings", selectmode="browse") # 设置列标题 for col in columns: self.user_tree.heading(col, text=col) self.user_tree.column(col, width=100, anchor=tk.CENTER) # 添加滚动条 scrollbar = ttk.Scrollbar(container, orient=tk.VERTICAL, command=self.user_tree.yview) self.user_tree.configure(yscroll=scrollbar.set) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.user_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 刷新用户列表 self.refresh_user_list() # 按钮框架 btn_frame = tk.Frame(self.root) btn_frame.pack(pady=20) # 添加用户按钮 add_btn = tk.Button( btn_frame, text="添加用户", command=self.add_user, font=("黑体", 12), bg="#2ecc71", fg="white" ) add_btn.pack(side=tk.LEFT, padx=10) # 删除用户按钮 delete_btn = tk.Button( btn_frame, text="删除用户", command=self.delete_user, font=("黑体", 12), bg="#e74c3c", fg="white" ) delete_btn.pack(side=tk.LEFT, padx=10) # 导出用户名单按钮 export_users_btn = tk.Button( btn_frame, text="导出文件", command=self.export_users_csv, font=("黑体", 12), bg=BUTTON_BG, fg="white" ) export_users_btn.pack(side=tk.LEFT, padx=10) # 返回按钮 back_btn = tk.Button( btn_frame, text="返回桌面", command=self.start_desktop_session, font=("黑体", 12), bg=BUTTON_BG, fg="white" ) back_btn.pack(side=tk.LEFT, padx=10) def refresh_user_list(self): """刷新用户列表显示(只显示工号)""" # 清除现有数据 for item in self.user_tree.get_children(): self.user_tree.delete(item) # 添加用户数据(只显示工号) for user_id in TRAINED_USERS.keys(): self.user_tree.insert("", tk.END, values=(user_id,)) def add_user(self): """添加新用户(只添加工号,密码为空)""" dialog = tk.Toplevel(self.root) dialog.title("添加新用户") dialog.geometry("200x150") dialog.transient(self.root) dialog.grab_set() # 窗口剧中设置 screen_width = dialog.winfo_screenwidth() screen_height = dialog.winfo_screenheight() x = (screen_width - 200) // 2 y = (screen_height - 150) // 2 dialog.geometry(f"200x150+{x}+{y}") # 只要求输入工号(不需要密码) tk.Label(dialog, text="工号:").pack(pady=(20, 0)) id_entry = tk.Entry(dialog, width=20) id_entry.pack() # tk.Label(dialog, text="密码:").pack(pady=(10, 0)) # password_entry = tk.Entry(dialog, show="*", width=20) # password_entry.pack() def save_new_user(): user_id = id_entry.get().strip() if not user_id: messagebox.showerror("错误", "工号不能为空", parent=dialog) return if user_id in TRAINED_USERS: messagebox.showerror("错误", "该工号已存在", parent=dialog) return # 添加到全局用户列表(密码初始化为空字符串) TRAINED_USERS[user_id] = "" # 设置初始密码为空 save_user_file(TRAINED_USERS, USER_FILE) # 刷新显示 self.refresh_user_list() dialog.destroy() messagebox.showinfo("成功", f"用户 {user_id} 添加成功") tk.Button(dialog, text="保存", command=save_new_user).pack(pady=20) def delete_user(self): """删除选中用户""" selected = self.user_tree.selection() if not selected: messagebox.showerror("错误", "请先选择一个用户") return item = selected[0] values = self.user_tree.item(item, "values") user_id = values[0] if user_id == "ADMIN": messagebox.showerror("错误", "不能删除管理员账户") return if messagebox.askyesno("确认", f"确定要删除用户 {user_id} 吗?"): # 从全局用户列表中删除 if user_id in TRAINED_USERS: del TRAINED_USERS[user_id] save_user_file(TRAINED_USERS, USER_FILE) self.refresh_user_list() messagebox.showinfo("成功", f"用户 {user_id} 已删除") def update_usage_time(self): """更新使用时间显示""" if not self.is_locked: # 如果没有锁 usage_seconds = time.time() - self.login_time minutes, seconds = divmod(int(usage_seconds), 60) self.time_label.config(text=f"使用时间: {minutes:02d}:{seconds:02d}") self.root.after(1000, self.update_usage_time) # 1s后更新时间显示 def start_activity_monitoring(self): """启动鼠标键盘事件监听""" # 如果已有监听器在运行,先停止 if self.listening: self.stop_activity_monitoring() # 创建新的监听器实例 self.mouse_listener = mouse.Listener(on_move=self.activity_detected) self.keyboard_listener = keyboard.Listener(on_press=self.activity_detected) # 启动线程 mouse_thread = threading.Thread(target=self.mouse_listener.start) keyboard_thread = threading.Thread(target=self.keyboard_listener.start) mouse_thread.daemon = True keyboard_thread.daemon = True mouse_thread.start() keyboard_thread.start() self.listening = True def stop_activity_monitoring(self): """停止鼠标键盘事件监听""" if self.listening: if self.mouse_listener: self.mouse_listener.stop() if self.keyboard_listener: self.keyboard_listener.stop() self.mouse_listener = None self.keyboard_listener = None self.listening = False def activity_detected(self, *args): """检测到用户活动""" if not self.is_locked: # 系统未被锁定 self.last_activity = time.time() # 更新最后活动时间 # ⭐ 修改点1:确保在倒计时期间检测到活动时重新启动监听 if self.countdown_timer: # 如果倒计时正在进行 self.root.after_cancel(self.countdown_timer) self.countdown_timer = None # 重新启动桌面会话和监听 self.stop_activity_monitoring() # 先停止当前监听 self.clear_screen() self.start_desktop_session() # 这会重新启动监听 def start_idle_monitor(self): """监控空闲状态""" if not self.is_locked: idle_time = time.time() - self.last_activity if idle_time > IDLE_THRESHOLD: # 空闲超时 self.start_lock_countdown() else: # 每秒检查一次 self.idle_timer = self.root.after(1000, self.start_idle_monitor) def start_lock_countdown(self): """开始锁屏倒计时""" # 关键修改:在倒计时开始前恢复全屏模式 self.root.overrideredirect(False) # 恢复窗口边框 self.root.attributes('-fullscreen', True) # 设置全屏 self.clear_screen() self.root.configure(bg=LOCK_SCREEN_BG) # self.root.attributes('-fullscreen', True) # 倒计时显示 self.countdown = COUNTDOWN_DURATION self.countdown_label = tk.Label( self.root, text=f"系统将在 {self.countdown} 秒后锁定...", font=("Arial", 36, "bold"), fg="red", bg=LOCK_SCREEN_BG ) self.countdown_label.place(relx=0.5, rely=0.4, anchor=tk.CENTER) # 提示信息 prompt = tk.Label( self.root, text="检测到系统空闲,移动鼠标或按键取消锁定", font=("Arial", 20), fg="#3498db", bg=LOCK_SCREEN_BG ) prompt.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 用户信息 user_info = tk.Label( self.root, text=f"当前用户: {self.current_user}", font=("Arial", 16), fg="white", bg=LOCK_SCREEN_BG ) user_info.place(relx=0.5, rely=0.6, anchor=tk.CENTER) # 开始倒计时 self.update_countdown() def update_countdown(self): """更新倒计时显示""" self.countdown -= 1 self.countdown_label.config(text=f"系统将在 {self.countdown} 秒后锁定...") if self.countdown <= 0: self.lock_system() else: # 每秒检查一次 self.countdown_timer = self.root.after(1000, self.update_countdown) def lock_system(self, manual=False): """锁定系统并保存使用记录""" logout_time = time.time() usage_seconds = logout_time - self.login_time # 格式化使用时间 minutes, seconds = divmod(int(usage_seconds), 60) usage_time = f"{minutes:02d}:{seconds:02d}" # 创建记录 record = { "user_id": self.current_user, "login_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.login_time)), "logout_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(logout_time)), "usage_duration": usage_time } # 加密保存记录 self.save_usage_record(record) # 停止监听和计时器 if self.idle_timer: self.root.after_cancel(self.idle_timer) if self.countdown_timer: self.root.after_cancel(self.countdown_timer) self.stop_activity_monitoring() # 重置用户状态 self.current_user = None self.is_locked = True # 关键修改:在锁定时恢复全屏模式 self.root.overrideredirect(False) # 恢复窗口边框 self.root.attributes('-fullscreen', True) # 设置全屏 # 显示登录界面 self.show_login_screen() def save_usage_record(self, record): """加密保存使用记录""" # 生成或加载加密密钥 if not os.path.exists(KEY_FILE): # 检查密钥文件是否存在 key = Fernet.generate_key() # 若不存在(首次运行),Fernet.generate_key()生成一个新的对称加密密钥(256位),并以二进制模式写入文件 with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: # 如果已存在,直接读取密钥内容 key = f.read() cipher = Fernet(key) # 创建Fernet加密器实例.Fernet是AES加密的封装库,提供简单易用的对称加密功能 # 读取已有记录,避免新记录覆盖旧数据 records = [] if os.path.exists(LOG_FILE): # 检查日志文件是否存在 with open(LOG_FILE, "rb") as f: # 如果存在,以二进制模式读取加密数据 encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) # 使用cipher.decrypt()解密 records = json.loads(decrypted_data) # 通过json.loads()将JSON字符串解析为Python列表 # 添加新记录 records.append(record) # 加密保存 encrypted_data = cipher.encrypt(json.dumps(records).encode()) # 将整个记录列表重新加密 with open(LOG_FILE, "wb") as f: f.write(encrypted_data) # 管理员管理使用记录⭐ def admin_view(self): """管理员查看使用记录""" password = simpledialog.askstring("❗", "请输入管理员密码:", show='*') if password == ADMIN_PASSWORD: records = self.get_usage_records() # 获取使用记录函数 self.display_records(records) # 显示使用记录函数 else: messagebox.showerror("认证失败", "密码错误!") # 密码框 def get_usage_records(self): """获取使用记录(解密)""" if not os.path.exists(KEY_FILE) or not os.path.exists(LOG_FILE): return [] try: with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) with open(LOG_FILE, "rb") as f: encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) return json.loads(decrypted_data) except: return [] def display_records(self, records): """显示使用记录""" self.clear_screen() self.root.title("使用记录 - 管理员视图") self.root.geometry("1000x700") # 标题 title = tk.Label( self.root, text="电脑使用记录 - 安全报告", font=("Arial", 24, "bold"), pady=20, bg=LOGIN_BG, fg="white" ) title.pack() # 滚动文本框 text_area = scrolledtext.ScrolledText( self.root, wrap=tk.WORD, font=("Consolas", 12), width=120, height=30 ) text_area.pack(padx=20, pady=3, fill=tk.BOTH, expand=True) # 添加表头 header = "工号 登录时间 登出时间 使用时间\n" text_area.insert(tk.INSERT, header) text_area.insert(tk.INSERT, "-" * 65 + "\n") # 添加记录 for record in records: line = ( f"{record['user_id']:<8} " f"{record['login_time']:<20} " f"{record['logout_time']:<20} " f"{record['usage_duration']:<8}\n" # f"{record.get('lock_type', '自动'):<8}\n" ) text_area.insert(tk.INSERT, line) text_area.configure(state='disabled') # 设为只读 # 添加返回桌面按钮 btn_frame = tk.Frame(self.root) btn_frame.pack(pady=5) # pady参数在垂直方向(上下)添加10像素的填充,确保与其他界面元素有足够的间距 # 导出记录按钮 tk.Button( btn_frame, text="导出文件", command=lambda: self.export_records_csv(records), font=("黑体", 12), bg=BUTTON_BG, fg="white", width=15 ).pack(side=tk.LEFT, padx=10) # 返回按钮 tk.Button( btn_frame, text="返回桌面", command=self.start_desktop_session, font=("黑体", 12), bg=BUTTON_BG, fg="white", width=15 ).pack(side=tk.LEFT, padx=10) # # tk.Button( # btn_frame, # text="返回登录界面", # command=self.show_login_screen, # font=("黑体", 12), # bg=BUTTON_BG, # fg="white", # width=20 # ).pack(side=tk.LEFT, padx=10) # 将使用记录和人员名单导出 def export_records_csv(self, records): """导出使用记录为CSV文件""" try: # 获取当前时间作为文件名 timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) filename = f"使用记录_{timestamp}.csv" with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) # 写入表头 writer.writerow(["工号", "登录时间", "登出时间", "使用时间"]) # 写入每条记录 for record in records: writer.writerow([ record['user_id'], record['login_time'], record['logout_time'], record['usage_duration'] ]) # ⭐导出为excel表格 # try: # # 获取当前时间作为文件名 # timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) # filename = f"使用记录_{timestamp}.xlsx" # # # 创建DataFrame # data = { # "工号": [record['user_id'] for record in records], # "登录时间": [record['login_time'] for record in records], # "登出时间": [record['logout_time'] for record in records], # "使用时间": [record['usage_duration'] for record in records] # } # df = pd.DataFrame(data) # # # 导出到Excel # df.to_excel(filename, index=False, engine='openpyxl') # messagebox.showinfo("导出成功", f"使用记录已导出到: {os.path.abspath(filename)}") except Exception as e: messagebox.showerror("导出失败", f"导出使用记录时出错: {str(e)}") def export_users_csv(self): """导出用户名单为CSV文件(解密后导出只包含工号)""" try: # 获取当前时间作为文件名 timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) filename = f"用户名单_{timestamp}.csv" with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) # 写入表头(只包含工号) writer.writerow(["工号"]) # 写入每条记录(只导出工号) for user_id in TRAINED_USERS.keys(): writer.writerow([user_id]) messagebox.showinfo("导出成功", f"用户名单已解密并导出到: {os.path.abspath(filename)}") except Exception as e: messagebox.showerror("导出失败", f"导出用户名单时出错: {str(e)}") """导出excel文件""" # try: # timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) # filename = f"用户名单_{timestamp}.xlsx" # # # 创建dataframe # data = [] # for user_id, password in TRAINED_USERS.items(): # data.append([user_id, password]) # df = pd.DataFrame(data, columns=["工号", "密码"]) # # # 导出到excel # df.to_excel(filename, index=False, engine='openpyxl') def clear_screen(self): """清除当前屏幕所有内容""" for widget in self.root.winfo_children(): widget.destroy() def lock_on_close(self): """关闭窗口时锁定系统""" if self.current_user: self.lock_system(manual=True) self.root.destroy() # 启动系统 if __name__ == "__main__": app = SecureDesktopMonitor() # 绑定窗口关闭事件 app.root.protocol("WM_DELETE_WINDOW", app.lock_on_close)
最新发布
11-01
基于以下代码修改以实现,即使将"开始桌面会话"这一窗口最小化到任务栏也可以实现锁屏。因为目前的情况是缩小到任务栏后超过闲置时间阈值也无法正常锁屏,必须要保持显示才可以,这种情况并不满足要求。请修改,只用给出修改部分代码即可。 import tkinter as tk from tkinter import simpledialog, messagebox, scrolledtext, ttk import time import threading import json import os import platform from cryptography.fernet import Fernet from pynput import mouse, keyboard import sys import subprocess import csv import pandas as pd # 配置参数(常量使用大写) IDLE_THRESHOLD = 3 * 60 # 3分钟空闲时间(秒) COUNTDOWN_DURATION = 10 # 锁屏倒计时(秒) KEY_FILE = "system_secret.key" # 加密密钥文件名 LOG_FILE = "secure_usage_log.log" # 操作日志文件路径 ADMIN_PASSWORD = "123" # 管理员密码 LOCK_SCREEN_BG = "#000000" # 锁屏背景色(黑色) LOGIN_BG = "#2c3e50" # 登录界面背景色(深蓝色) BUTTON_BG = "#3498db" # 按钮背景色(亮蓝色) USER_FILE = r"C:\Users\x00708\PycharmProjects\mission\practice_from_work_pycharm\lock monitor\培训通过人员名单.csv" # 用户名单文件路径 USER_MANAGEMENT_PASSWORD = ADMIN_PASSWORD # 用户管理密码设置为管理员密码 # 培训通过人员名单(工号:密码) # 修改全局用户加载逻辑 def load_trained_users(file_path): """ 从CSV文件加载培训通过人员名单 文件格式要求:工号,密码 返回字典 {工号: 密码} """ users = {} try: # 检查文件是否存在 if not os.path.exists(file_path): print(f"用户文件 {file_path} 不存在,创建初始管理员账号") # 创建只包含管理员的初始用户名单 users = {"ADMIN": ADMIN_PASSWORD} # 保存加密的用户名单 save_user_file(users, file_path) return users # 加载加密密钥 if not os.path.exists(KEY_FILE): key = Fernet.generate_key() with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) # 解密用户数据 with open(file_path, "rb") as f: encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) return json.loads(decrypted_data) except Exception as e: print(f"加载用户名单错误: {str(e)}") # 返回只包含管理员的用户名单 return {"ADMIN": ADMIN_PASSWORD} # 添加保存用户文件的函数 def save_user_file(users, file_path): """保存用户字典到CSV文件""" try: # 确保密钥存在 if not os.path.exists(KEY_FILE): key = Fernet.generate_key() with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) # 加密用户数据 encrypted_data = cipher.encrypt(json.dumps(users).encode()) with open(file_path, "wb") as f: f.write(encrypted_data) return True except Exception as e: print(f"保存用户文件时出错: {str(e)}") return False # 从文件加载培训通过人员名单 TRAINED_USERS = load_trained_users(USER_FILE) # 确保管理员账户存在 if "ADMIN" not in TRAINED_USERS: TRAINED_USERS["ADMIN"] = ADMIN_PASSWORD save_user_file(TRAINED_USERS, USER_FILE) class SecureDesktopMonitor: def __init__(self): # 创建主窗口 self.root = tk.Tk() # 创建主窗口对象 self.root.title("安全桌面监控系统") # 设置窗口标题 self.root.geometry("800x600") # 初始窗口尺寸 self.root.configure(bg=LOGIN_BG) # 设置背景色 # 设置窗口在最顶层 self.root.attributes("-topmost", True) # 系统状态变量,防止用户绕过监控 self.current_user = None # 当前登录用户(未登录时为None) self.login_time = 0 # 登陆时间(初始为0) self.last_activity = time.time() # 最后活动时间(初始化为当前时间) self.idle_timer = None # 空闲检测定时器(用于推迟锁屏) self.countdown_timer = None # 倒计时定时器(锁屏前提示) self.listening = False # 监听器状态标志 self.is_locked = False # 界面锁屏状态(初始为True,即未登录时锁定) # 创建鼠标键盘监听器 self.mouse_listener = mouse.Listener(on_move=self.activity_detected) # 鼠标移动触发活动检测 self.keyboard_listener = keyboard.Listener(on_press=self.activity_detected) # 键盘按键触发活动检测 # 显示初始登录界面 self.show_login_screen() # Tkinter事件循环(阻塞式,保持程序运行),mainloop()是Tkinter的核心,用于处理用户事件(如点击、输入) self.root.mainloop() def show_login_screen(self): """显示登录界面""" self.clear_screen() # 清空当前屏幕所有内容 self.is_locked = True # 标记系统为锁定状态 # 设置全屏模式(防止用户切换窗口) self.root.attributes('-fullscreen', True) # 创建居中主框架 main_frame = tk.Frame(self.root, bg=LOGIN_BG) # bg颜色深蓝色 main_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 使用place布局精确居中 # 系统标题标签 title = tk.Label( main_frame, text="安全桌面登录", font=("Arial", 24, "bold"), # 字体族、大小、粗体 fg="white", # 前景色(白色) bg=LOGIN_BG # 背景色与界面一致 ) title.pack(pady=20) # 打包布局,垂直间距20像素 # 登录表单框架(使用grid布局对齐输入框) form_frame = tk.Frame(main_frame, bg=LOGIN_BG) form_frame.pack(pady=20) # 工号输入标签和输入框 tk.Label( form_frame, # 框架格式 text="工号:", font=("黑体", 14), fg="white", bg=LOGIN_BG ).grid(row=0, column=0, padx=10, pady=10, sticky="e") self.id_entry = tk.Entry(form_frame, font=("Arial", 14), width=20) # 单行文本输入框 self.id_entry.grid(row=0, column=1, padx=10, pady=10) self.id_entry.focus_set() # 自动聚焦到工号输入框(提升用户体验) # 密码输入标签和输入框(内容显示为*号) tk.Label( form_frame, text="密码:", font=("黑体", 14), fg="white", bg=LOGIN_BG ).grid(row=1, column=0, padx=10, pady=10, sticky="e") self.pw_entry = tk.Entry(form_frame, show="*", font=("Arial", 14), width=20) # show="*" 隐藏输入 self.pw_entry.grid(row=1, column=1, padx=10, pady=10) # 放置位置 # 绑定回车键(用户输入密码后按回车可直接登录) self.pw_entry.bind("<Return>", lambda event: self.authenticate_user()) # 登录按钮 login_btn = tk.Button( form_frame, text="登 录", command=self.authenticate_user, # 点击时触发认证方法 font=("黑体", 18, "bold"), bg=BUTTON_BG, # 按钮背景色 fg="white", # 按钮文字颜色 width=15, height=2 ) login_btn.grid(row=2, column=0, columnspan=2, pady=20) # 放置位置跨两列居中 # 系统信息 sys_info = tk.Label( main_frame, text="用户使用时长监测系统 | 仅限授权人员使用", font=("黑体", 10), fg="#bdc3c7", bg=LOGIN_BG ) sys_info.pack(side=tk.BOTTOM, pady=10) def authenticate_user(self): """验证用户身份(首次登录设置密码)""" user_id = self.id_entry.get().strip() # 获取用户id和密码 password = self.pw_entry.get().strip() # 检查是否是管理员登录且用户名单只有管理员 is_only_admin = len(TRAINED_USERS) == 1 and "ADMIN" in TRAINED_USERS # 检查用户是否存在 if user_id in TRAINED_USERS: # 如果是管理员且用户名单为空,直接进入用户管理界面 if is_only_admin and user_id == "ADMIN": self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.manage_users() # 直接进入用户管理 return # 处理首次登录(密码为空) if TRAINED_USERS[user_id] == "": self.set_initial_password(user_id) return # 如果id和密码匹配,则正常启动桌面会话 if TRAINED_USERS[user_id] == password: self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.start_desktop_session() # 开始桌面会话函数 return else: messagebox.showerror("访问拒绝", "输入密码有误,请重新输入!") self.pw_entry.delete(0, tk.END) return # 如果用户名单只有管理员,给出特定提示 if is_only_admin: messagebox.showerror("访问拒绝", "当前只有管理员可以登录系统,请使用管理员账号登录") else: messagebox.showerror("访问拒绝", "未授权操作!禁止访问系统!") # 清空密码框 self.pw_entry.delete(0, tk.END) # 新用户首次登陆设置密码 def set_initial_password(self, user_id): """新用户首次登录设置密码""" dialog = tk.Toplevel(self.root) dialog.title("设置初始密码") dialog.geometry("300x200") dialog.transient(self.root) dialog.grab_set() # 窗口居中设置 screen_width = dialog.winfo_screenwidth() screen_height = dialog.winfo_screenheight() x = (screen_width - 300) // 2 y = (screen_height - 200) // 2 dialog.geometry(f"300x200+{x}+{y}") tk.Label(dialog, text=f"欢迎新用户 {user_id}").pack(pady=(10, 0)) tk.Label(dialog, text="请设置您的初始密码:").pack(pady=(10, 0)) password_entry = tk.Entry(dialog, show="*", width=20) password_entry.pack() tk.Label(dialog, text="确认密码:").pack(pady=(10, 0)) confirm_entry = tk.Entry(dialog, show="*", width=20) confirm_entry.pack() def save_password(): password = password_entry.get().strip() confirm = confirm_entry.get().strip() if not password: messagebox.showerror("错误", "密码不能为空", parent=dialog) return if password != confirm: messagebox.showerror("错误", "两次输入的密码不一致", parent=dialog) return # 更新密码 TRAINED_USERS[user_id] = password save_user_file(TRAINED_USERS, USER_FILE) # 完成设置 dialog.destroy() messagebox.showinfo("成功", "密码设置成功!") # 自动登录 self.current_user = user_id self.login_time = time.time() self.last_activity = self.login_time self.is_locked = False self.start_desktop_session() tk.Button(dialog, text="保存", command=save_password).pack(pady=10) def start_desktop_session(self): """开始桌面会话""" self.clear_screen() self.root.attributes('-fullscreen', False) self.root.geometry("400x400") self.root.title(f"使用中 - 用户: {self.current_user}") # 忽略窗口关闭事件 self.root.protocol("WM_DELETE_WINDOW", self.handle_window_close) # 停止可能存在的旧监听器 self.stop_activity_monitoring() # 启动新的监听 self.start_activity_monitoring() # 显示桌面内容 desktop_frame = tk.Frame(self.root) desktop_frame.pack(fill=tk.BOTH, expand=True) # expand可将组件由其势力范围扩大到扩展范围 # 欢迎信息 welcome_msg = tk.Label( desktop_frame, text=f"欢迎, {self.current_user}", font=("Arial", 22, "bold"), pady=40 ) welcome_msg.pack() # 状态信息 status_text = tk.Label( desktop_frame, text="桌面已解锁 - 工作中...", font=("黑体", 14), fg="green" ) status_text.pack(pady=15) # 使用时间显示 self.time_label = tk.Label( desktop_frame, text="使用时间: 00:00", font=("黑体", 12) ) self.time_label.pack(pady=10) # 手动锁定按钮 lock_btn = tk.Button( desktop_frame, text="锁定系统", command=self.lock_system, # 调用锁定系统函数 font=("黑体", 12), bg="#e74c3c", fg="white" ) lock_btn.pack(pady=20) # lock_btn为变量名对象,pack为tkinter的几何布局管理器,可自动排列控件 # ⭐管理员按钮组框架 admin_frame = tk.Frame(desktop_frame) admin_frame.pack(pady=10) # 管理员查看记录按钮(仅管理员可见) if self.current_user == "ADMIN": admin_btn = tk.Button( admin_frame, text="管理员查看记录", command=self.admin_view, font=("黑体", 12), bg="#F5FFFA", fg="#e74c3c" ) admin_btn.pack(side=tk.LEFT, padx=5) # 新增:管理用户名单按钮(仅管理员可见) manage_users_btn = tk.Button( admin_frame, text="管理用户名单", command=self.manage_users, font=("黑体", 12), bg="#F5FFFA", fg="#e74c3c" ) manage_users_btn.pack(side=tk.LEFT, padx=5) # 启动鼠标键盘事件监听 self.start_activity_monitoring() # 鼠标键盘监听函数 # 开始空闲检测 self.start_idle_monitor() # 开始更新使用时间显示 self.update_usage_time() def handle_window_close(self): """处理窗口关闭事件 - 忽略关闭尝试并显示警告""" messagebox.showwarning("操作受限", "请使用\"锁定系统\"按钮退出桌面会话") # 管理员管理培训通过名单⭐ def manage_users(self): """管理员管理用户名单""" # 如果是首次登录(只有管理员),跳过密码验证 if len(TRAINED_USERS) > 1 or "ADMIN" not in TRAINED_USERS: # 验证管理员密码 password = simpledialog.askstring("❗", "请输入管理员密码:", show='*') if password != ADMIN_PASSWORD: messagebox.showerror("认证失败", "密码错误!") return # 如果是首次登录(只有管理员),显示特殊提示 is_only_admin = len(TRAINED_USERS) == 1 and "ADMIN" in TRAINED_USERS self.clear_screen() self.root.title("用户名单管理") self.root.geometry("800x600") # 标题 title = tk.Label( self.root, text="用户名单管理", font=("Arial", 24, "bold"), pady=20, bg=LOGIN_BG, fg="white" ) title.pack() # 如果是首次登录,显示提示信息 if is_only_admin: prompt = tk.Label( self.root, text="首次使用请添加至少一个普通用户账号", font=("黑体", 14), fg="red", pady=10 ) prompt.pack() # 框架容器 container = tk.Frame(self.root) container.pack(fill=tk.BOTH, expand=True, padx=20, pady=10) # both指定容器在X和Y两个方向上填充父容器分配的空间, expand容器会随着父容器的扩大而扩展, pady设置容器的外间距(边距) # 创建Treeview显示用户(只显示工号) columns = ("工号",) # 单元素元组需要加逗号 self.user_tree = ttk.Treeview(container, columns=columns, show="headings", selectmode="browse") # 设置列标题 for col in columns: self.user_tree.heading(col, text=col) self.user_tree.column(col, width=100, anchor=tk.CENTER) # 添加滚动条 scrollbar = ttk.Scrollbar(container, orient=tk.VERTICAL, command=self.user_tree.yview) self.user_tree.configure(yscroll=scrollbar.set) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.user_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 刷新用户列表 self.refresh_user_list() # 按钮框架 btn_frame = tk.Frame(self.root) btn_frame.pack(pady=20) # 添加用户按钮 add_btn = tk.Button( btn_frame, text="添加用户", command=self.add_user, font=("黑体", 12), bg="#2ecc71", fg="white" ) add_btn.pack(side=tk.LEFT, padx=10) # 删除用户按钮 delete_btn = tk.Button( btn_frame, text="删除用户", command=self.delete_user, font=("黑体", 12), bg="#e74c3c", fg="white" ) delete_btn.pack(side=tk.LEFT, padx=10) # 导出用户名单按钮 export_users_btn = tk.Button( btn_frame, text="导出文件", command=self.export_users_csv, font=("黑体", 12), bg=BUTTON_BG, fg="white" ) export_users_btn.pack(side=tk.LEFT, padx=10) # 返回按钮 back_btn = tk.Button( btn_frame, text="返回桌面", command=self.start_desktop_session, font=("黑体", 12), bg=BUTTON_BG, fg="white" ) back_btn.pack(side=tk.LEFT, padx=10) def refresh_user_list(self): """刷新用户列表显示(只显示工号)""" # 清除现有数据 for item in self.user_tree.get_children(): self.user_tree.delete(item) # 添加用户数据(只显示工号) for user_id in TRAINED_USERS.keys(): self.user_tree.insert("", tk.END, values=(user_id,)) def add_user(self): """添加新用户(只添加工号,密码为空)""" dialog = tk.Toplevel(self.root) dialog.title("添加新用户") dialog.geometry("300x200") dialog.transient(self.root) dialog.grab_set() # 窗口剧中设置 screen_width = dialog.winfo_screenwidth() screen_height = dialog.winfo_screenheight() x = (screen_width - 300) // 2 y = (screen_height - 200) // 2 dialog.geometry(f"300x200+{x}+{y}") # 只要求输入工号(不需要密码) tk.Label(dialog, text="工号:").pack(pady=(10, 0)) id_entry = tk.Entry(dialog, width=20) id_entry.pack() # tk.Label(dialog, text="密码:").pack(pady=(10, 0)) # password_entry = tk.Entry(dialog, show="*", width=20) # password_entry.pack() def save_new_user(): user_id = id_entry.get().strip() if not user_id: messagebox.showerror("错误", "工号不能为空", parent=dialog) return if user_id in TRAINED_USERS: messagebox.showerror("错误", "该工号已存在", parent=dialog) return # 添加到全局用户列表(密码初始化为空字符串) TRAINED_USERS[user_id] = "" # 设置初始密码为空 save_user_file(TRAINED_USERS, USER_FILE) # 刷新显示 self.refresh_user_list() dialog.destroy() messagebox.showinfo("成功", f"用户 {user_id} 添加成功") tk.Button(dialog, text="保存", command=save_new_user).pack(pady=20) def delete_user(self): """删除选中用户""" selected = self.user_tree.selection() if not selected: messagebox.showerror("错误", "请先选择一个用户") return item = selected[0] values = self.user_tree.item(item, "values") user_id = values[0] if user_id == "ADMIN": messagebox.showerror("错误", "不能删除管理员账户") return if messagebox.askyesno("确认", f"确定要删除用户 {user_id} 吗?"): # 从全局用户列表中删除 if user_id in TRAINED_USERS: del TRAINED_USERS[user_id] save_user_file(TRAINED_USERS, USER_FILE) self.refresh_user_list() messagebox.showinfo("成功", f"用户 {user_id} 已删除") def update_usage_time(self): """更新使用时间显示""" if not self.is_locked: # 如果没有锁 usage_seconds = time.time() - self.login_time minutes, seconds = divmod(int(usage_seconds), 60) self.time_label.config(text=f"使用时间: {minutes:02d}:{seconds:02d}") self.root.after(1000, self.update_usage_time) # 1s后更新时间显示 def start_activity_monitoring(self): """启动鼠标键盘事件监听""" # 如果已有监听器在运行,先停止 if self.listening: self.stop_activity_monitoring() # 创建新的监听器实例 self.mouse_listener = mouse.Listener(on_move=self.activity_detected) self.keyboard_listener = keyboard.Listener(on_press=self.activity_detected) # 启动线程 mouse_thread = threading.Thread(target=self.mouse_listener.start) keyboard_thread = threading.Thread(target=self.keyboard_listener.start) mouse_thread.daemon = True keyboard_thread.daemon = True mouse_thread.start() keyboard_thread.start() self.listening = True def stop_activity_monitoring(self): """停止鼠标键盘事件监听""" if self.listening: if self.mouse_listener: self.mouse_listener.stop() if self.keyboard_listener: self.keyboard_listener.stop() self.mouse_listener = None self.keyboard_listener = None self.listening = False def activity_detected(self, *args): """检测到用户活动""" if not self.is_locked: # 系统未被锁定 self.last_activity = time.time() # 更新最后活动时间 # ⭐ 修改点1:确保在倒计时期间检测到活动时重新启动监听 if self.countdown_timer: # 如果倒计时正在进行 self.root.after_cancel(self.countdown_timer) self.countdown_timer = None # 重新启动桌面会话和监听 self.stop_activity_monitoring() # 先停止当前监听 self.clear_screen() self.start_desktop_session() # 这会重新启动监听 def start_idle_monitor(self): """监控空闲状态""" if not self.is_locked: idle_time = time.time() - self.last_activity if idle_time > IDLE_THRESHOLD: # 空闲超时 self.start_lock_countdown() else: # 每秒检查一次 self.idle_timer = self.root.after(1000, self.start_idle_monitor) def start_lock_countdown(self): """开始锁屏倒计时""" self.clear_screen() self.root.configure(bg=LOCK_SCREEN_BG) self.root.attributes('-fullscreen', True) # 倒计时显示 self.countdown = COUNTDOWN_DURATION self.countdown_label = tk.Label( self.root, text=f"系统将在 {self.countdown} 秒后锁定...", font=("Arial", 36, "bold"), fg="red", bg=LOCK_SCREEN_BG ) self.countdown_label.place(relx=0.5, rely=0.4, anchor=tk.CENTER) # 提示信息 prompt = tk.Label( self.root, text="检测到系统空闲,移动鼠标或按键取消锁定", font=("Arial", 20), fg="#3498db", bg=LOCK_SCREEN_BG ) prompt.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 用户信息 user_info = tk.Label( self.root, text=f"当前用户: {self.current_user}", font=("Arial", 16), fg="white", bg=LOCK_SCREEN_BG ) user_info.place(relx=0.5, rely=0.6, anchor=tk.CENTER) # 开始倒计时 self.update_countdown() def update_countdown(self): """更新倒计时显示""" self.countdown -= 1 self.countdown_label.config(text=f"系统将在 {self.countdown} 秒后锁定...") if self.countdown <= 0: self.lock_system() else: # 每秒检查一次 self.countdown_timer = self.root.after(1000, self.update_countdown) def lock_system(self, manual=False): """锁定系统并保存使用记录""" logout_time = time.time() usage_seconds = logout_time - self.login_time # 格式化使用时间 minutes, seconds = divmod(int(usage_seconds), 60) usage_time = f"{minutes:02d}:{seconds:02d}" # 创建记录 record = { "user_id": self.current_user, "login_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.login_time)), "logout_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(logout_time)), "usage_duration": usage_time } # 加密保存记录 self.save_usage_record(record) # 停止监听和计时器 if self.idle_timer: self.root.after_cancel(self.idle_timer) if self.countdown_timer: self.root.after_cancel(self.countdown_timer) self.stop_activity_monitoring() # 重置用户状态 self.current_user = None self.is_locked = True # 显示登录界面 self.show_login_screen() def save_usage_record(self, record): """加密保存使用记录""" # 生成或加载加密密钥 if not os.path.exists(KEY_FILE): # 检查密钥文件是否存在 key = Fernet.generate_key() # 若不存在(首次运行),Fernet.generate_key()生成一个新的对称加密密钥(256位),并以二进制模式写入文件 with open(KEY_FILE, "wb") as f: f.write(key) with open(KEY_FILE, "rb") as f: # 如果已存在,直接读取密钥内容 key = f.read() cipher = Fernet(key) # 创建Fernet加密器实例.Fernet是AES加密的封装库,提供简单易用的对称加密功能 # 读取已有记录,避免新记录覆盖旧数据 records = [] if os.path.exists(LOG_FILE): # 检查日志文件是否存在 with open(LOG_FILE, "rb") as f: # 如果存在,以二进制模式读取加密数据 encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) # 使用cipher.decrypt()解密 records = json.loads(decrypted_data) # 通过json.loads()将JSON字符串解析为Python列表 # 添加新记录 records.append(record) # 加密保存 encrypted_data = cipher.encrypt(json.dumps(records).encode()) # 将整个记录列表重新加密 with open(LOG_FILE, "wb") as f: f.write(encrypted_data) # 管理员管理使用记录⭐ def admin_view(self): """管理员查看使用记录""" password = simpledialog.askstring("❗", "请输入管理员密码:", show='*') if password == ADMIN_PASSWORD: records = self.get_usage_records() # 获取使用记录函数 self.display_records(records) # 显示使用记录函数 else: messagebox.showerror("认证失败", "密码错误!") # 密码框 def get_usage_records(self): """获取使用记录(解密)""" if not os.path.exists(KEY_FILE) or not os.path.exists(LOG_FILE): return [] try: with open(KEY_FILE, "rb") as f: key = f.read() cipher = Fernet(key) with open(LOG_FILE, "rb") as f: encrypted_data = f.read() decrypted_data = cipher.decrypt(encrypted_data) return json.loads(decrypted_data) except: return [] def display_records(self, records): """显示使用记录""" self.clear_screen() self.root.title("使用记录 - 管理员视图") self.root.geometry("1000x700") # 标题 title = tk.Label( self.root, text="电脑使用记录 - 安全报告", font=("Arial", 24, "bold"), pady=20, bg=LOGIN_BG, fg="white" ) title.pack() # 滚动文本框 text_area = scrolledtext.ScrolledText( self.root, wrap=tk.WORD, font=("Consolas", 12), width=120, height=30 ) text_area.pack(padx=20, pady=3, fill=tk.BOTH, expand=True) # 添加表头 header = "工号 登录时间 登出时间 使用时间\n" text_area.insert(tk.INSERT, header) text_area.insert(tk.INSERT, "-" * 65 + "\n") # 添加记录 for record in records: line = ( f"{record['user_id']:<8} " f"{record['login_time']:<20} " f"{record['logout_time']:<20} " f"{record['usage_duration']:<8}\n" # f"{record.get('lock_type', '自动'):<8}\n" ) text_area.insert(tk.INSERT, line) text_area.configure(state='disabled') # 设为只读 # 添加返回桌面按钮 btn_frame = tk.Frame(self.root) btn_frame.pack(pady=5) # pady参数在垂直方向(上下)添加10像素的填充,确保与其他界面元素有足够的间距 # 导出记录按钮 tk.Button( btn_frame, text="导出文件", command=lambda: self.export_records_csv(records), font=("黑体", 12), bg=BUTTON_BG, fg="white", width=15 ).pack(side=tk.LEFT, padx=10) # 返回按钮 tk.Button( btn_frame, text="返回桌面", command=self.start_desktop_session, font=("黑体", 12), bg=BUTTON_BG, fg="white", width=15 ).pack(side=tk.LEFT, padx=10) # # tk.Button( # btn_frame, # text="返回登录界面", # command=self.show_login_screen, # font=("黑体", 12), # bg=BUTTON_BG, # fg="white", # width=20 # ).pack(side=tk.LEFT, padx=10) # 将使用记录和人员名单导出 def export_records_csv(self, records): """导出使用记录为CSV文件""" try: # 获取当前时间作为文件名 timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) filename = f"使用记录_{timestamp}.csv" with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) # 写入表头 writer.writerow(["工号", "登录时间", "登出时间", "使用时间"]) # 写入每条记录 for record in records: writer.writerow([ record['user_id'], record['login_time'], record['logout_time'], record['usage_duration'] ]) # ⭐导出为excel表格 # try: # # 获取当前时间作为文件名 # timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) # filename = f"使用记录_{timestamp}.xlsx" # # # 创建DataFrame # data = { # "工号": [record['user_id'] for record in records], # "登录时间": [record['login_time'] for record in records], # "登出时间": [record['logout_time'] for record in records], # "使用时间": [record['usage_duration'] for record in records] # } # df = pd.DataFrame(data) # # # 导出到Excel # df.to_excel(filename, index=False, engine='openpyxl') # messagebox.showinfo("导出成功", f"使用记录已导出到: {os.path.abspath(filename)}") except Exception as e: messagebox.showerror("导出失败", f"导出使用记录时出错: {str(e)}") def export_users_csv(self): """导出用户名单为CSV文件(解密后导出只包含工号)""" try: # 获取当前时间作为文件名 timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) filename = f"用户名单_{timestamp}.csv" with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) # 写入表头(只包含工号) writer.writerow(["工号"]) # 写入每条记录(只导出工号) for user_id in TRAINED_USERS.keys(): writer.writerow([user_id]) messagebox.showinfo("导出成功", f"用户名单已解密并导出到: {os.path.abspath(filename)}") except Exception as e: messagebox.showerror("导出失败", f"导出用户名单时出错: {str(e)}") """导出excel文件""" # try: # timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) # filename = f"用户名单_{timestamp}.xlsx" # # # 创建dataframe # data = [] # for user_id, password in TRAINED_USERS.items(): # data.append([user_id, password]) # df = pd.DataFrame(data, columns=["工号", "密码"]) # # # 导出到excel # df.to_excel(filename, index=False, engine='openpyxl') def clear_screen(self): """清除当前屏幕所有内容""" for widget in self.root.winfo_children(): widget.destroy() def lock_on_close(self): """关闭窗口时锁定系统""" if self.current_user: self.lock_system(manual=True) self.root.destroy() # 启动系统 if __name__ == "__main__": app = SecureDesktopMonitor() # 绑定窗口关闭事件 app.root.protocol("WM_DELETE_WINDOW", app.lock_on_close)
11-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值