下面是使用 **Python 编写基于 CHAP 协议的用户认证系统**,并实现 **图形化用户界面(GUI)** 的完整实现。服务端和客户端可以**独立运行**,并且都带有可视化界面。
---
## ✅ 实现功能
1. **CHAP 协议认证流程**:
- 客户端发送用户名
- 服务端生成挑战(随机值)
- 客户端使用密码和挑战值生成 MD5 哈希响应
- 服务端验证哈希是否正确
2. **可视化界面**:
- 使用 `tkinter` 实现图形界面
- 显示认证状态、通信记录
3. **服务端与客户端分离**:
- 可以分别运行服务端和客户端脚本
- 支持 TCP 通信
---
## ✅ 技术栈
- Python 3.x
- `tkinter`:GUI 库
- `socket`:TCP 通信
- `hashlib`:MD5 哈希计算
- `threading`:多线程支持 GUI 与通信并行
---
## ✅ 1. 服务端代码(server_gui.py)
```python
import socket
import hashlib
import threading
import tkinter as tk
from tkinter import messagebox
# 用户数据库(模拟)
users = {
"user1": "password1",
"admin": "admin123"
}
CHALLENGE_SIZE = 16
def generate_challenge():
"""生成随机挑战值"""
return hashlib.sha256(str(time.time()).encode()).hexdigest()[:CHALLENGE_SIZE]
def verify_response(password, challenge, client_hash):
expected_hash = hashlib.md5((challenge + password).encode()).hexdigest()
return expected_hash == client_hash
def handle_client(conn, addr, log_text):
log_text.insert(tk.END, f"[+] 连接来自: {addr}\n")
try:
# 接收用户名
username = conn.recv(1024).decode()
log_text.insert(tk.END, f"收到用户名: {username}\n")
if username not in users:
conn.sendall("USER_NOT_FOUND".encode())
log_text.insert(tk.END, "用户不存在,认证失败\n")
conn.close()
return
# 生成挑战值
challenge = generate_challenge()
log_text.insert(tk.END, f"生成挑战值: {challenge}\n")
conn.sendall(challenge.encode())
# 接收客户端哈希
client_hash = conn.recv(1024).decode()
log_text.insert(tk.END, f"收到客户端哈希: {client_hash}\n")
# 验证
if verify_response(users[username], challenge, client_hash):
conn.sendall("AUTH_SUCCESS".encode())
log_text.insert(tk.END, "认证成功!\n")
while True:
msg = conn.recv(1024).decode()
if not msg or msg == "quit":
break
log_text.insert(tk.END, f"客户端消息: {msg}\n")
else:
conn.sendall("AUTH_FAILED".encode())
log_text.insert(tk.END, "认证失败\n")
except Exception as e:
log_text.insert(tk.END, f"错误: {str(e)}\n")
finally:
conn.close()
log_text.insert(tk.END, "连接已关闭\n")
def start_server(log_text):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 65432))
server.listen(5)
log_text.insert(tk.END, "服务端启动,监听中...\n")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr, log_text))
thread.start()
def create_server_gui():
root = tk.Tk()
root.title("CHAP 服务端")
log_text = tk.Text(root, height=20, width=60)
log_text.pack()
start_button = tk.Button(root, text="启动服务端", command=lambda: threading.Thread(target=start_server, args=(log_text,)).start())
start_button.pack()
root.mainloop()
if __name__ == "__main__":
create_server_gui()
```
---
## ✅ 2. 客户端代码(client_gui.py)
```python
import socket
import hashlib
import threading
import tkinter as tk
from tkinter import messagebox
CHALLENGE_SIZE = 16
def chap_authenticate(sock, username, password, log_text):
sock.sendall(username.encode())
log_text.insert(tk.END, "发送用户名到服务端\n")
response = sock.recv(1024).decode()
if response == "USER_NOT_FOUND":
log_text.insert(tk.END, "认证失败: 用户不存在\n")
return False
challenge = response
log_text.insert(tk.END, f"收到挑战值: {challenge}\n")
client_hash = hashlib.md5((challenge + password).encode()).hexdigest()
sock.sendall(client_hash.encode())
log_text.insert(tk.END, f"发送哈希响应: {client_hash}\n")
result = sock.recv(1024).decode()
if result == "AUTH_SUCCESS":
log_text.insert(tk.END, "认证成功!\n")
return True
else:
log_text.insert(tk.END, "认证失败\n")
return False
def receive_messages(sock, log_text, entry, send_button):
while True:
try:
msg = sock.recv(1024).decode()
if not msg or msg == "quit":
log_text.insert(tk.END, "服务端已断开连接\n")
entry.config(state='disabled')
send_button.config(state='disabled')
break
log_text.insert(tk.END, f"服务端: {msg}\n")
except:
break
def send_message(sock, entry, log_text):
msg = entry.get()
if msg:
sock.sendall(msg.encode())
log_text.insert(tk.END, f"客户端: {msg}\n")
entry.delete(0, tk.END)
if msg == "quit":
entry.config(state='disabled')
def connect_to_server(username, password, log_text, entry, send_button):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(('127.0.0.1', 65432))
if chap_authenticate(sock, username, password, log_text):
threading.Thread(target=receive_messages, args=(sock, log_text, entry, send_button), daemon=True).start()
send_button.config(command=lambda: send_message(sock, entry, log_text))
else:
sock.close()
except Exception as e:
log_text.insert(tk.END, f"连接失败: {str(e)}\n")
def create_client_gui():
root = tk.Tk()
root.title("CHAP 客户端")
tk.Label(root, text="用户名:").pack()
username_entry = tk.Entry(root)
username_entry.pack()
tk.Label(root, text="密码:").pack()
password_entry = tk.Entry(root, show="*")
password_entry.pack()
log_text = tk.Text(root, height=15, width=60)
log_text.pack()
entry = tk.Entry(root, width=50)
entry.pack()
send_button = tk.Button(root, text="发送", state='disabled')
send_button.pack()
def on_login():
username = username_entry.get()
password = password_entry.get()
if not username or not password:
messagebox.showwarning("输入错误", "请输入用户名和密码")
return
entry.config(state='normal')
send_button.config(state='normal')
threading.Thread(target=connect_to_server, args=(username, password, log_text, entry, send_button)).start()
login_button = tk.Button(root, text="登录", command=on_login)
login_button.pack()
root.mainloop()
if __name__ == "__main__":
create_client_gui()
```
---
## ✅ 使用说明
### 🔹 服务端运行:
```bash
python server_gui.py
```
点击 "启动服务端" 按钮开始监听。
### 🔹 客户端运行:
```bash
python client_gui.py
```
输入用户名和密码,点击 "登录",即可进行 CHAP 认证。
---
## ✅ 示例认证流程
| 步骤 | 内容 |
|------|------|
| 1 | 客户端输入用户名 `user1`,密码 `password1` |
| 2 | 服务端发送挑战值 |
| 3 | 客户端计算 MD5(挑战值 + 密码) 并发送 |
| 4 | 服务端验证哈希,返回认证结果 |
| 5 | 认证成功后,双方可进行聊天通信 |
---
## ✅