歌者文明清理员 发表于 2023-7-17 14:57:26

谁来帮我修一下bug?

本帖最后由 歌者文明清理员 于 2023-7-17 15:00 编辑

补充:对不起,搞错了,内容已更新

问题是为什么使用“testuser”做用户名,密码是“abcdABCDAB”的时候提示“用户已存在”?

通过admin tool 可以看见,但还是搞不懂怎么回事

import os
import string
from ttkbootstrap import *
from tkinter import Text, Button
from pickle import load, dump
from threading import Thread
from time import sleep

PATH_DATA = os.path.join(os.path.dirname(__file__), "data")
PATH_USERS = os.path.join(PATH_DATA, "users.fishc")

(
    CODE_SUCCESS,
    CODE_NOT_FOUND,
    CODE_DUPLICATE,
    CODE_WRONG_PARAMETER,
    CODE_LENGTH_ERROR,
    CODE_WEAK_PASSWORD
) = range(6)

SAFE_LOWEST, SAFE_1, SAFE_2, SAFE_3, SAFE_HIGHEST = range(5)

def check_dir(path):
    """
    检查一个目录是否存在,如果不存在就创建一个
    :param path: 路径
    :return: None
    """
    if not os.path.isdir(path):
      os.mkdir(path)

def check_file(path, binary=False, default=""):
    """
    检查一个文件是否存在,如果不存在就创建一个
    :param path: 路径
    :param binary: 是否为二进制文件
    :param default: 如果此文件为空,默认值
    :return: none
    """
    if not os.path.isfile(path):
      if binary:
            with open(path, "wb") as f:
                dump(default, f)
      else:
            with open(path, "w") as f:
                f.write(default)

check_dir(PATH_DATA)
check_file(PATH_USERS, True, [])

class User:
    def __init__(self, username, password):
      """
      创建一个用户
      :param username: 用户名
      :param password: 密码
      """
      self.username = username
      self.password = password
   
    def info(self):
      """
      获取某人的 info
      :return: info `(username, password)`
      """
      return self.username, self.password

def add_user(username, password):
    """
    将一个用户添加到 `data/users.fishc`
    :param username: 用户名
    :param password: 密码
    :return: 被添加的用户
    """
    user = User(username, password)
    data = get_userlist()
    data.append(user)
    write_into_userlist(data)
    return user

def remove_user(username, password):
    """
    从用户列表移除一个用户(data/users.fishc)
    :param username: 用户名
    :param password: 密码
    :return: 被删除的用户
    """
    data = get_userlist()
    index = None
    for i, (user, pwd) in enumerate(map(User.info, data)):
      if user == username:
            if pwd == password:
                index = i
                break
            else:
                raise ValueError("wrong password")
    else:
      raise ValueError("wrong username")
    data.pop(index)
    write_into_userlist(data)

def get_userlist():
    """
    获取用户列表(data/users.fishc)
    :return: 用户列表
    """
    with open(PATH_USERS, "rb") as f:
      data = load(f)
    return data

def write_into_userlist(data):
    """
    将内容写入用户列表(data/users.fishc)
    :param data: 新内容
    :return: None
    """
    with open(PATH_USERS, "wb") as f:
      dump(data, f)

def login(username, password):
    """
    登录进一个账号
    :param username: 用户名
    :param password: 密码
    :return: (状态码, 找到的用户)
    """
    users = get_userlist()
    # 为了便于判断,用 for 循环
    for user in users:
      if user.info() == (username, password):
            # 登录成功
            return (CODE_SUCCESS, user)
    return (CODE_NOT_FOUND, None)

def register(username, password):
    """
    注册一个用户
    :param username: 用户名
    :param password: 密码
    :return: (状态码, 新用户)
    """
    # 检查用户名是否已被使用
    users =
    if username in users:
      return (CODE_DUPLICATE, None)
    if username == "Admin":
      return (CODE_SUCCESS, add_user(username, password))
    if not 3 <= len(username) < 11:
      return (CODE_LENGTH_ERROR, None)
    # 密码强度
    password_strength = check_password_strength(password)
    # 必须达到 SAFE_3 级别
    if password_strength < SAFE_3:
      if password_strength == SAFE_LOWEST:
            show_message(label_password_weak0)
      elif password_strength == SAFE_1:
            show_message(label_password_weak1)
      elif password_strength == SAFE_2:
            show_message(label_password_weak2)
      
    # 创建用户
    return (CODE_SUCCESS, add_user(username, password))

def command_login():
    """
    作为 button 的 `command` 属性的函数,用于调用 `login()`
    :return: None
    """
    value_username = entry_username.get()
    value_password = entry_password.get()
    status = login(value_username, value_password)
    if status == CODE_SUCCESS:
      show_message(label_message2)
      frame_login.place_forget()
    else:
      show_message(label_message1)

def command_register():
    """
    作为 button 的 `command` 属性的函数,用于调用 `register()`
    :return: None
    """
    value_username = entry_username.get()
    value_password = entry_password.get()
    status = register(value_username, value_password)
    if status == CODE_SUCCESS:
      show_message(label_message3)
    elif status == CODE_DUPLICATE:
      show_message(label_message4)
    elif status == CODE_LENGTH_ERROR:
      show_message(label_message5)

def disappear(obj, method, time1, time2):
    """
    使某个控件在几秒后消失。(打字机特效,指定秒数少一个字符)

    注意,请使用 `threading.Thread` 调用此函数,否则会造成窗口卡死。
    :param obj: 控件
    :param method: 控件使用的方法,为“place”、“pack”、“grid”中的一种
    :param time1: 过多久才开始消失
    :param time2: 消失的时间,多久消失一个字
    :return: 状态码
    """
    # 先提前搞到文字,到时候消失了要放回去
    text = obj["text"]
    # 等待
    sleep(time1)
    while obj["text"]:
      # 减少一个字
      obj["text"] = obj["text"][:-1]
      sleep(time2)
    obj["text"] = text
    # 使控件消失
    getattr(obj, f"{method}_forget")()

def show_message(obj):
    """
    显示一条消息
    :param obj: 消息对象(`Label`)
    :return: None
    """
    obj.place(**temp3)
    Thread(target=disappear, args=(obj, "place", 1, 0.1)).start()

def check_password_strength(pwd):
    """
    检查密码的安全级别。规则如下:

    |             判断条件         | 判断结果 |    返回值      |
    |:-----------------------------|:---------|:---------------|
    | 密码长度 < 10                | 不安全   | `SAFE_LOWEST`|
    | 密码只含有数字               | 不太安全 | `SAFE_1`       |
    | 密码只有字母               | 中等   | `SAFE_2`       |
    | 密码有字母和数字             | 有点安全 | `SAFE_3`       |
    | 密码有数字、大小写字母和符号 | 安全   | `SAFE_HIGHEST` |

    :param pwd: 密码
    :return: 参考表格
    """
    if len(pwd) < 10:
      return SAFE_LOWEST
    number = False
    lowercase = False
    uppercase = False
    punctuation = False
    for char in pwd:
      # 避免二次判断
      if not number and char in string.digits:
            number = True
      if not lowercase and char in string.ascii_lowercase:
            lowercase = True
      if not uppercase and char in string.ascii_uppercase:
            uppercase = True
      if not punctuation and char in string.punctuation:
            punctuation = True
    result = (number, lowercase, uppercase, punctuation)
    result_count = result.count(True)
    if number and result_count == 1:
      # 只有数字
      return SAFE_1
    if (lowercase or uppercase) and 1 <= result_count < 3:
      # 只有字母
      return SAFE_2
    if (lowercase or uppercase) and number and 2 <= result_count < 4:
      # 大/小写字母且有数字
      return SAFE_3
    if lowercase and uppercase and number and punctuation:
      return SAFE_HIGHEST

def change_password_visiblity():
    """
    切换密码输入框的可见性,如果密码没隐藏则隐藏,如果隐藏了就不隐藏
    :return: None
    """
    if entry_password["show"] == "":
      # 密码没有隐藏
      entry_password["show"] = "*"
      button_change_password_visiblity["text"] = "显示密码"
    else:
      # 密码隐藏了
      entry_password["show"] = ""
      button_change_password_visiblity["text"] = "隐藏密码"

def change_admin_tool_visiblity():
    """
    切换 Admin Tool 的可见性
    :return: None
    """
    if bool(frame_admin_tool.winfo_manager()):
      frame_admin_tool.place_forget()
      # 没隐藏
      button_admin_tool["text"] = "打开 Admin Tool"
    else:
      frame_admin_tool.place(relx=0.9, rely=0.2, anchor="ne")
      # 隐藏了
      button_admin_tool["text"] = "关闭 Admin Tool"

def change_users_visiblity():
    """
    管理用户(Admin Tool)
    :return: None
    """
    # 以防万一
    text_users.delete(0.0, END)
    if bool(text_users.winfo_manager()):
      button_show_userlist["text"] = "显示用户列表"
      text_users.grid_forget()
    else:
      button_show_userlist["text"] = "隐藏用户列表"
      text_users.grid(row=1, column=0, **temp2)
      text_users.insert(END, '\n'.join([' '.join(user.info()) for user in get_userlist()]))

temp1 = {"padx": 10, "pady": 10}
temp2 = {"padx": 5, "pady": 5}
temp3 = {"relx": 0.8, "rely": 0.2, "anchor": "ne"}

bootstyle = {
    "primary":   {"fg": "white", "bg": "#4582ec"},
    "secondary": {"fg": "white", "bg": "#adb5db"},
    "success":   {"fg": "white", "bg": "#02b875"},
    "info":      {"fg": "white", "bg": "#17a2b8"},
    "warning":   {"fg": "white", "bg": "#f0ad4e"},
    "danger":    {"fg": "white", "bg": "#d9534f"},
    "light":   {"fg": "black", "bg": "#f8f9fa"},
    "dark":      {"fg": "white", "bg": "#343a40"}
}

root = Window("This Forum 1.0 Beta 测试版本 - By dddddgz", "morph")
root.geometry("1000x800+100+100")

label_message1 = Label(root, bootstyle="danger", text="密码或用户名错误")
label_message2 = Label(root, bootstyle="success", text="登录成功!")
label_message3 = Label(root, bootstyle="success", text="注册成功!")
label_message4 = Label(root, bootstyle="danger", text="用户名已存在")
label_message5 = Label(root, bootstyle="danger", text="用户名字符数限制:3-10")

label_password_weak0 = Label(root, bootstyle="danger", text="密码太弱,没有达到10个字符")
label_password_weak1 = Label(root, bootstyle="danger", text="密码只有数字")
label_password_weak2 = Label(root, bootstyle="danger", text="密码只有大写或小写字母")

frame_login = Frame(root)

label_username = Label(frame_login, bootstyle="dark", text="用户名")
label_username.grid(row=0, column=0, **temp1, columnspan=2)
entry_username = Entry(frame_login, bootstyle="info", width=30)
entry_username.grid(row=1, column=0, **temp1, columnspan=2)

label_password = Label(frame_login, bootstyle="dark", text="密码")
label_password.grid(row=2, column=0, **temp1, columnspan=2)
entry_password = Entry(frame_login, bootstyle="primary", width=30)
entry_password.grid(row=3, column=0, **temp1, columnspan=2)

button_change_password_visiblity = Button(frame_login, **bootstyle["primary"])
button_change_password_visiblity["command"] = change_password_visiblity
button_change_password_visiblity.grid(row=3, column=2, **temp1)
change_password_visiblity()

button_login = Button(frame_login)
button_login["text"] = "登录"
button_login["width"] = 8
button_login["command"] = command_login
button_login.grid(row=4, column=0, **temp1)

button_register = Button(frame_login)
button_register["text"] = "注册"
button_register["width"] = 8
button_register["command"] = command_register
button_register.grid(row=4, column=1, **temp1)

frame_login.place(relx=0.5, rely=0.5, anchor="center")

button_admin_tool = Button(root, **bootstyle["info"])
button_admin_tool["text"] = "打开 Admin Tool"
button_admin_tool["command"] = change_admin_tool_visiblity
button_admin_tool.place(relx=0.8, rely=0.9, anchor="ne")

frame_admin_tool = Frame(root)

button_show_userlist = Button(frame_admin_tool, **bootstyle["warning"])
button_show_userlist["text"] = "查看用户列表"
button_show_userlist["command"] = change_users_visiblity
button_show_userlist.grid(row=0, column=0, **temp2)

text_users = Text(frame_admin_tool, width=10, height=10)

root.mainloop()

陶远航 发表于 2023-7-17 14:57:35

根据你提供的代码,我看到在注册用户时会检查用户名是否已存在。如果使用"testuser"作为用户名,密码是"1111111111"时提示"用户已存在",这是因为在第80行检查用户名是否已被使用时,程序发现"testuser"已经存在于用户列表中,所以返回了状态码CODE_DUPLICATE,表示用户名已存在。你可以更改用户名或选择一个不存在的用户名进行测试。

歌者文明清理员 发表于 2023-7-19 12:37:07

@isdkz @sfqxx @liuhongrun2022

liuhongrun2022 发表于 2023-7-19 12:51:42

没明白你的意思

歌者文明清理员 发表于 2023-7-19 12:55:24

liuhongrun2022 发表于 2023-7-19 12:51
没明白你的意思

问题是为什么使用“testuser”做用户名,密码是“abcdABCDAB”的时候提示“用户已存在”?

歌者文明清理员 发表于 2023-7-19 12:57:16

非常抱歉,但是我只能处理最多2048个字数token的请求。您提供的内容超过了我的处理能力,请缩短您的输入并重新提交问题,我将尽力回答。谢谢!

isdkz 发表于 2023-7-19 12:59:33

您的代码看上去非常整洁,但是在您的 "register" 函数中,您没有正确地返回密码强度检查的结果。当前的代码只有当密码强度达到 SAFE_3 或更高级别时,才会注册新用户,但是,如果密码强度低于 SAFE_3,函数并未返回任何状态码。这可能是您遇到“用户已存在”提示的原因。

您的 "register" 函数可以修改如下:

def register(username, password):
    """
    注册一个用户
    :param username: 用户名
    :param password: 密码
    :return: (状态码, 新用户)
    """
    # 检查用户名是否已被使用
    users =
    if username in users:
      return (CODE_DUPLICATE, None)
    if username == "Admin":
      return (CODE_SUCCESS, add_user(username, password))
    if not 3 <= len(username) < 11:
      return (CODE_LENGTH_ERROR, None)
    # 密码强度
    password_strength = check_password_strength(password)
    # 必须达到 SAFE_3 级别
    if password_strength < SAFE_3:
      if password_strength == SAFE_LOWEST:
            show_message(label_password_weak0)
            return (CODE_WEAK_PASSWORD, None)
      elif password_strength == SAFE_1:
            show_message(label_password_weak1)
            return (CODE_WEAK_PASSWORD, None)
      elif password_strength == SAFE_2:
            show_message(label_password_weak2)
            return (CODE_WEAK_PASSWORD, None)
    # 创建用户
    return (CODE_SUCCESS, add_user(username, password))


这样,在密码强度低于 SAFE_3 时,该函数就会返回一个状态码(CODE_WEAK_PASSWORD),并显示相应的消息。这可能解决了您的问题。

此外,我注意到代码中没有具体的 "Admin Tool",您提到的 "Admin Tool" 是另一个模块或者是界面部分吗?如果是,请确保在使用该工具添加或修改用户时,遵循同样的用户名和密码要求。

我希望这能解决您的问题。如果还有其他问题或细节需要了解,请随时告诉我!

歌者文明清理员 发表于 2023-7-19 13:09:43

isdkz 发表于 2023-7-19 12:59
您的代码看上去非常整洁,但是在您的 "register" 函数中,您没有正确地返回密码强度检查的结果。当前的代码 ...

谢,问题确实解决了,可是command register里我并没有检查返回值为none的情况啊

isdkz 发表于 2023-7-19 13:11:04

歌者文明清理员 发表于 2023-7-19 13:09
谢,问题确实解决了,可是command register里我并没有检查返回值为none的情况啊

Python中,当函数没有明确返回值的时候,默认返回的是None。如果你在使用command register并且没有考虑到返回None的情况,可能会在使用返回结果的时候遇到问题。

比如说,如果你试图对一个None值进行某种操作(如索引,迭代或者调用方法等)的话,Python会抛出一个TypeError。

这就是为什么在处理command register的返回值之前,我们通常会检查它是否为None。如果为None,那么我们可以根据具体情况来决定下一步的操作,比如抛出一个错误,返回一个默认值,或者跳过后续的操作。

以下是一个例子:

result = command_register(args)
if result is None:
    print("Command not found!")
else:
    # 处理正常的结果


在这个例子中,如果command_register返回None(也就是说,没有找到与给定参数匹配的命令),我们会打印一个错误信息,然后跳过后续的处理。如果找到了匹配的命令,我们就继续正常的处理。

cjjJasonchen 发表于 2023-7-19 14:19:17

哇歌者你这代码写的漂亮啊{:10_275:}
https://xxx.ilovefishc.com/forum/202307/17/161432nbtt5wj5m5rmetoe.gif
页: [1]
查看完整版本: 谁来帮我修一下bug?