hashlib加密模块和logging模块,购物车项目
hashlib加密模块
简介
hashlib模块是一个提供了字符串加密功能的模块,包含MD5和SHA的加密算法。具体的加密支持有:
MD5,sha1,sha224,sha256, sha384, sha512, blake2b,blake2s,sha3_224, sha3_256,
sha3_384, sha3_512, shake_128, shake_256
该模块在用户登录认证方面应用广泛,对文本加密也很常见。文件和文件之间的校验。
基本调用
基本步骤,加密算法使用方法都一样的。以MD5加密为例:
1.创建一个MD5加密对象
2.对字符串进行转换为byte后,进行算法加密
3.进行16进制的显示
代码演示:
# 导入hashlib模块
import hashlib
# 实例化一个MD5的加密对象
md5 = hashlib.md5()
# 调用MD5对象的update方法,进行字符串加密(这里要传入的是编码后字节)
md5.update('hello'.encode('utf8'))
# 返回一个仅包含16进制数字的字符串
ret = md5.hexdigest()
# 打印加密返回后的字符串
print(ret)
5d41402abc4b2a76b9719d911017c592
可以创建的加密算法有:md5,sha1, sha224, sha256, sha384, sha512
# 实例化一个MD5加密对象
md5 = hashlib.md5()
# 实例化一个sha1加密对象
sha1 = hashlib.sha1()
# 实例化一个sha224加密对象
sha224 = hashlib.sha224()
# 实例化一个sha256加密对象
sha256 = hashlib.sha256()
如果数据量较大,可以分块多次调用update(),最后的计算结果都是一样的
md5 = hashlib.md5()
md5.update('春游去'.encode('utf8'))
md5.update('动物园'.encode('utf8'))
res = md5.hexdigest()
print(res)
dbc49f26ce915c5f7a13bba1c21679f2
new_md5 = hashlib.md5()
new_md5.update('春游去动物园'.encode('utf8'))
new_res = md5.hexdigest()
print(new_res)
dbc49f26ce915c5f7a13bba1c21679f2
加盐处理
简介
但是通常这样的密码就相当于一个固定的加密字符串,如果有的人专门写一个这样的字典,里面存储了各
种字符串组合及对应的MD5加密字符串,然后去暴力测试,这样也就有可能计算出我们真正的密码,所以,要
确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串
来实现,俗称“加盐”:
代码演示:
# 没有加盐的字符串加密
new_md5 = hashlib.md5()
new_md5.update('how to use md5 in python hashlib?'.encode('utf8'))
print(new_md5.hexdigest())
# 加盐以后的字符串加密
new_md5 = hashlib.md5('我的梦想是冲出地球!'.encode('utf8')) # 加盐
new_md5.update('how to use md5 in python hashlib?'.encode('utf8'))
print(new_md5.hexdigest())
# 结果
# d26a53750bc40b38b65a520292f69306
# 52841008c37295e291f426bbabe56f15
动态加盐
# 意义就是在加盐的地方使用一个变量比如,用户名
user = input('user>>: ')
pwd = input('pwd>>: ')
md5 = hashlib.md5('{0}的梦想是去全世界各种地方逛逛'.format(user).encode('utf8'))
md5.update(pwd.encode('utf8')) # 更新密码
print(md5.hexdigest())
校验文件时,由于文件过大,计算MD5值的方法:
def file_get_md5(file):
md5 = hashlib.md5()
with open(file, mode='rb') as fp: # 打开文件
for line in fp: # 循环,每次一行
md5.update(line) # 每次更新
return md5.hexdigest()
应用场景
1.密码加密如何比对
用户输入的还是明文但是到了程序里面之后会采用相同的加密算法变成密文
之后拿着密文与跟数据库里面的密文比对如果一致就是密码正确不一致就是错误
2.文件内容一致性校验
作为软件的提供者 我们在提供安全软件的同时会对给该软件内容做加密处理得到一个该安全软件独有的密文
用户在下载软件之后也会对内容做相同的加密之后比对两次密文是否一致
如果是表示中途没有被修改 如果不是表示中途被修改过 可能存在病毒
logging模块
简介
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:
1.可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息;
2.print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出;
基本使用
日志按照重要程度分为五个级别:默认只有达到warning警告级别及以上才会记录日志
logging.debug('debug message') # 10
logging.info('info message') # 20
logging.warning('warning message') # 30
logging.error('error message') # 40
logging.critical('critical message') # 50
配置logging基本的设置,然后在控制台输出日志
import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
logging.basicConfig函数各参数
filename:指定日志文件名;
filemode:和file函数意义相同,指定日志文件的打开模式,'w'或者'a';
format:指定输出的格式和内容,format可以输出很多有用的信息
参数:作用
%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(message)s:打印日志信息
datefmt:指定时间格式,同time.strftime()(格式化时间);
level:设置日志级别,默认为logging.WARNNING;
stream:指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到
sys.stderr,当stream和filename同时指定时,stream被忽略;
购物车项目
要求:
项目功能
1.用户注册
2.用户登录
3.添加购物车
4.结算购物车
项目说明
用户数据采用json格式存储到文件目录db下 一个用户一个单独的文件
数据格式 {"name":"jason","pwd":123}
'''密码必须加密后存储'''
ps:文件名可以直接用用户名便于校验
用户注册时给每个用户添加两个默认的键值对(账户余额 购物车)
{"balance":15000,"shop_car":{}}
添加购物车功能 商品列表可以自定义或者采用下列格式
good_list = [
['挂壁面',3]
['印度飞饼', 22]
['极品木瓜', 666],
['土耳其土豆', 999],
['伊拉克拌面', 1000],
['董卓戏张飞公仔', 2000],
['仿真玩偶', 10000]
]
用户可以反复添加商品,在购物车中记录数量
{'极品木瓜':[个数,单价]}
结算购物车
获取用户购物车中所有的商品计算总价并结算即可
一.分析要求,搭框架
拿到项目的第一步是分析要求,然后搭建起简单的框架。将我们可能需要用到的函数提前先定义了,函数体用pass填入,再将函数
名放入字典中,方便之后使用和追加函数,然后将主要的运行代码写好,再来进行函数体代码的编写。
![](https://s1.ax1x.com/2022/03/31/qf4BqS.png)
二.将函数的功能一个一个完善起来
判断db文件是否存在,如不存在则创建
if not os.path.exists(data_path):
os.mkdir(data_path)
注册
首先我们要获取用户输入的用户名和密码,并且密码要二次验证。如果2次密码不一致,则提醒用户密码不一致。然后使用os.path.join方法来拼出存储用户信息的文件路径,因为我们是使用用户名作为文件名的,所以我们可以判断这个路径下有没有相同的文件,如果有,则提示用户账号已存在,没有则先将密码md5加密并且使用动态加盐处理然后初始化一条用户数据,存入文件中。
#注册
def register():
# 获取用户名和密码
name = input('请输入你的用户名: ').strip()
pwd = input('请输入你的密码: ').strip()
pwd_again = input('再次确认密码: ').strip()
# 判断两次密码是否一致,不一致提醒用户
if pwd_again != pwd:
print('------两次密码不一致------')
return
# 使用os.path.join方法拼出存储用户信息的文件路径
user_file_path = os.path.join(data_path, f'{name}.json')
# 该用户文件存在,则提醒用户
if os.path.exists(user_file_path):
print('------用户已经存在------')
return
# 初始化用户数据
md5 = hashlib.md5()
md5.update(f'{name}用户的密码是{pwd}'.encode('utf8')) # 动态加盐
res = md5.hexdigest()
user_dict = {'name': name, 'pwd': res, 'balance': 15000, 'shop_car': {}}
# 将用户数据写入文件
open_file_write(user_file_path,user_dict)
print(f'------用户 {name} 注册成功------')
登入
先获取用户输入的用户名和密码,将用户名拼成的文件名在db目录中去查找,如果没有,则提示用户不存在,有就进行密码的比对。密码比对正确则登入成功。登入成功后将全局变量is_login字典中的name对应的值改为登入的用户名。
#登入
def login():
# 获取用户名和密码
name = input('请输入你的用户名: ').strip()
pwd = input('请输入你的密码: ').strip()
# 使用os.path.join方法拼出存储用户信息的文件路径
user_file_path = os.path.join(data_path, f'{name}.json')
# 判断数据库中是否有这个用户的数据,没有则提示用户
if not os.path.exists(user_file_path):
print('------用户不存在------')
return
# 将数据从文件中转成原数据格式
user_dict = open_file_read(user_file_path)
# 判断用户输入的密码是否正确
md5 = hashlib.md5()
md5.update(f'{name}用户的密码是{pwd}'.encode('utf8'))
res = md5.hexdigest()
if res == user_dict['pwd']:
print('------登入成功------')
# 记录已登入的用户
is_login['name'] = name
else:
print('------账号或密码错误------')
添加购物车
先向用户展示商品信息,然后我们可以先定义一个临时的空的购物车,将用户选购的商品临时添加到临时购物车。获取用户输入的商品编号和数量,然后将对应的商品名,数量和单价添加到临时购物车,当用户输入exit时退出购物,并且将临时购物车中的数据更新到对应用户的数据文件中。
#添加购物车
def add_shop_car():
good_list = [
['挂壁面', 3],
['印度飞饼', 22],
['极品木瓜', 666],
['土耳其土豆', 999],
['伊拉克拌面', 1000],
['董卓戏张飞公仔', 2000],
['仿真玩偶', 10000]
]
# 创建一个临时的购物车
shop_char = {}
while True:
# 循环打印出商品信息
for i, j in enumerate(good_list, start=0):
print(f'商品编号:{i} | 商品名:{j[0]} | 商品单价:{j[1]}')
# 获取用户输入的编号
chioce = input('请输入要购买的商品的编号,或输入exit退出购物: ').strip()
# 判断用户是否想退出购物,如要退出,将购物车加入到对应的用户数据中去
if chioce == 'exit':
# 拼出对应用户的数据文件路径
user_file_path = os.path.join(data_path, '%s.json' % is_login.get('name'))
# 将用户数据读出来
user_dict = open_file_read(user_file_path)
# 判断用户原来的购物车中是否有东西,有就增加,没有就添加新的键值对
for name in shop_char.keys():
if name in user_dict['shop_car'].keys():
user_dict['shop_car'][name][0] += shop_char[name][0]
else:
user_dict['shop_car'][name] = shop_char[name]
# 将用户信息重写回文件
open_file_write(user_file_path,user_dict)
print('------添加成功------')
break
# 如果用户输入的是商品编号,则准备开始添加
if int(chioce) in range(len(good_list)):
# 将商品名和单价分别取出来,放如两个变量
good_list_list = good_list[int(chioce)]
good_list_name = good_list_list[0]
good_list_money = good_list_list[1]
numb = int(input('请输入数量: ').strip())
if good_list_name in shop_char.keys():
# 购物车中已经有该商品,则添加数量
shop_char[good_list_name][0] += numb
else:
# 购物车没这个商品也就是第一次添加,则向零时购物车中新增键值对
shop_char[good_list_name] = [numb, good_list_money]
else:
print('------请输入数字编号------')
结算
先获取对应用户的余额,在计算购物车中的总价,如果总价大于余额,则提示用户余额不足,否则将余额减去总价,再将这些数据写回文件中。
def pay():
#获取用户余额
user_file_path = os.path.join(data_path, '%s.json' % is_login.get('name'))
user_dict = open_file_read(user_file_path)
user_money = user_dict['balance']
need_money = 0
# 计算需要的金额
for i in user_dict['shop_car'].values():
need_money += i[0] * i[1]
#如果总计大于余额提示用户额不足,否则结算成功,购物车清空
if need_money > user_money:
print('------余额不足------')
else:
user_dict['shop_car'] = {}
user_dict['balance'] = user_money-need_money
open_file_write(user_file_path,user_dict)
print('------结算成功------')
print(f'---本次消费:{need_money},账户余额:{user_money-need_money}---')
验证装饰器
装饰器,判断再进行购物车添加和结算时先判断用户是否已登入
def f1(func):
def f2(*args, **kwargs):
if is_login.get('name'):
res = func(*args, **kwargs)
return res
else:
print('------请先登入------')
login()
return f2
代码
import os
import json
import hashlib
# 获取执行文件的路径
yun_path = os.path.dirname(__file__)
# 使用os.path.join方法拼出db文件路径
data_path = os.path.join(yun_path, 'db')
# 定义一个全局变量,记录已登入的用户名
is_login = {'name': None}
# 判断db文件是否存在,如不存在则创建
if not os.path.exists(data_path):
os.mkdir(data_path)
# 装饰器,判断再进行购物车添加和结算时先判断用户是否已登入
def f1(func):
def f2(*args, **kwargs):
if is_login.get('name'):
res = func(*args, **kwargs)
return res
else:
print('------请先登入------')
login()
return f2
def open_file_read(path):
with open(path) as f1:
user_dict = json.load(f1)
return user_dict
def open_file_write(path,user_dict):
with open(path, 'w') as f1:
json.dump(user_dict, f1)
#注册
def register():
# 获取用户名和密码
name = input('请输入你的用户名: ').strip()
pwd = input('请输入你的密码: ').strip()
pwd_again = input('再次确认密码: ').strip()
# 判断两次密码是否一致,不一致提醒用户
if pwd_again != pwd:
print('------两次密码不一致------')
return
# 使用os.path.join方法拼出存储用户信息的文件路径
user_file_path = os.path.join(data_path, f'{name}.json')
# 该用户文件存在,则提醒用户
if os.path.exists(user_file_path):
print('------用户已经存在------')
return
# 初始化用户数据
md5 = hashlib.md5()
md5.update(f'{name}用户的密码是{pwd}'.encode('utf8')) # 动态加盐
res = md5.hexdigest()
user_dict = {'name': name, 'pwd': res, 'balance': 15000, 'shop_car': {}}
# 将用户数据写入文件
open_file_write(user_file_path,user_dict)
print(f'------用户 {name} 注册成功------')
#登入
def login():
# 获取用户名和密码
name = input('请输入你的用户名: ').strip()
pwd = input('请输入你的密码: ').strip()
# 使用os.path.join方法拼出存储用户信息的文件路径
user_file_path = os.path.join(data_path, f'{name}.json')
# 判断数据库中是否有这个用户的数据,没有则提示用户
if not os.path.exists(user_file_path):
print('------用户不存在------')
return
# 将数据从文件中转成原数据格式
user_dict = open_file_read(user_file_path)
# 判断用户输入的密码是否正确
md5 = hashlib.md5()
md5.update(f'{name}用户的密码是{pwd}'.encode('utf8'))
res = md5.hexdigest()
if res == user_dict['pwd']:
print('------登入成功------')
# 记录已登入的用户
is_login['name'] = name
else:
print('------账号或密码错误------')
@f1
def add_shop_car():
good_list = [
['挂壁面', 3],
['印度飞饼', 22],
['极品木瓜', 666],
['土耳其土豆', 999],
['伊拉克拌面', 1000],
['董卓戏张飞公仔', 2000],
['仿真玩偶', 10000]
]
# 创建一个临时的购物车
shop_char = {}
while True:
# 循环打印出商品信息
for i, j in enumerate(good_list, start=0):
print(f'商品编号:{i} | 商品名:{j[0]} | 商品单价:{j[1]}')
# 获取用户输入的编号
chioce = input('请输入要购买的商品的编号,或输入exit退出购物: ').strip()
# 判断用户是否想退出购物,如要退出,将购物车加入到对应的用户数据中去
if chioce == 'exit':
# 拼出对应用户的数据文件路径
user_file_path = os.path.join(data_path, '%s.json' % is_login.get('name'))
# 将用户数据读出来
user_dict = open_file_read(user_file_path)
# 判断用户原来的购物车中是否有东西,有就增加,没有就添加新的键值对
for name in shop_char.keys():
if name in user_dict['shop_car'].keys():
user_dict['shop_car'][name][0] += shop_char[name][0]
else:
user_dict['shop_car'][name] = shop_char[name]
# 将用户信息重写回文件
open_file_write(user_file_path,user_dict)
print('------添加成功------')
break
# 如果用户输入的是商品编号,则准备开始添加
if int(chioce) in range(len(good_list)):
# 将商品名和单价分别取出来,放如两个变量
good_list_list = good_list[int(chioce)]
good_list_name = good_list_list[0]
good_list_money = good_list_list[1]
numb = int(input('请输入数量: ').strip())
if good_list_name in shop_char.keys():
# 购物车中已经有该商品,则添加数量
shop_char[good_list_name][0] += numb
else:
# 购物车没这个商品也就是第一次添加,则向零时购物车中新增键值对
shop_char[good_list_name] = [numb, good_list_money]
else:
print('------请输入数字编号------')
@f1
def pay():
#获取用户余额
user_file_path = os.path.join(data_path, '%s.json' % is_login.get('name'))
user_dict = open_file_read(user_file_path)
user_money = user_dict['balance']
need_money = 0
# 计算需要的金额
for i in user_dict['shop_car'].values():
need_money += i[0] * i[1]
#如果总计大于余额提示用户额不足,否则结算成功,购物车清空
if need_money > user_money:
print('------余额不足------')
else:
user_dict['shop_car'] = {}
user_dict['balance'] = user_money-need_money
open_file_write(user_file_path,user_dict)
print('------结算成功------')
print(f'---本次消费:{need_money},账户余额:{user_money-need_money}---')
def login_out():
# 初始化,还原到未登入状态
is_login['name'] = None
print('------退出成功------')
# 将方法名放入字典方便调用
func_dict = {
'1': register,
'2': login,
'3': add_shop_car,
'4': pay,
'5': login_out
}
while True:
print('''
1.用户注册
2.用户登录
3.添加购物车
4.结算购物车
5.退出登入
''')
# 获取用户输入的指令
chioce = input('请输入要执行的指令: ').strip()
if chioce in func_dict.keys():
func_dict[chioce]()
else:
print('------请输入正确的指令------')