此篇接上篇,有兴趣的可以去主页或专栏看看,没有的话咱直接开始:
本文主要介绍在RSA加密的加持下完成一个可以多验证方式登录的登录界面及其附带的注册系统,对于该系统,为了尽可能地保证安全性,本系统通过注册文件检查设置了单次注册,并且删除注册文件后会重新生成密钥,使信息无法解码,即便可以绕过登录检查也无法查看存储的信息,真正做到了绝对安全(暴力破解密码……这个就算了吧)但是,密码是8位以上的无限位,同时需要包含大小写字母和数字,让暴力破解当场崩溃,接下来就是代码段(注释明确),我会在最后放上程序截图。
"""
-*- coding: utf-8 -*-
@File : password_memory_pickle.py
@author: Liu_Zixin
@CSDN : 山河之书Liu_Zixin
@Time : 2022/09/15 21:23
"""
import re
import rsa
import pickle
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
window = tk.Tk() # 新建主窗口
window.title("TOP SECRET") # 主窗口题头
window.geometry("450x300") # 主窗口大小
tk.Label(window, text="NO ACCESS FOR IRRELEVANT USERS").place(x=120, y=60) # 窗口内文字,顺便位置也设定了
username = tk.Label(window, text="user:") # 设置输入用户名提示文字
username.place(x=100, y=150) # 设置提示文字位置
username_str = tk.StringVar()
username_input = tk.Entry(window, width=20, textvariable=username_str, show="*") # 新建输入框,获取输入值,输入框内输入的内容将被“*”代替
username_input.place(x=180, y=150) # 设置输入框位置
password = tk.Label(window, text="password:") # 这是输入密码的框,和上面的完全同理
password.place(x=100, y=190)
password_str = tk.StringVar()
password_input = tk.Entry(window, width=20, textvariable=password_str, show="*")
password_input.place(x=180, y=190)
def log_out(): # 退出函数,用于关闭窗口
window.destroy() # 关闭主窗口
def log_in():
username_info = username_input.get() # 获取用户名和密码的输入信息
password_info = password_input.get()
try:
with open("file_log_in.pickle", "rb+") as file_log_in: # 打开存储登录信息的pickle类型文件,注意,此处使用rb+打开方式,二进制形式打开,而且在没有文件的情况下会报FileNotFoundError,从而与下文except结合,启动注册程序
data_log_in = pickle.load(file_log_in) # load函数获取信息
with open('private.pem') as private_file: # 这是私钥存储专用文件
p = private_file.read() # 读取私钥
privkey_use = rsa.PrivateKey.load_pkcs1(p.encode()) # 解码私钥
line_1_0 = rsa.decrypt(data_log_in[0], privkey_use) # 解密数据项1与2(后面会写,这两个分别是账号和密码)
line_2_0 = rsa.decrypt(data_log_in[1], privkey_use)
line_1 = line_1_0.decode() # 解码(解密之后的信息仍然是byte型,不能直接用)
line_2 = line_2_0.decode()
if username_info == line_1: # 如果输入的用户名在登录信息文档中有
if password_info == line_2: # 如果密码对了
tk.messagebox.showinfo("ACCESS PERMITTED", "%s, WELCOME" % line_1) # 欢迎
main() # 启动主界面程序(书见下回,把这行去掉就可以单独运行该部分程序,这算是个接口吧)
else: # 如果密码不对
tk.messagebox.showerror("ACCESS DENIED", "INCORRECT PASSWORD") # 拒绝访问,密码错误
else: # 如果账户不存在
tk.messagebox.showerror("ACCESS DENIED", "ILLEGAL USER") # 拒绝访问,非法用户
file_log_in.close()
private_file.close() # 别忘记关文档,不然会告诉你什么叫做bug来了都不知道在哪,这个会影响进一步运行的,可能会出现文件无法读取的情况
except FileNotFoundError: # 如果没有对应的文件
sign_up() # 启动注册程序
def sign_up(): # 注册函数(在看这一部分时由衷建议跳过下面的save_user函数先看下面的界面建立过程,然后再返回来看save_user函数,因为逻辑上那样看是合理的,但写的时候不能那么写,会读不出函数)
def save_user(): # 注册信息写入函数
username_new_info = username_new_input.get() # 获取输入的账户,密码,确认密码,密保问题及答案
password_new_info = password_new_input.get()
password_confirm_info = password_confirm_input.get()
security_question_info = security_question_input.get()
answer_info = answer_input.get()
pattern_password = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[0-9a-zA-Z]{8,}$" # 可以检查是否包含8位以上并且同时具有大小写字母、数字的正则表达式
result_password = re.match(pattern_password, password_new_info) # 正则验证,返回True或False
if username_new_info == "" or password_new_info == "" or security_question_info == "" or answer_info == "": # 如果有信息为空
tk.messagebox.showinfo("", "Information can not be empty") # 需要重新输入
elif not result_password: # 如果不符合密码格式
tk.messagebox.showinfo("", "Password must contain more than 8 characters, \n"
"including numbers, upper and lower case letters.") # 提醒重输
elif password_new_info != password_confirm_info: # 如果前后密码不一致
tk.messagebox.showinfo("", "Inconsistent passwords.") # 打回去重新输入
else:
(pubkey, privkey) = rsa.newkeys(1024) # 如果一切安好,则启动密钥生成,这里就有一道防入侵机制,如果登录信息文档被删除并重建,那么密钥就会彻底更改,也就无法读取使用原公钥加密的信息
pub = pubkey.save_pkcs1() # 读取公钥
pub_file = open("public.pem", "w+") #新建(如果被入侵就会重建,抹除原有密钥的痕迹)密钥文档
pub_file.write(pub.decode()) # 解码后写入文档
pub_file.close() # 关闭文档
pri = privkey.save_pkcs1() # 私钥,一样的逻辑
pri_file = open("private.pem", "w+")
pri_file.write(pri.decode())
pri_file.close()
with open("public.pem") as public_file: # 打开公钥文档读取公钥
p = public_file.read()
pubkey_use = rsa.PublicKey.load_pkcs1(p.encode()) # 获取公钥
username_new_encode = rsa.encrypt(username_new_info.encode(), pubkey_use)
password_new_encode = rsa.encrypt(password_new_info.encode(), pubkey_use)
password_confirm_encode = rsa.encrypt(password_confirm_info.encode(), pubkey_use)
security_question_encode = rsa.encrypt(security_question_info.encode(), pubkey_use)
answer_encode = rsa.encrypt(answer_info.encode(), pubkey_use) # 加密所有信息
data_log_in = [username_new_encode, password_new_encode, password_confirm_encode, security_question_encode,
answer_encode] # 将加密后的信息写入列表,便于存储和读取
with open("file_log_in.pickle", "wb+") as file_log_in:
pickle.dump(data_log_in, file_log_in) # 新建登录信息存储文档
tk.messagebox.showinfo("", "SIGN UP SUCCESSFULLY") # 提醒注册成功
public_file.close()
file_log_in.close() # 关闭两个文件
window_sign_up.destroy() # 顺手把注册窗口抹掉
window_sign_up = tk.Tk() #一切都和一开始的那个主界面相似
window_sign_up.title("SIGN UP")
window_sign_up.geometry("350x320")
username_new = tk.Label(window_sign_up, text="user:") # 都是完全一样的逻辑
username_new.place(x=10, y=10)
username_new_str = tk.StringVar()
username_new_input = tk.Entry(window_sign_up, width=20, textvariable=username_new_str)
username_new_input.place(x=150, y=10)
password_new = tk.Label(window_sign_up, text="password:")
password_new.place(x=10, y=40)
password_new_str = tk.StringVar()
password_new_input = tk.Entry(window_sign_up, width=20, textvariable=password_new_str, show="*")
password_new_input.place(x=150, y=40)
password_confirm = tk.Label(window_sign_up, text="password:")
password_confirm.place(x=10, y=70)
password_confirm_str = tk.StringVar()
password_confirm_input = tk.Entry(window_sign_up, width=20, textvariable=password_confirm_str, show="*")
password_confirm_input.place(x=150, y=70)
tk.Label(window_sign_up, text="Password must contain more than 8 characters, \n"
"including numbers, upper and lower case letters").place(x=10, y=100)
security_question = tk.Label(window_sign_up, text="security question:")
security_question.place(x=10, y=160)
security_question_str = tk.StringVar()
security_question_input = tk.Entry(window_sign_up, width=20, textvariable=security_question_str)
security_question_input.place(x=150, y=160)
tk.Label(window_sign_up, text="Security question for situation that password is forgotten").place(x=10, y=190)
answer = tk.Label(window_sign_up, text="answer:")
answer.place(x=10, y=220)
answer_str = tk.StringVar()
answer_input = tk.Entry(window_sign_up, width=20, textvariable=answer_str)
answer_input.place(x=150, y=220)
confirm_sign_up = tk.Button(window_sign_up, text="SIGN UP", command=save_user)
confirm_sign_up.place(x=150, y=250) # 设置注册按键,并调用save_user函数
def forget_password_log_in(): # 忘记密码登录还是日常开文件
with open("file_log_in.pickle", "rb+") as file_log_in: #
data_log_in = pickle.load(file_log_in)
with open("private.pem") as private_file:
q = private_file.read()
privkey_use = rsa.PrivateKey.load_pkcs1(q.encode())
line_1_0 = rsa.decrypt(data_log_in[0], privkey_use) # 这三个分别是,用户名,密保问题,答案
line_4_0 = rsa.decrypt(data_log_in[3], privkey_use)
line_5_0 = rsa.decrypt(data_log_in[4], privkey_use)
line_1 = line_1_0.decode()
line_4 = line_4_0.decode()
line_5 = line_5_0.decode()
def forget_password_log_in_check(): # 设置密保问题检查
answer_info = answer_input.get() # 先把输入的答案读取出来
if answer_info == line_5: # 看看写的答案对不对?
tk.messagebox.showinfo("ACCESS PERMITTED", "%s, WELCOME" % line_1)
main() # 对的话,欢迎,启动主程序,还是把这里删了就能单独运行该程序
window_forget_password_log_in.destroy() # 顺手把这个已经没用的窗口关掉,我滴任务完成啦!
else: # 答案错误
tk.messagebox.showerror("ACCESS DENIED", "WRONG ANSWER") # 拒绝访问,答案错误
window_forget_password_log_in = tk.Tk() # 日常建立窗口
window_forget_password_log_in.title("FORGET PASSWORD")
window_forget_password_log_in.geometry("230x140")
tip = tk.Label(window_forget_password_log_in, text="Use your security question to log in.")
tip.place(x=10, y=10)
answer = tk.Label(window_forget_password_log_in, text="%s" % line_4) #完全一样的逻辑,只不过这里text是从文档里读出的密保问题,这个是支持中文的
answer.place(x=10, y=40)
answer_str = tk.StringVar()
answer_input = tk.Entry(window_forget_password_log_in, width=30, textvariable=answer_str, show="*")
answer_input.place(x=10, y=70)
forget_password_login = tk.Button(window_forget_password_log_in, text="LOG IN",
command=forget_password_log_in_check)
forget_password_login.place(x=80, y=100) # 登陆按键啥的都捯饬好
login = tk.Button(window, text="LOG IN", command=log_in) # 登录按钮,执行log_in函数
login.place(x=120, y=230)
logdown = tk.Button(window, text="LOG OUT", command=log_out) # 退出按钮,执行log_out函数
logdown.place(x=260, y=230)
login_with_questions = tk.Button(window, text=" FORGET \nPASSWORD?", command=forget_password_log_in) # 忘记密码登录,执行password_log_in函数
login_with_questions.place(x=350, y=180)
window.mainloop() # 主循环,是主界面的事情
程序运行效果图:
下一篇文章将对主程序部分进行描述,当然,我认为这部分程序可以说是具备独立运行的能力(把两行main()扔了),可以对接一些其他的小程序,就是做着玩嘛,加一个具有加密能力的登录系统岂不美哉,这格调不一下子就上来了嘛。当然,欢迎各方大佬不吝赐教,毕竟这程序在性能方面我并没有作优化,一个非计算机专业的大二学生,接触编程不过大半年,想做的很好大概是有些难度。但是各位的支持和指教将为我提供无穷的动力和不断提升的机会,在此感谢各位浏览者和提出建议者。
毁灭吧,赶紧的,累了