登录验证程序-[sys][datetime][json][hashlib]模块的运用
程序目录结构
├── account
│ └── 1234.json
└── bin
└── user_login.py
1234.json
{
"expire_date": "2020-01-01",
"id": 1234,
"status": 0,
"pay_day": 22,
"password": "950b80f9ec4a84bdb828400a404e55d5",
"salt": "eq8nXoV9Ndh6"
}
user_login.py
# -*- coding: utf-8 -*-
# @Author : 'hhjie'
# @Time : 2018-05-14 13:18
# @File : 综合练习-01---用户登录-json.py
"""
{"expire_date": "2021-01-01", "id": 1234, "status": 0, "pay_day": 22, "password": "abc"}
①:
1、用户名为json文件名,密码为 password。
2、判断是否过期,与expire_date进行对比。
3、登陆成功后,打印“登陆成功”,三次登陆失败,status值改为1,并且锁定账号。
分析:
1、用户名为json文件名,意思是每个用户对应一个json文件。如果用户输入用户名,则在用户文件夹中寻找是否包含该用户的用户文件。要有读取json文件的函数。
2、判断是否过期,意思为通过时间模块获取到年月日。和文件中的"expire_date"日期进行比对。由于是字符串类型。需要转换为时间戳。
用当前时间减去"expire_date"的时间。如果结果为负数或者0.则未过期,反之。如果大于0,则过期。
3、三次登录失败后,要修改状态。要写入文件。需要单独写一个写入文件的函数。锁定账号,就是在判断中,如果被锁定了,直接退出。
②:
三次验证的密码进行hashlib加密处理。即:json文件保存为md5的值,然后用md5的值进行验证。
独立的函数用hashlib处理密码加密
分析:
由于hashlib可通过反推表反推出字符串。所有我这处理的方式是将密码进行加盐操作,得到唯一的md5值。
这个'盐'利用随机字符生成,并保存在用户列表中。用变量 user_salt 表示。当然这个salt和用户之间在真实数据库中需要另外一张表对应。
"""
import os
import json
import string
import random
import datetime
import hashlib
def load_user_info(user_file):
user_info = json.load(open(user_file, mode='r', encoding='utf-8'))
return user_info
def dump_user_info(user_info, user_file):
temp_file = user_file + ".bak"
json.dump(user_info, open(temp_file, mode='w', encoding='utf-8'))
os.remove(user_file)
os.rename(temp_file, user_file)
def get_files_of_dir(dir_path):
all_files_dirs = os.listdir(dir_path)
return all_files_dirs
def is_expired(expire_date):
today = datetime.datetime.today()
expire_day = datetime.datetime.strptime(expire_date, "%Y-%m-%d")
# 注意:传入的参数时间字符串为:【"2021-01-01"】但是经过转换为元组时间时,变为【"2021-01-01 00:00:00"】
delta = today - expire_day # 结果为3 days, 0:00:00 这种形式的时间格式。其中有很多方法。days拿到天数。
if delta.days > 0: # 如果比对日期为正数的话,则过期了。返回True,意为过期为真。
print("您的账号已过期,请联系管理员")
return True
else:
print("账号离过期还剩【{0}】天".format(abs(delta.days)))
return False
def encrypt_password(password, salt=None):
# 两个功能,一是新用户创建时,未给定salt,则随机生成salt并加密密码,返回salt和hash值。二是非新用户登录时,从文件信息中获取到salt和密码加密
if not salt: # 如果salt没有值,就将随机字符串赋值给salt。
salt = ''.join(
random.sample(string.digits + string.ascii_letters, 12)) # 生成16位的随机字符串作为加密的salt。
password_hash = hashlib.md5()
password_hash.update((password + salt).encode('utf-8')) # 将密码和salt进行合并字符串进行加密
return salt, password_hash.hexdigest()
def start():
try_count = 0
exit_flag = False
while not exit_flag:
username = input("请输入用户名:")
username_file_name = username + ".json"
username_file_path = os.path.join(account_dir, username_file_name)
if username_file_name in get_files_of_dir(account_dir):
username_info = load_user_info(username_file_path) # 从用户json文件中获取用户信息
expire_date = username_info["expire_date"]
is_expire = is_expired(expire_date)
if is_expire:
exit_flag = True
while not exit_flag:
if username_info["status"] == 1:
print("您的账号已被锁定,请联系系统管理员。")
exit_flag = True
break
if try_count == 3: # 先判断尝试次数是否等于3次。
username_info["status"] = 1
dump_user_info(username_info, username_file_path) # 这里的文件一定要是绝对路径
print("尝试登录系统次数超限,账号已被锁定。")
exit_flag = True
break # 这里要有break,因为虽然这里为True,整个系统退出了,但是还会执行下面的代码,因此要用break。下面代码不继续执行了。
user_password = input("请输入密码: ")
user_encrypt_password = encrypt_password(user_password, username_info["salt"])[1] # 获取加密密码的hash值。
if user_encrypt_password == username_info["password"]:
print("欢迎您【{0}】,登录系统成功".format(username))
exit_flag = True
else:
print("密码错误,请重试...")
try_count += 1
left_try_count = 3 - try_count
if left_try_count == 0: # 如果剩余尝试次数为0的话,就跳过下面代码。
continue
print("还有{0}次尝试登录系统的机会!".format(left_try_count))
else:
print("用户名不存在,请重试")
if __name__ == '__main__':
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 获取当前绝对路径的目录名
account_dir = os.path.join(BASE_DIR, "account") # 用户账户目录
start()