python之常用模块二(hashlib logging configparser)

时间:2022-02-20 05:12:08
摘要:hashlib *****      logging *****       configparser *

一、hashlib模块
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。 特点:
这个算法是不可逆,就是说经过这个算法处理后的保存数据,不能还原为原来的数据,
不同的字符串通过这个算法的计算得到的密文总是不同的,
即使是不同的语言不同的环境(操作系统、版本、时间)只要是用相同的算法处理相同的字符串,那么获得的结果总是相同的。 md5算法 :32位16进制的数字字符组成的字符串
应用最广泛的摘要算法
效率高,相对不复杂,如果只是传统摘要不安全
sha算法 :40位的16进制的数字字符组成的字符串
sha算法要比md5算法更复杂
且shan n的数字越大算法越复杂,耗时越久,结果越长,越安全 应用场景:
1,登录验证
2,确认通过网络传输的数据是否完整(如网上下载安装包等) 1、MD5算法:速度很快,生成的结果是固定的长度(128 bit(128位二进制),用32位的16进制字符串表示)
1-1、计算出一个字符串的MD5值
import hashlib # 导入hashlib模块
md5_obj = hashlib.md5() # 实例化一个md5算法对象
md5_obj.update('hello,world!'.encode('utf-8')) # update里面是要处理的数据(但必须是bytes类型)
ret = md5_obj.hexdigest() # 调用hexdigest生成结果
print(ret) # 结果:c0e84e870874dd37ed0d164c7986f03a 1-2、如果数据量很大,可以分块多次调用update()
import hashlib
md5_obj = hashlib.md5()
md5_obj.update('hello,'.encode('utf-8'))
md5_obj.update('world!'.encode('utf-8'))
ret = md5_obj.hexdigest()
print(ret) # 结果:c0e84e870874dd37ed0d164c7986f03a 1-3、登录验证
试想如果用户的账号密码等信息都是以明文的形式存储在数据库中,如果数据库泄露,
那么用户的密码立刻就泄露了,只要截取到你数据库信息的人都能登录你所有的用户,
这样以明文的形式存储密码是极其不安全的,所以正确的存储方式是摘要的形式存储,
比如MD5、SHA1等。
原数据库:
xiaoming | 123456
zhangsan | 666
wangxiaoer | 888
(这样的话泄露数据库就把所有信息都泄露了) MD5:
xiaoming | e10adc3949ba59abbe56e057f20f883e
zhangsan | fae0b27c451c728867a567e8c1bb4e53
wangxiaoer | 0a113ef6b61820daa5611c870ed8d5ee
(这样存储的话,即使数据库泄露了,也没人知道是什么密码) # 那么用户登录的时候,只需要把用户输入的密码进行MD5算法,再与存储的MD5值比较,
# 相同的就登录成功
import hashlib
def get_md5(s):
md5_obj = hashlib.md5()
md5_obj.update(s.encode('utf-8'))
return md5_obj.hexdigest() def login():
username = input('>>>:')
passwd = input('>>>:')
with open('user_info.txt',encoding='utf-8',mode='r') as f:
for line in f:
usr,pwd = line.strip().split('|')
if username == usr and get_md5(passwd) == pwd:
print('登录成功!')
break
else:
print('登录失败!')
login() (小知识:如果很早之前就使用过找回密码这个功能的人应该有印象,当年找回密码,管理员真的会把你原密码返回给你的,
因为当时还没有对密码进行算法的加密,所以当时的数据是很不安全的,但是现在呢,你使用找回密码的功能,一般只会让你
重新设置密码,并不会返回你之前的密码给你,因为现在的密码都是经过算法加密后的,管理员都不知道密码的明文是什么,
给你返回一串数据你也看不懂,所以都是让你直接改新的密码,然后管理员就把新密码跟新到数据库中。) 1-4、加盐
上述的方法是不是看起来很完美了?还是太年轻了,虽然MD5算法是不可逆的,但是,很多用户喜欢用123456,888888等
简单的密码,于是,黑客可以事先计算出一些常用密码的MD5值,得到一个反推表:
'e10adc3949ba59abbe56e057f20f883e':''
'fae0b27c451c728867a567e8c1bb4e53':''
'0a113ef6b61820daa5611c870ed8d5ee':''
这样,无需破解,黑客只需要对比他们制作的MD5数据库,就可以破解一些常用的密码的用户账号,俗称撞库。 那么如何加强对密码的保护呢?可以使用‘加盐的方法’
什么是加盐?
就是在对密码进行MD5算法的时候,为密码增加一个复杂的字符串,形成新的MD5值,那样即使是使用简单的密码,
只要这个'盐'的值不被黑客知道,那么他们也是破解不了密码的。比如:
import hashlib
md5_obj = hashlib.md5('密码保护'.encode('utf-8'))
md5_obj.update(''.encode('utf-8'))
ret = md5_obj.hexdigest()
print(ret) # 88e7b7b01cb04043824d5c65f9455dd1(这个值的明文就相当于:'密码保护123456') (原始的123456的MD5值是:e10adc3949ba59abbe56e057f20f883e)
用户在登录验证的时候,只要我们的程序默认把'盐'的值加进去进行验证,那么用户还是只需要输入123456就可以正常登录。 1-5、动态加盐
有了加盐之后,是不是觉得又完美了?哎,还是年轻啊,虽然加盐后的密码很难破解了,但是如果有人获取了你的数据库,
然后去你的网站恶意注册账号,注册的密码是那些简单的密码(123456,666,999等),当他注册了很多这样的账号后,
他可以自己加盐去尝试,如果加了某个盐后的密码123456跟他窃取的数据库上的某个记录一致,那么这个'盐'就被知道了,
那么他就可以把所有简单的密码又给破解了。 所以这个时候就需要用到动态加盐,每个密码所加的盐都不是固定的(可以为密码加上用户名,或者用户名的第一个字符,最后一个字符等)
看看例子吧:
import hashlib
username = input('>>>:')
passwd = input('>>>:')
md5_obj = hashlib.md5(username.encode('utf-8'))
md5_obj.update(passwd.encode('utf-8'))
ret = md5_obj.hexdigest()
print(ret) 1-6、文件的一致性校验
我们在网上下载文件(视频、安装包等)的时候,如果下载的文件不完整,就无法使用,而我们往往下载好一个文件的时候,电脑都会自动给我们
检查下载的文件是否完整,那么判断下载的文件是否这个步骤是怎么执行的呢?
其实要下载的文件都会有一个原文件的MD5值,当我们把文件下载到电脑后,系统会帮我们计算文件的MD5,然后对比原文件的MD5值,
如果MD5不一致,就证明下载的文件不完整(丢帧)。
(校验文件的MD5就不需要加盐了,因为本身就只是判断文件是否完整,就是公开的值)
我们这里看看如何计算一个文件的MD5值: import os
import hashlib
file_path = r'你下载好的文件的路径'
def get_file_MD5(file_path,buffer=1024): # buffer:默认每次读取的大小
md5_obj = hashlib.md5()
file_size = os.path.getsize(file_path) # 文件的大小
with open(file_path,mode='rb') as f:
while file_size:
content = f.read(buffer)
file_size -= len(content)
md5_obj.update(content)
return md5_obj.hexdigest()
print(get_file_MD5(file_path)) 2、sha算法
2-1、定义:SHA算法是一个系列的算法,有SHA1、SHA224、SHA256、SHA384、SHA512等,
使用较多的是SHA1,SHA1的结果是160 bit,通常用一个40位的16进制字符串表示。
SHA1、SHA224、SHA256、SHA384、SHA512等都比SHA1更安全,不过越安全的算法越慢,而且摘要长度更长。 2-2、SHA的调用和MD5完全类似
import hashlib
sha_obj = hashlib.sha1()
sha_obj.update('hello,world!'.encode('utf-8'))
ret = sha_obj.hexdigest()
print(ret) # 4518135c05e0706c0a34168996517bb3f28d94b5 二、logging模块
1、说明
正规的开发所有的程序都必须记录日志
日志的作用:
1.给用户看的,例如:
购物软件
视频软件
银行卡
2.给内部人员看的
2.1给技术人员看的
计算器
500个小表达式
一些计算过程、或者是一些操作过程需要记录下来
程序出现bug的时候 来帮助我们记录过程 排除错误
2.2给非技术人员看的
学校、公司的软件
谁在什么时候做了什么事儿,增删改查操作(尤其是删除操作) 2、logging的级别
import logging
logging.debug('debug message') # 计算或者工作的细节
logging.info('info message') # 记录一些用户的增删改查的操作
logging.warning('warning operation') # 警告操作
logging.error('error message') # 错误操作
logging.critical('critical message') # 批判的 直接导致程序出错退出的 结果:
WARNING:root:warning operation
ERROR:root:error message
CRITICAL:root:critical message 从上面可以看出:
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于warning级别的日志,
这说明默认的日志级别设置为WARNING(日志级别等级critical > error > warning > info > debug),
默认的日志格式为日志级别:level级别:用户:输出消息。
  3、简单配置:logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M', # 把asctime的时间格式改为:年-月-日 时:分
filename='test.log',
filemode='w') logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
(简单配置中文显示的是乱码) 参数说明:
level:设置logger的日志级别(大于等于这个参数的级别都会显示)
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),
默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 format参数中可能用到的格式化串:
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒(可用datefmt修改默认日期时间格式。)
%(filename)s 调用日志输出函数的模块的文件名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(levelname)s 日志级别
%(message)s用户输出的消息
%(name)s 用户的名字 %(levelno)s 数字形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有 4、对象的配置:解决中文问题、同时向文件和屏幕输出内容
import logging
# 先创建一个log对象 logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # 设置显示级别 # 还要创建一个控制文件输出的文件操作符
fh = logging.FileHandler('mylog.log',encoding='utf-8') # 指定编码可以解决中文乱码问题 # 还要创建一个控制屏幕输出的屏幕操作符
sh = logging.StreamHandler() # 要创建一个格式(多个格式也可以)
fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s [line:%(lineno)d]') # 文件操作符绑定一个格式
fh.setFormatter(fmt)
# fh.setLevel(logging.WARNING) # 可以设置写入文件的级别,不设置的话就默认用log对象设置的级别(一般全部级别的信息都写进去) # 屏幕操作符绑定一个格式
sh.setFormatter(fmt)
sh.setLevel(logging.WARNING) # 可以设置屏幕打印的级别,不设置的话就默认用log对象设置的级别
#但是不能设置比log对象设置的级别低,否则只会执行log对象的级别,比如:log对象设置的级别是WARNING,你这里设置INFO,级别
#没有WARNING高,不会显示 # logger对象来绑定:文件操作符, 屏幕操作符
logger.addHandler(sh)
logger.addHandler(fh) logger.debug('debug message') # 计算或者工作的细节
logger.info('info message') # 记录一些用户的增删改查的操作
logger.warning('warning message') # 警告操作
logger.error('error message') # 错误操作
logger.critical('critical message') # 批判的 直接导致程序出错退出的 三、configparser模块:配置文件模块
该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)
1、创建配置文件:
import configparser
config = configparser.ConfigParser() #实例化一个对象 config["DEFAULT"] = {'ServerAliveInterval': '', # 中括号里面的是组名,后面的值是字典,[DEFAULT]组可用被其他组调用
'Compression': 'hello', #字典里面的字母,写入配置文件后均变为小写字母
} config['bit.org'] = {'User':'Big_rookie'} config['topsecret.server.com'] = {'Host Port':'','ForwardX11':'no'} with open('example.ini', 'w') as configfile: # 把刚才创建的配置信息写入文件
config.write(configfile) # 对象.write(文件句柄) 2、查
import configparser config = configparser.ConfigParser() # 实例化一个对象
config.read('example.ini') # 读取配置文件信息
print(config.sections()) # 打印配置文件的组名(DEFAULT默认不显示):['bitbucket.org', 'topsecret.server.com'] print('bytebong.com' in config) # 判断bytebong.com这个组是否在配置文件中:False
print('bit.org' in config) # 判断bitbucket.org这个组是否在配置文件中:True
print(config['bit.org']["user"]) # 打印bit.org组下的user的值:Big_rookie
print(config['DEFAULT']['compression']) # 打印DEFAULT组下的compression的值:hello
print(config['topsecret.server.com']['compression']) #打印topsecret.server.com组下的compression的值(会去DEFAULT组找):hello
print(config['bit.org']) # 打印组名<Section: bitbucket.org>
for key in config['bit.org']: # 循环输出bit.org组的键,有default也会打印default的键
print(key)
print(config.options('bit.org')) # 跟for循环一样,打印'bit.org'下所有键
print(config.items('bit.org')) #打印'bit.org'下所有键值对
print(config.get('bit.org','user')) # get方法:找到bit.org下的user对应的值 3、增删改
import configparser
config = configparser.ConfigParser() config.read('example.ini') config.add_section('person') # 增加组 config.remove_section('bit.org') # 删除组
config.remove_option('topsecret.server.com',"forwardx11") # 删除某个组的某个值 config.set('topsecret.server.com','k1','') # 为某个组设置一个键值对
config.set('person','k2','') with open('newwxample.ini', "w") as f:
config.write(f) # 把修改的内容写进新的文件(旧文件没有改动的会原样写到新文件中)