一、hashlib模块
hashlib模块是一个摘要算法模块,能够把一个字符串数据类型的变量转换成一个定长的、密文的字符串,字符串里的每一个字符都是一个十六进制数。
(一)、hashlib模块算法的共同特点
- 对于同一个字符串,不管字符串有多长,只要是相同的,无论在任何环境下,多少次执行,在任何语言中使用相同的算法/相同的手段的结果永远是相同的。
-
(二)、md5算法
优点:32个字符,相对于sha1算法的40个字符,md5算法普及率更高,计算速度更快
- 缺点:相对于sha1算法,md5算法没有sha1算法安全。不够安全不是因为算法本身,而是因为速度快,使用的人多,那么存在撞库的风险也就越大。
算法流程:
优点:40个字符,相对于md5算法更加安全
- 缺点:速度比md5更慢
- 算法流程:
- 创建md5对象:sha1_obj = hashlib.sha1()
- 更新对象:sha1_obj.update(str_obj.encode(‘utf-8’))
- 生成密文结果:sha1_obj.hexdigest()
- 打印
import hashlib
# md5是一个算法
str = 'lizhen122343254'
md5_obj = hashlib.md5()
md5_obj.update(str.encode('utf-8'))
ret = md5_obj.hexdigest()
print(ret, len(ret), type(ret))
# sha1算法
sha1_obj = hashlib.sha1()
sha1_obj.update(str.encode('utf-8'))
ret1 = sha1_obj.hexdigest()
print(ret1, len(ret1), type(ret1))
'''
9312639e4e17d01b0fc88d6a84e8cff3 32 <class 'str'>
06af75e90a56b268582388f99526c73faabca62e 40 <class 'str'>
'''
撞库:加密摘要算法主要用于处理存入数据库中的非常重要信息,例如:密码,当注册密码后,通过算法程序对注册的密码进行加密计算生成一个加密的唯一字符串存在数据库中,注意:不是把密码直接存在数据库。当登陆的时候,加密算法根据输入的密码生成加密字符串,把新生成的加密字符串与数据库中存入的唯一加密字符串比对,如果相等则允许登陆,如果不想等则不允许登陆,这是保护信息安全的重要一环。虽然加密算法不可逆,一旦加密就不可能通过加密字符串推出原有字符串,但是很多的不怀好意的人,专门创建数据库,存储自己编写的字符串生成的加密字符串,把这些加密字符串和别人数据库中的加密字符串比对,寻找相等的加密字符串,从而拿到原有字符串的内容,这个被称为撞库。
(四)、登陆的密文验证(针对撞库问题的解决办法)逐渐进阶
- 最初级的加密即是上边直接对一个字符串进行加密,这种加密通过撞库简单的常用的字符串很容易被破解
- 静态加盐:针对初级加密中的问题,进一步的解决办法是加盐,即在创建算法对象的时候直接加入固定字符串,并对字符串进行编码,然后再执行update()和hexdigest()方法,最后得到加盐后的加密字符串,这个字符串和单纯的一个字符串的加密字符传不一样,如果攻击者不知道加的盐是什么,密码就是安全的。但是如果攻击者找到了加的盐的内容,整个数据库都存在风险。
- 动态加盐:既然上边静态加盐也不安全,那么可以把盐换成动态的,即username为盐,password为需要加密的字符串,username和password完全相同的几率极小,这样的话账号就是安全的。
创建安全防护的时候必须用动态加盐的方式,前两种为推导的过程。
# 动态加盐
import sys
username = sys.argv[1]
pwd = sys.argv[2]
# 这里用的是sys模块中的argv交互,也可以使用input
def create_md5(username, pwd):
md5_obj = hashlib.md5(username.encode('utf-8'))
md5_obj.update(pwd.encode('utf-8'))
password = md5_obj.hexdigest()
return password
md5_password = create_md5(username, pwd)
if md5_password == '431149301c8792c1ab4c40b783d37100' and username ==username:
print('登陆成功')
else:
print('登陆失败')
(五)、文件的一致性校验问题
文件的一致性认证使用的也是hashlib模块中的加密算法,把‘文件句柄.read(每次读取字节)’作为update的参数来使用,其他的逻辑和和普通的算法一样。
需要注意的是:
- byte文件在read()后不需要使用encode(‘utf-8’)编码,json格式文件需要在read()后添加encode(‘utf-8’)编码
- 一次性从文件中读取内容的update和分批次、多次从同一文件中连续读取所有内容的update效果是完全一样的,得到的密文字符串也是完全一样的。
import os
import hashlib
'''创建一个函数读取文件并进行文件一致性校验'''
def sha1_num(path, bytes_num=100):
size_num = 0
# 字节数
file_size = os.path.getsize(path)
# os.path.getsize(path)获取文件总的字节数
sha1_obj = hashlib.sha1()
# 创建sha1加密对象
with open(path, 'rb') as f:
while size_num <= file_size:
# 当字节数小于等于总的字节数时执行下边操作
sha1_obj.update(f.read(bytes_num))
# hashlib模块中文章一次读取update的结果和分段读取update的结果是完全一样的
size_num += bytes_num
result = sha1_obj.hexdigest()
return result
s1 = sha1_num('../第三方库/pickle_dump', bytes_num=10240000)
s2 = sha1_num('../pickle_dump', bytes_num=10240)
# 两个同样的文件,放在不同的路径下,判断文件的一致性
if s1 == s2:
print(True)
# 密文相等则文件具有一致性
else:
print(False)
'''
True
'''
(六)、hmac模块
hmac模块是hashlib模块的简化版,基于hashlib模块创建的,在具体的方法使用上比hashlib少了一步。
- 基本的格式:
- import hmac
- md5_obj = hmac.new(盐,需要加密的部分)
- result = md5.digest()
需要特别注意的是:hashlib中的md5方法生成的密文结果是字符串,而hmac生成的结果却是bytes,另外这种方法虽然是基于hashlib的md5加密算法演变而来,但是方法不能混用。
import hmac
salt = b'randompassword'
r_s = 'sdfjhlasdhdsalhkl'.encode('utf-8')
md5_obj = hmac.new(salt, r_s)
result = md5_obj.digest()
二、logging日志模块
(一)、login可以显示的信息类型:
- 调试信息:logging.debug(‘调试信息’)
- 基础信息:logging.info(‘基础信息’)
- 警告信息:logging.warning(‘警告信息’)
- 错误信息:logging.error(‘错误信息’)
- 严重错误信息:logging.critical(‘严重错误/批判信息’)
如果不做任何的设置,日志默认只会显示警告、错误和严重错误三类信息,不会显示调试信息和基础信息。
(二)、日志设置
配置分类:
- 基础配置:使用basicConfig()来设置,特点是:操作简单,但是可定制化不高
- 高级配置:创建对象等来设置,特点是:操作有难度,但是可定制化很高。
1、基础配置
设置显示的日志需要在所有的信息类型之前添加logging.basicConfig()
basicConfig()的基本的参数:level 最低显示等级对应五种信息类型, 格式为:
- level=logging.DEBUG
- level=logging.INFO
- level=logging.WARNING
- level=logging.ERROR
- level=logging.CRITICAL
五个等级必须全部大写,在日常的工作中正常情况下最低的等级时INFO,除非是调试才会设置到DEBUG等级。
format日志输出内容格式化,具体的格式属性: | 字段/属性名称 | 使用格式 | 描述 | | :—-: | :—-: | :—-: | | asctime | %(asctime)s | 日志事件发生的时间—人类可读时间,如:2003-07-08 16:49:45,896 | | created | %(created)f | 日志事件发生的时间—时间戳,就是当时调用time.time()函数返回的值 | | relativeCreated | %(relativeCreated)d | 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的) | | msecs | %(msecs)d | 日志事件发生事件的毫秒部分 | | levelname | %(levelname)s | 该日志记录的文字形式的日志级别(’DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’) | | levelno | %(levelno)s | 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) | | name | %(name)s | 所使用的日志器名称,默认是’root’,因为默认使用的是 rootLogger | | message | %(message)s | 日志记录的文本内容,通过 msg % args计算得到的 | | pathname | %(pathname)s | 调用日志记录函数的源码文件的全路径 | | filename | %(filename)s | pathname的文件名部分,包含文件后缀 | | module | %(module)s | filename的名称部分,不包含后缀 | | lineno | %(lineno)d | 调用日志记录函数的源代码所在的行号 | | funcName | %(funcName)s | 调用日志记录函数的函数名 | | process | %(process)d | 进程ID | | processName | %(processName)s | 进程名称,Python 3.1新增 | | thread | %(thread)d | 线程ID | | threadName | %(thread)s 线程名称 | |
显示的日期格式:
datefmt = ‘%a, %d, %b %Y %H:%M:%S’日志存储的文件名:
格式为:filename = ‘path+日志名’
日志名:name.log, 后缀必须是log注意:如果添加filename = ‘path+日志名’,则往日志中添加,如果不写则是直接显示在屏幕上。
日志文件的操作模式:
格式为:filemode = ‘a/w’注意:第一次创建日志文件的时候必须用w,一旦创建后必须改成a,如果不写这一条,默认模式是a。
import logging
logging.basicConfig(
level=logging.INFO,
format = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s %(process)d',
datefmt = '%a, %d, %b %Y %H:%M:%S',
# 每一个参数之间必须用逗号隔开
filename = '../第三方库/test.log',
filemode = 'a'
)
logging.debug('调试信息')
logging.info('基础信息')
logging.warning('警告信息')
logging.error('错误信息')
logging.critical('批判/严重错误信息')
'''
Sun, 16, Feb 2020 09:01:10 logging日志模块.py [line:17] INFO 基础信息 3434
Sun, 16, Feb 2020 09:01:10 logging日志模块.py [line:18] WARNING 警告信息 3434
Sun, 16, Feb 2020 09:01:10 logging日志模块.py [line:19] ERROR 错误信息 3434
Sun, 16, Feb 2020 09:01:10 logging日志模块.py [line:20] CRITICAL 批判/严重错误信息 3434
'''
注意: 使用基础配置方式的时候,logging.basicConfig()和loggin.debug/info/warning/error/critical()两者缺一不可,basicConfig()必须写在文件开头,第二个部分信息内容需要写在具体的程序中。
2、logger对象的形式操作日志文件
通过创建对象的方式来实现可定制化
具体的流程:
创建logger对象:logger = logging.getLogger()
根据需要可以创建多个对象
创建文件管理对象符: fh = logging.FileHandler(‘logger对象.log’, encoding=’utf-8’)
- 设置显示日志信息的等级: logger.setLevel(logging.DEBUG/INFO/WARNING/ERROR/CRITICAL)
- 创建屏幕管理操作符: sh = logging.StreamHandler()
- 创建日志输出的格式(可以设置多个不同的格式): format = logging.Formatter(‘具体的显示格式’)
- 为上述四项建立联系
- 文件管理对象符 绑定一个 格式: fh.setFormatter(format)
- 屏幕管理对象符 绑定一个 格式: sh.setFormatter(format)
- logger对象 绑定 文件操作管理符(可以绑定多个文件管理操作符):logger对象.addHandler(fh)
- logger对象 绑定 屏幕操作管理符: logger对象.addHandler(sh)
- 设置显示的信息:
logger.debug(‘显示信息’)
logger.info(‘显示信息’)
logger.warning(‘显示信息’)
logger.error(‘显示信息’)
logger.critical(‘显示信息’)
# 创建logger对象
logger = logging.getLogger()
logger1 = logging.getLogger()
# 设置日志显示等级
logger.setLevel(logging.DEBUG)
# 创建屏幕管理操作符
sh = logging.StreamHandler()
# 创建文件管理操作符
fh = logging.FileHandler('logger.log', encoding = 'utf-8')
fh1 = logging.FileHandler('logger1.log', encoding = 'utf-8')
# 创建日志输出的格式(可以设置多个不同的格式)
format1 = logging.Formatter('%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s %(process)d')
format2 = logging.Formatter('%(asctime)s [line:%(lineno)d] %(levelname)s %(message)s %(process)d')
# 为上述四项建立联系
# 文件管理对象符 绑定一个 格式
fh.setFormatter(format1)
fh1.setFormatter(format2)
# 屏幕管理对象符 绑定一个 格式
sh.setFormatter(format2)
# logger对象 绑定 文件操作管理符(可以绑定多个文件管理操作符)
logger.addHandler(fh)
logger1.addHandler(fh1)
# logger对象 绑定 屏幕操作管理符
logger.addHandler(sh)
logger.debug('测试信息')
logger.info('测试信息')
logger.warning('测试信息')
logger.error('测试信息')
logger.critical('测试信息')
'''
# 文件:创建了logger.log和logger1.log
# 屏幕显示:
2020-02-16 09:55:06,050 [line:64] DEBUG 测试信息 5007
2020-02-16 09:55:06,052 [line:65] INFO 测试信息 5007
2020-02-16 09:55:06,052 [line:66] WARNING 测试信息 5007
2020-02-16 09:55:06,054 [line:67] ERROR 测试信息 5007
2020-02-16 09:55:06,055 [line:68] CRITICAL 测试信息 5007
'''
日志对象、文件操作管理符、屏幕操作管理符和日志格式都是可以设置多个的,设置几个根据需要来定