zoukankan      html  css  js  c++  java
  • 模块二 hashlib模块、configparser模块、logging模块

    算法介绍

    Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

    什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

    摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

    摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

    我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

    import hashlib#引入摘要算法
     
    md5 = hashlib.md5()#md5算法的对象
    md5.update('how to use md5 in python hashlib?')#使用md5算法
    print md5.hexdigest()#获取摘要结果
    
    #计算结果如下:
    #d26a53750bc40b38b65a520292f69306
    摘要算法
    a = alex3714 ===摘要==> 174692740812ab238919
    alex3714 ===摘要==> 174692740812ab238919

    登录 md5 sha
    密码不能使用明文存储
    密文存储 摘要算法
    校验文件一致性 md5
    网络的上传下载
    保证多台机器状态的一致
    相同的字符串使用相同的算法 在任何时候 得到的结果都是一致的
    全世界的md5算法都是一样的
    123456 111111
    md5_obj = hashlib.md5()
    md5算法的对象
    撞库

    如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

    md5 = hashlib.md5()
    md5.update('how to use md5 in ')
    md5.update('python hashlib?')
    print md5.hexdigest()

    MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

    import hashlib#引入摘要算法
    md5_obj = hashlib.md5()# md5算法的对象
    md5_obj.update(b'alex3714')  # 使用md5摘要算法对'alex3714'进行摘要
    res = md5_obj.hexdigest()   # 获取摘要之后的结果
    print(res,type(res))  #aee949757a2e698417463d47acac93df    32位
    user = input('user : ')#输入用户名
    passwd = input('passwd : ')#输入密码
    md5_obj = hashlib.md5()#md5算法的对象
    md5_obj.update(passwd.encode('utf-8'))#使用md5摘要算法对'passwd用utf-8'进行摘要
    passwd = md5_obj.hexdigest()#passwd=获取摘要之后的结果
    if user == 'alex' and passwd == 'aee949757a2e698417463d47acac93df':#如果用户名等于'alex'and密码等于md5算法之后的密码
        print('登陆成功')

    SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

    #sha1和md5用法完全相同
    md5_obj = hashlib.sha1()#sha1算法的对象
    md5_obj.update(b'alex3714')  # 使用sha1算法的摘要算法对'alex3714'进行摘要
    res = md5_obj.hexdigest()   # 获取摘要之后的结果
    print(res)  #8a003668a9c990f15148f9e4046e1410781533b6  40

    摘要算法应用

    任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:

    name    | password
    --------+----------
    michael | 123456
    bob     | abc999
    alice   | alice2008

    如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:

    username | password
    ---------+---------------------------------
    michael  | e10adc3949ba59abbe56e057f20f883e
    bob      | 878ef96e86145580c38c87f0410ad153
    alice    | 99b1c2188db85afee403b1536010c2c9

    考虑这么个情况,很多用户喜欢用123456,888888,password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:

    'e10adc3949ba59abbe56e057f20f883e': '123456'
    '21218cca77804d2ba1922c33e0151105': '888888'
    '5f4dcc3b5aa765d61d8327deb882cf99': 'password'

    这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。

    对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护呢?

    由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

    hashlib.md5("salt".encode("utf8"))

    经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

    但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

    如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

    摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

    加盐

    全世界的md5算法都是一样的
    123456  111111
    md5_obj = hashlib.md5()
    md5算法的对象
    md5_obj.update(b
    '123456') # 使用md5摘要算法对'alex3714'进行摘要 res = md5_obj.hexdigest() # 获取摘要之后的结果 print(res,type(res)) #aee949757a2e698417463d47acac93df 32位 123456 e10adc3949ba59abbe56e057f20f883e 撞库 加盐 md5_obj = hashlib.md5(''.encode('utf-8')) # md5算法的对象 md5_obj.update(b'alex3714') # 使用md5摘要算法对'alex3714'进行摘要 res = md5_obj.hexdigest() # 获取摘要之后的结果 print(res,type(res)) aee949757a2e698417463d47acac93df 32位 0e249b9c16ea1d840ce700587cada978
    # 动态加盐  _ 校园管理系统
    username = 'alex'   # alex alex3714
                        # egon egon5068
    md5_obj = hashlib.md5(username.encode('utf-8')+''.encode('utf-8'))#加上一个 '盐'
    md5_obj.update(b'alex3714')#使用md5摘要算法对加了盐的alex
    res = md5_obj.hexdigest()#获取摘要结果
    print(res)
    # 校验文件一致性
    with open('userinfo','rb') as f:
        md5_obj = hashlib.md5()#md5算法的对象
        md5_obj.update(f.read())#使用md5算法对f文件进行摘要
        res = md5_obj.hexdigest()#获取摘要结果
        print(res)
    #
    with open('userinfo','rb') as f:
        md5_obj = hashlib.md5()
        for line in f:
            md5_obj.update(line)   # update操作可以在hexdigest之前执行多次
                                   # 分次对一个长字符串进行摘要
        res = md5_obj.hexdigest()  # 结果是对整个长字符串的摘要结果
        print(res)
    
    #56fc9aa78c2dd71d547988b24bec198a
    md5_obj = hashlib.md5()
    md5_obj.update(b'aaabbb')#bytes数据类型
    res = md5_obj.hexdigest()
    print(res)  #6547436690a26a399603a7096e876a2d
    
    md5_obj = hashlib.md5()
    md5_obj.update(b'aa')
    md5_obj.update(b'abbb')
    res = md5_obj.hexdigest()
    print(res)  #6547436690a26a399603a7096e876a2d
    #执行两次字符串相拼接得到的结果和一次执行一部分的结果相同

    configparser模块

    该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)。

    .py  里面的所有值 都不需要进行转换或者处理 直接当做变量使用
           通用性不高
    文本格式 key = value
            都要进行文件处理 _ 通用
    ini
    [北京校区]     # section
    课程 = python,linux   # option
    python讲师 = egon,yuanhao,nezha,boss_gold
    linux讲师 = 李导,何首乌
    [上海校区]
    课程 = go,linux
    python讲师 = egon
    linux讲师 = 李导,何首乌

    创建文件

    来看一个好多软件的常见文档格式如下:

    [DEFAULT]
    ServerAliveInterval = 45
    Compression = yes
    CompressionLevel = 9
    ForwardX11 = yes
      
    [bitbucket.org]
    User = hg
      
    [topsecret.server.com]
    Port = 50022
    ForwardX11 = no

     如果想用python生成一个这样的文档怎么做呢?

    import configparser
    config = configparser.ConfigParser()#配置文件模块中的一个ConfigParser()类
    # config 是一个操作配置文件的对象
    config["DEFAULT"] = {'ServerAliveInterval': '45',
                          'Compression': 'yes',
                         'CompressionLevel': '9',
                         'ForwardX11':'yes'
                         }
    #[]ini文件内部中括号内添加的是一个组名
    config['bitbucket.org'] = {'User':'hg'}
    config['topsecret.server.com'] = {'Host Port':'50022',
                                      'ForwardX11':'no'}
    with open('example.ini', 'w') as configfile:#打开exanple.ini的文件必须以ini结尾,以写的方式打卡
       config.write(configfile)#如果没有则配置,有则删除并写入
    #生成一个ini的文件

     

      

    查找文件

    import configparser
    # 引入模块
    config = configparser.ConfigParser()#实例化一个对象
    # print(config.sections())     # []   .sections()方法可以查看配置文件中的所有的组
    config.read('example.ini')     # 读取'example.ini'文件
    print(config.sections())
    #   ['bitbucket.org', 'topsecret.server.com'] 显示组名但是不能显示[DEFAULT]组
    print('bytebong.com' in config) # 返回False #config相当于一个大字典
    print('bitbucket.org' in config) # 返回True
    print(config['bitbucket.org']["user"])  #hg 显示配置文件,某一个组内key对应的值
    print(config['DEFAULT']['Compression']) #yes配置文件不区分大小写
    print(config['topsecret.server.com']['ForwardX11'])  #no
    #
    print(config['bitbucket.org'])          #<Section: bitbucket.org> 通过组名查看下面所有的项
    #
    for key in config['bitbucket.org']:     # 注意,有default会默认default的键
        print(key)
    #
    print(config.options('bitbucket.org'))  # 同for循环,找到'bitbucket.org'下所有键
    print(config.items('bitbucket.org'))    #找到'bitbucket.org'下所有键值对
    print(config.get('bitbucket.org','compression')) # yes       get方法Section下的key对应的value

    增删改操作

    import configparser
    config = configparser.ConfigParser()  #创建一个对象
    config.read('example.ini')  #让这个对象读example.ini文件
    config.add_section('yuan') #增加一个组
    config.remove_section('bitbucket.org')#删除一个组
    config.remove_option('topsecret.server.com',"forwardx11")#删除组下的一个项
    config.set('topsecret.server.com','k1','11111')#设置一个组下的一个项的值
    config.set('yuan','k2','22222')#在新组里边增添加了一个增加
    config.write(open('example.ini', "w"))#把内存里的东西重新写到example.ini文件里

    logging模块

    logging
    操作日志的模块
    什么叫日志
    给用户看的
    用户的重要行为
    登录 涉及安全
    账单 钱
    给开发和运维和测试人员看的
    自测 logging.debug('一些中间结果')
    测试 1++++++1
    运维
    记录
    打印在屏幕上
    写入文件里
    logging的优势
    格式更加规范
    等级更加鲜明

    函数式简单配置

    import logging  
    logging.debug('debug message')  #调试
    logging.info('info message')  #信息
    logging.warning('warning message')  #警告
    logging.error('error message')  #错误
    logging.critical('critical message') #严重错误

    默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

    灵活配置日志级别,日志格式,输出位置:

    logging.basicConfig(level=logging.ERROR,  
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                        datefmt='%a, %d %b %Y %H:%M:%S',
                        filename='test.log',
                        filemode='a')
    logging.debug('debug message')  # 调试
    logging.info('info message')    # 信息
    logging.warning('warning message')  # 警告
    logging.error('error message')      # 错误
    logging.critical('critical message')  # 严重错误

    配置参数:

    logger对象配置

    # 使用logger对象的用法
    import logging
    # 首先创建一个logger对象
    logger = logging.getLogger()
    #创建一个格式
    fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # 创建一个 文件句柄 控制向哪个文件中输出 用什么格式
    fh = logging.FileHandler('test3.log',encoding='utf-8')#创建文件句柄
    fh.setFormatter(fmt)
    # 创建一个 屏幕句柄 控制向屏幕输出 用什么格式
    sh = logging.StreamHandler()
    sh.setFormatter(fmt)
    # 将logger对象和文件句柄,屏幕句柄绑在一起
    logger.addHandler(fh)#logger对象可以添加多个fh和ch对象 
    logger.addHandler(sh)
    logger.setLevel(logging.DEBUG)     # 首先必须要整体对logger进行设置
    sh.setLevel(logging.INFO)  #设计sh等级
    fh.setLevel(logging.WARNING)  #设计fh等级
    logger.debug('logger debug message')  #调试等级
    logger.info('logger info message')  #信息等级
    logger.warning('logger warning message')  #预警等级
    logger.error('logger error message')  #错误等级
    logger.critical('logger critical message')  #严重错误

    logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过

    fh.setLevel(logging.Debug)单对文件流设置某个级别。

     

  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/chongdongxiaoyu/p/8568014.html
Copyright © 2011-2022 走看看