初学了PYQT,试着自己写了一个关于密码存储的管理系统。这套系统可以很好的解决我在日常工作中记不住密码的问题,使用非对称算法生成的秘钥文件,非常的安全。借此与大家一起分享。
系统用到的工具主要有QT设计师软件(Qt Designer),VsCode编辑器。
一、设计Qt界面
因为学得还不是很深入,使用的都是软件自带的一些配色。
1.登录界面
新用户注册界面
重置登录密码界面
当输入密码错误超过5次,将锁定键盘60秒
密码管理的主界面
查看密码的界面
排好界面后将ui文件保存到自定义的目录,并用pyuic5的命令将ui文件转换成py文件。
接下来就可以用编辑器敲代码了。
这里我就偷下懒,直接把代码粘出来分享给大家一起学习。
1.登录页面代码
from PyQt5.QtWidgets import (
QApplication,QDialog,QMessageBox
)
from login import Ui_login_pwd_manager
from Pwd_Manager_main import PwdManager
from set_passwd_main import Reset_Passwd
from sign_page_main import RegisterAccount
from lock_keyboard_main import LockKeyboard
import sys
import pymysql
import os
class LoginManager(Ui_login_pwd_manager,QDialog):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.show()
if os.path.exists('./error.txt'):
os.remove('./error.txt')
self.pushButton_login.clicked.connect(self.show_manager_window)
self.pushButton_sign_account.clicked.connect(self.show_sign_window)
self.pushButton_find_passwd.clicked.connect(self.show_find_passwd)
self.pushButton_quit.clicked.connect(self.close)
def show_msg(self,msg):
msgs = QMessageBox.warning(
self,
"信息提示",
msg
)
return msgs
def show_manager_window(self):
try:
host = '主机名或IP地址'
user = self.lineEdit_user.text()
pwd = self.lineEdit_pwd.text()
db = '数据库名'
port = 端口号
conn = pymysql.connect(host=host,user=user,password=pwd,db=db,port=port)
cur = conn.cursor()
if os.path.exists('./error.txt'):
os.remove('./error.txt')
pwd_manager = PwdManager(engine=conn,object=cur,user=user)
pwd_manager.exec()
except:
with open('./error.txt','a+') as fin:
fin.write('error\n')
self.error_msg()
def error_msg(self):
count_set = 6
if os.path.exists('./error.txt'):
with open('./error.txt','r') as fout:
error = fout.readlines()
count_now = count_set - len(error)
self.show_msg(f"用户名或密码错误还有{count_now}次机会,键盘将锁定!")
if count_now == 1:
lock = LockKeyboard()
lock.exec()
def show_sign_window(self):
sign_window = RegisterAccount()
sign_window.exec()
def show_find_passwd(self):
reset_pwd = Reset_Passwd()
reset_pwd.exec()
2.注册页面代码
from distutils.log import error
from PyQt5.QtWidgets import (
QDialog,QMessageBox
)
from sign_page import Ui_sign
import pymysql
import base64
class RegisterAccount(Ui_sign,QDialog):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.show()
try:
host = '主机名或IP地址'
user = '用户名'
pwd = '密码'
db = '数据库名'
port = 端口号
self.conn = pymysql.connect(host=host,user=user,password=pwd,db=db,port=port)
self.cur = self.conn.cursor()
except Exception as e:
QMessageBox.warning(
self,
"错误信息",
f"连接服务器失败!{e}"
)
self.pushButton_sign.clicked.connect(self.sign)
self.pushButton_close.clicked.connect(self.close)
def show_msg(self,msg):
msgs = QMessageBox.warning(self,"信息提示",msg)
return msgs
def sign(self):
user = self.lineEdit_user.text()
pwd = self.lineEdit_pwd.text()
pwd_again = self.lineEdit_pwd_again.text()
phone = self.lineEdit_phone.text()
try:
check_sql = f"""
SELECT user FROM mysql.user WHERE user="{user}"
"""
self.cur.execute(check_sql)
get_user = self.cur.fetchall()
if get_user:
self.show_msg('用户名已存在!')
else:
if pwd == "" or pwd_again == "":
self.show_msg("密码不能为空")
elif pwd != pwd_again:
self.show_msg("两次输入的密码不一致,请重新输入!")
else:
if 0 < len(phone) < 11:
self.show_msg("手机号码格式错误!")
elif len(phone) == 11:
phone = str(base64.b64encode(phone.encode()))[2:-2]
sqls_user = []
create_user_local_sql = f"""
CREATE USER "{user}"@'localhost' IDENTIFIED BY "{pwd}"
"""
create_user_any_sql = f"""
CREATE USER "{user}"@'%' IDENTIFIED BY "{pwd}"
"""
sqls_user.append([[create_user_local_sql],[create_user_any_sql]])
for i in sqls_user[0]:
sql = ''.join(i).replace('\n','')
self.cur.execute(sql)
self.conn.commit()
sqls_set_table = []
create_table_sql = f"""
CREATE TABLE {user}_table(
account_name varchar(50),
user_name varchar(50),
pwd varchar(200) DEFAULT '1234'
)
"""
unique_account_sql = f"""
ALTER TABLE {user}_table
ADD UNIQUE (account_name)
"""
unique_user_sql = f"""
ALTER TABLE {user}_table
ADD UNIQUE (user_name)
"""
grant_local_sql = f"""
GRANT ALL ON 数据库名.{user}_table TO "{user}"@'localhost' IDENTIFIED BY "{pwd}"
"""
grant_any_sql = f"""
GRANT ALL ON 数据库名.{user}_table TO "{user}"@'%' IDENTIFIED BY "{pwd}"
"""
user_info_sql = f"""
INSERT INTO user_info VALUES("{user}","{phone}")
"""
sqls_set_table.append([[create_table_sql],[unique_account_sql],[unique_user_sql],[grant_local_sql],[grant_any_sql],[user_info_sql]])
for i in sqls_set_table[0]:
sql = ''.join(i).replace('\n','')
self.cur.execute(sql)
self.conn.commit()
self.show_msg(f"恭喜你,注册成功!\n快点儿登录试试吧!")
else:
self.show_msg("请填写您的手机号码,便于您找回密码!")
except pymysql.err.OperationalError as e:
self.show_msg(f"用户名已存在!{e}")
3.重置密码页面
# coding=utf-8
from distutils.log import info
import urllib
import urllib.request
import hashlib
from PyQt5.QtWidgets import (
QDialog,QMessageBox
)
from reset_pwd import Ui_reset_passwd
import pymysql
import string
import random
import base64
class Reset_Passwd(Ui_reset_passwd,QDialog):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.show()
try:
host = '主机名或IP地址'
user = '用户名'
pwd = '密码'
db = '数据库名'
port = 端口号
self.conn = pymysql.connect(host=host,user=user,password=pwd,db=db,port=port)
self.cur = self.conn.cursor()
except Exception as e:
QMessageBox.warning(
self,
"错误信息",
f"连接服务器失败!{e}"
)
self.statusStr = {
'0': '短信发送成功',
'-1': '参数不全',
'-2': '服务器空间不支持,请确认支持curl或者fsocket,联系您的空间商解决或者更换空间',
'30': '密码错误',
'40': '账号不存在',
'41': '余额不足',
'42': '账户已过期',
'43': 'IP地址限制',
'50': '内容含有敏感词'
}
chars = string.digits*10
self.code = ''.join(random.sample(chars,4))
self.pushButton_get_check_num.clicked.connect(self.send_msg)
self.pushButton_set_pwd.clicked.connect(self.set_pwd)
self.pushButton_close.clicked.connect(self.close)
def show_msg(self,msg):
msgs = QMessageBox.warning(self,"信息提示",msg)
return msgs
def md5(self,str):
m = hashlib.md5()
m.update(str.encode("utf8"))
return m.hexdigest()
def send_msg(self):
smsapi = "https://api.smsbao.com/sms?"
self.username = self.lineEdit_user_name.text()
if not self.username:
self.show_msg("用户名不能为空!")
user_sql = f"""
SELECT user FROM mysql.user WHERE user="{self.username}"
"""
self.cur.execute(user_sql)
get_user = self.cur.fetchall()
if get_user:
# 短信平台账号
user = '短信平台账号'
# 短信平台密码
password = self.md5('短信平台密码')
# 要发送的短信内容
content = f"【密码管理系统】您的验证码是{self.code}。请妥善保管好您的验证码并在5分钟内输入。如非本人操作,请忽略本短信"
# 要发送短信的手机号码
info_sql = f"""
SELECT info FROM user_info WHERE user="{self.username}"
"""
self.cur.execute(info_sql)
user_info = self.cur.fetchall()
if user_info:
info = f"'{user_info[0][0]}='"
info = str(base64.b64decode(info))[2:-1]
self.label_set_info.setText(f"给手机号码{info[:3]}****{info[-4:]}发送验证信息")
data = urllib.parse.urlencode({'u': user, 'p': password, 'm': info, 'c': content})
send_url = f"{smsapi}{data}"
response = urllib.request.urlopen(send_url)
the_page = response.read().decode('utf-8')
self.show_msg(f"{self.statusStr[the_page]},请注意查收!")
else:
self.show_msg("请输入接收验证码的手机号码!")
else:
self.show_msg(f"用户不存在!")
def set_pwd(self):
msgs = []
get_code = self.lineEdit_check_num.text()
pwd = self.lineEdit_pwd.text()
pwd_again = self.lineEdit_pwd_again.text()
if get_code == self.code:
if pwd == pwd_again:
if pwd and pwd_again:
reset_sql1 = f"""
ALTER USER "{self.username}"@'localhost' IDENTIFIED BY "{pwd}"
"""
reset_sql2 = f"""
ALTER USER "{self.username}"@'%' IDENTIFIED BY "{pwd}"
"""
sqls = [[reset_sql1],[reset_sql2]]
for i in sqls:
sql = ''.join(i)
self.cur.execute(sql)
self.conn.commit()
msgs.append("重置密码成功!\n快去登录系统试试吧!")
else:
msgs.append("密码不能为空!")
else:
msgs.append("两次输入的密码不一致!")
else:
msgs.append("验证码不正确!")
if msgs:
self.show_msg(''.join(msgs))
4.键盘锁页面
from PyQt5.QtWidgets import (
QApplication,QDialog
)
from lock_keyboard import Ui_lock_keyboard
import time
import os
from ctypes import windll
class LockKeyboard(Ui_lock_keyboard,QDialog):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.show()
self.lock()
self.pushButton_close.clicked.connect(self.close)
def lock(self):
user32 = windll.LoadLibrary('user32.dll')
user32.BlockInput(True)
countdown = 60
count = 0
while count < countdown:
count_now = countdown -count
self.label_lock.setText(f"键盘已锁定! {count_now}秒后解锁")
QApplication.processEvents()
time.sleep(1)
count += 1
if count_now == 1:
self.label_lock.setText("解锁")
QApplication.processEvents()
user32.BlockInput(False)
if os.path.exists('./error.txt'):
os.remove('./error.txt')
5.密码管理主页面
from PyQt5.QtWidgets import (
QDialog,QMessageBox,QTableWidgetItem
)
from pwd_manager import Ui_pwd_manager
from view_pwd_main import ViewPwd
import pymysql
import random
import string
import base64
import rsa
import os
class PwdManager(Ui_pwd_manager,QDialog):
def __init__(self,engine,object,user) -> None:
super().__init__()
self.setupUi(self)
self.show()
self.conn = engine
self.cur = object
self.user = user
(pubkey,privkey) = rsa.newkeys(512)
pubkey_file = pubkey.save_pkcs1()
privkey_file = privkey.save_pkcs1()
pubkey_path = r"./pubkey.pem"
privkey_path = r"./privkey.pem"
if not os.path.exists(pubkey_path):
with open(pubkey_path,'wb') as fin:
fin.write(pubkey_file)
if not os.path.exists(privkey_path):
with open(privkey_path,'wb') as fin1:
fin1.write(privkey_file)
with open(pubkey_path,'r') as fout:
self.pubkey = rsa.PublicKey.load_pkcs1(fout.read())
self.pushButton_query_pwd.clicked.connect(self.query_one_pwd)
self.pushButton_query_pwd_all.clicked.connect(self.query_all_pwd)
self.pushButton_create_pwd.clicked.connect(self.create_pwd)
self.pushButton_add_pwd.clicked.connect(self.add_pwd)
self.pushButton_refresh_pwd.clicked.connect(self.refresh_pwd_window)
self.pushButton_del_pwd.clicked.connect(self.del_pwd)
self.pushButton_del_all_pwd.clicked.connect(self.del_all_pwd)
def show_msg(self,msg):
msgs = QMessageBox.warning(self,"信息提示",msg)
return msgs
def query_one_pwd(self):
try:
account_name = self.lineEdit_get_account_name.text()
if account_name:
sql = f"""
SELECT * FROM {self.user}_table WHERE account_name="{account_name}"
"""
self.cur.execute(sql)
results = self.cur.fetchall()
print(results[0][2])
print(len(results[0][2]))
if results:
row = self.cur.rowcount
col = len(results[0])
self.tableWidget_show_pwd.setRowCount(row)
self.tableWidget_show_pwd.setColumnCount(col)
pwd = base64.b64decode(f"{results[0][2]}==")
print(pwd)
print(type(pwd))
for i in range(row):
for j in range(col):
self.tableWidget_show_pwd.setItem(
i,j,
QTableWidgetItem(str(results[i][j]))
)
reply = QMessageBox.question(self,"信息提示","是否查看密码?",QMessageBox.Yes|QMessageBox.No,QMessageBox.No)
if reply == QMessageBox.Yes:
view_pwd = ViewPwd(pwd)
view_pwd.exec()
else:
self.show_msg("没有找到对应账号类别的相关信息,确认账号类别输入正确后再试试哦!")
except Exception as e:
self.show_msg(f"请先输入要查询的账号类别{e}")
def query_all_pwd(self):
try:
sql = f"""
SELECT * FROM {self.user}_table
"""
self.cur.execute(sql)
results = self.cur.fetchall()
if results:
row = self.cur.rowcount
col = len(results[0])
self.tableWidget_show_pwd.setRowCount(row)
self.tableWidget_show_pwd.setColumnCount(col)
for i in range(row):
for j in range(col):
self.tableWidget_show_pwd.setItem(
i,j,
QTableWidgetItem(str(results[i][j]))
)
else:
self.show_msg("数据空空如也,快点儿存入你的第一个密码试试吧!")
except AttributeError as e:
self.show_msg(f"请先连接数据库!{e}")
#加密
def rsaEncrypt(self,str,pubkey):
content = str.encode('utf-8')
crypto = rsa.encrypt(content,pubkey)
return crypto
def create_pwd(self):
pwd_length = int(self.spinBox_pwd_length.text())
if pwd_length == 0:
self.show_msg("密码位数不能小于1")
elif pwd_length > 53:
self.show_msg("密码位数不能大于53")
else:
chars = []
if self.checkBox_letters.isChecked():
chars.append(string.ascii_letters*10)
if self.checkBox_digits.isChecked():
chars.append(string.digits*10)
if self.checkBox_punctuation.isChecked():
chars.append(string.punctuation*10)
if not chars:
chars = f"""
{string.ascii_letters},{string.digits},{string.punctuation}
"""
else:
chars = "".join(chars)
chars_list = random.sample(chars,pwd_length)
self.pwd = "".join(chars_list)
self.lineEdit_new_pwd.setText(self.pwd)
self.pwd = self.rsaEncrypt(self.pwd,self.pubkey)
print(self.pwd)
self.pwd = str(base64.b64encode(self.pwd))[2:-3]
print(self.pwd)
print(type(self.pwd))
print(len(','.join(self.pwd)))
def add_pwd(self):
msgs = []
try:
account_name = self.lineEdit_add_account_name.text()
if not account_name:
msgs.append("账号类别不能为空\n")
user_name = self.lineEdit_add_user_name.text()
if not user_name:
msgs.append("用户名不能为空\n")
sql = f"""
INSERT INTO {self.user}_table VALUES("{account_name}","{user_name}","{self.pwd}")
"""
self.cur.execute(sql)
self.conn.commit()
self.query_all_pwd()
#清空输入框数据
self.lineEdit_add_account_name.clear()
self.lineEdit_add_user_name.clear()
self.spinBox_pwd_length.clear()
self.lineEdit_new_pwd.clear()
except pymysql.err.IntegrityError as e:
msgs.append(f"账号类别/用户名已存在:{e}\n")
except pymysql.err.ProgrammingError as e:
msgs.append(f"自动生成的密码格式错误:{e}\n")
except AttributeError as e:
msgs.append(f"密码不能为空{e}\n")
if msgs:
self.show_msg("".join(msgs))
def refresh_pwd_window(self):
try:
for i in self.tableWidget_show_pwd.selectedItems():
if i.column() == 0:
text = self.tableWidget_show_pwd.item(i.row(),i.column()+1).text()
sql = f"""
UPDATE {self.user}_table SET account_name="{i.text()}" WHERE user_name="{text}"
"""
elif i.column() == 1:
text = self.tableWidget_show_pwd.item(i.row(),i.column()-1).text()
sql = f"""
UPDATE {self.user}_table SET user_name="{i.text()}" WHERE account_name="{text}"
"""
elif i.column() == 2:
text = self.tableWidget_show_pwd.item(i.row(),i.column()-1).text()
pwd = self.rsaEncrypt(i.text(),self.pubkey)
pwd = str(base64.b64encode(pwd))[2:-3]
sql = f"""
UPDATE {self.user}_table SET pwd="{pwd}" WHERE user_name="{text}"
"""
self.cur.execute(sql)
self.conn.commit()
self.query_all_pwd()
except Exception as e:
self.show_msg(f"请先选择要修改的数据!{e}")
def del_pwd(self):
try:
for i in self.tableWidget_show_pwd.selectedItems():
if i.column() == 0:
sql = f"""
DELETE FROM {self.user}_table WHERE account_name="{i.text()}"
"""
elif i.column() == 1:
sql = f"""
DELETE FROM {self.user}_table WHERE user_name="{i.text()}"
"""
elif i.column() == 2:
sql = f"""
DELETE FROM {self.user}_table WHERE pwd="{i.text()}"
"""
self.cur.execute(sql)
self.conn.commit()
self.query_all_pwd()
except Exception as e:
self.show_msg(f"请先选择要删除的账号类别或用户名!{e}")
def del_all_pwd(self):
sql = f"""
DELETE FROM {self.user}_table
"""
select_box = QMessageBox.question(
self,
"警告提示",
"你确定要删除所有数据吗?三思而后行!",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if select_box == QMessageBox.Yes:
self.cur.execute(sql)
self.conn.commit()
self.query_all_pwd()
6.创建新密码
from http import server
from PyQt5.QtWidgets import (
QApplication,QDialog,QMessageBox
)
from add_pwd import Ui_add_pwd
import sys
import pymysql
import random
import string
class AddPwd(Ui_add_pwd,QDialog):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.show()
self.host = '主机名或IP地址'
self.user = '用户名'
self.pwd = '密码'
self.db = '数据库名'
self.port = 端口号
try:
self.conn = pymysql.connect(host=self.host,user=self.user,password=self.pwd,db=self.db,port=self.port)
server_info = self.conn.get_server_info()
self.cur = self.conn.cursor()
except Exception as e:
self.show_msg(f"连接错误:{e}")
self.pushButton_create_pwd.clicked.connect(self.create_pwd)
self.pushButton_add_pwd.clicked.connect(self.add_pwd)
def create_pwd(self):
pwd_length = int(self.spinBox_pwd_length.text())
chars = []
if self.checkBox_letters.isChecked():
chars.append(string.ascii_letters*10)
if self.checkBox_digits.isChecked():
chars.append(string.digits*10)
if self.checkBox_punctuation.isChecked():
chars.append(string.punctuation*10)
if not chars:
chars = f"""
{string.ascii_letters},{string.digits},{string.punctuation}
"""
else:
chars = "".join(chars)
print(type(chars))
chars_list = random.sample(chars,pwd_length)
print(chars_list)
self.pwd = "".join(chars_list)
self.label_show_pwd.setText(f"新密码:{self.pwd}")
def add_pwd(self):
msgs = []
try:
cur = self.conn.cursor()
account_name = self.lineEdit_get_name1.text()
user_name = self.lineEdit_get_name2.text()
sql = f"""
insert into pwd values("{account_name}","{user_name}","{self.pwd}")
"""
cur.execute(sql)
self.conn.commit()
except pymysql.err.IntegrityError as e:
msgs.append(f"账号名称已存在:{e}")
except AttributeError as e:
msgs.append(f"数据库未连接,请退出窗口重新打开!{e}")
print(msgs)
if msgs:
QMessageBox.warning(
self,
"错误信息",
"".join(msgs)
)
else:
QMessageBox.information(self,"信息提示","新增密码成功!")
7.查看密码页面
from PyQt5.QtWidgets import (
QApplication,QDialog,QMessageBox,
QFileDialog
)
from view_pwd import Ui_view_pwd
import sys
import rsa
import os
class ViewPwd(Ui_view_pwd,QDialog):
def __init__(self,pwd) -> None:
super().__init__()
self.setupUi(self)
self.show()
self.str_pwd = pwd
self.pushButton_select_privkey.clicked.connect(self.select_file)
self.pushButton_view_pwd.clicked.connect(self.view_pwd)
self.pushButton_close.clicked.connect(self.close)
def select_file(self):
self.privkey = QFileDialog.getOpenFileName(self,"请选择您的私钥文件",os.getcwd())[0]
print(self.privkey)
self.lineEdit_select_privkey.setText(self.privkey)
def rsaDecrypt(self,str,pk):
with open(self.privkey,'rb') as fout:
pk = rsa.PrivateKey.load_pkcs1(fout.read())
content = rsa.decrypt(str,pk)
con = content.decode('utf-8')
return con
def view_pwd(self):
try:
pwd = self.rsaDecrypt(self.str_pwd,self.privkey)
self.lineEdit_get_pwd.setText(pwd)
except:
QMessageBox.warning(self,"错误提示","查看密码失败,您的私钥文件不匹配!")
最后,可以在网上找一张漂亮的图片做程序的logo,使用【pyinstaller -F -w 文件名 --noconsole --icon=ico图片名 】命令打包成exe文件,就可以在任意一台电脑上面运行了!
最最后,展示下效果图