2018年-04月26日 python hashlib模塊、IO模塊、JSON模塊

hashlib模塊

摘要算法簡介

  • Python的hashlib提供了常見的摘要算法,如MD5,SHA1等等。
  • 什麼是摘要算法呢?摘要算法又稱哈希算法、散列算法。它經過一個函數,把任意長度的數據轉換爲一個長度固定的數據串(一般用16進制的字符串表示)。
  • 舉個例子,你寫了一篇文章,內容是一個字符串'how to use python hashlib - by Michael',並附上這篇文章的摘要是'2d73d4f15c0db7f5ecb321b6a65e5d6d'。若是有人篡改了你的文章,並發表爲'how to use python hashlib - by Bob',你能夠一會兒指出Bob篡改了你的文章,由於根據'how to use python hashlib - by Bob'計算出的摘要不一樣於原始文章的摘要。
  • 可見,摘要算法就是經過摘要函數f()對任意長度的數據data計算出固定長度的摘要digest,目的是爲了發現原始數據是否被人篡改過。
  • 摘要算法之因此能指出數據是否被篡改過,就是由於摘要函數是一個單向函數,計算f(data)很容易,但經過digest反推data卻很是困難。並且,對原始數據作一個bit的修改,都會致使計算出的摘要徹底不一樣。
  • 咱們以常見的摘要算法MD5爲例,計算出一個字符串的MD5值:python

    import hashlib
    
    md5 = hashlib.md5()
    md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
    print(md5.hexdigest())
    
    ## 計算結果以下
    d26a53750bc40b38b65a520292f69306
  • 若是數據量很大,能夠分塊屢次調用update(),最後計算的結果是同樣的:
    import hashlib
    
    md5 = hashlib.md5()
    md5.update('how to use md5 in '.encode('utf-8'))
    md5.update('python hashlib?'.encode('utf-8'))
    print(md5.hexdigest())
  • 試試改動一個字母,看看計算的結果是否徹底不一樣。
  • MD5是最多見的摘要算法,速度很快,生成結果是固定的128 bit字節,一般用一個32位的16進制字符串表示。
  • 另外一種常見的摘要算法是SHA1,調用SHA1和調用MD5徹底相似:
    import hashlib
    
    sha1 = hashlib.sha1()
    sha1.update('how to use sha1 in '.encode('utf-8'))
    sha1.update('python hashlib?'.encode('utf-8'))
    print(sha1.hexdigest())
  • SHA1的結果是160 bit字節,一般用一個40位的16進制字符串表示。
  • 比SHA1更安全的算法是SHA256和SHA512,不過越安全的算法不只越慢,並且摘要長度更長。
  • 有沒有可能兩個不一樣的數據經過某個摘要算法獲得了相同的摘要?徹底有可能,由於任何摘要算法都是把無限多的數據集合映射到一個有限的集合中。這種狀況稱爲碰撞,好比Bob試圖根據你的摘要反推出一篇文章'how to learn hashlib in python - by Bob',而且這篇文章的摘要剛好和你的文章徹底一致,這種狀況也並不是不可能出現,可是很是很是困難。

 

摘要算法應用

  • 摘要算法能應用到什麼地方?舉個經常使用例子:
  • 任何容許用戶登陸的網站都會存儲用戶登陸的用戶名和口令。如何存儲用戶名和口令呢?方法是存到數據庫表中:

        image

  • 若是以明文保存用戶口令,若是數據庫泄露,全部用戶的口令就落入黑客的手裏。此外,網站運維人員是能夠訪問數據庫的,也就是能獲取到全部用戶的口令。
  • 正確的保存口令的方式是不存儲用戶的明文口令,而是存儲用戶口令的摘要,好比MD5:

        image

  • 當用戶登陸時,首先計算用戶輸入的明文口令的MD5,而後和數據庫存儲的MD5對比,若是一致,說明口令輸入正確,若是不一致,口令確定錯誤。

 

實例

  • 根據用戶輸入的口令,計算出存儲在數據庫中的MD5口令:
    def calc_md5(password):
        pass
  • 存儲MD5的好處是即便運維人員能訪問數據庫,也沒法獲知用戶的明文口令。
  • 設計一個驗證用戶登陸的函數,根據用戶輸入的口令是否正確,返回True或False:
    """
    michael:123456
    bob:abc999
    alice:alice2008
    """
    import hashlib
    db = {
        'michael': 'e10adc3949ba59abbe56e057f20f883e',
        'bob': '878ef96e86145580c38c87f0410ad153',
        'alice': '99b1c2188db85afee403b1536010c2c9'
    }
    def login(user, password):
        md5 = hashlib.md5()
        md5.update(password.encode('utf-8'))
        if db[user] == md5.hexdigest():
            return True
        else:
            return False
    
    print(login('michael', '123456'))
    print(login('bob', 'abc999'))
    print(login('alice', 'alice2008'))
    print(login('michael', '1234567'))
    print(login('bob', '123456'))
    print(login('alice', 'Alice2008'))

 

「加鹽」

  • 採用MD5存儲口令是否就必定安全呢?也不必定。假設你是一個黑客,已經拿到了存儲MD5口令的數據庫,如何經過MD5反推用戶的明文口令呢?暴力破解費事費力,真正的黑客不會這麼幹。
  • 考慮這麼個狀況,不少用戶喜歡用123456888888password這些簡單的口令,因而,黑客能夠事先計算出這些經常使用口令的MD5值,獲得一個反推表:
    'e10adc3949ba59abbe56e057f20f883e': '123456'
    '21218cca77804d2ba1922c33e0151105': '888888'
    '5f4dcc3b5aa765d61d8327deb882cf99': 'password'
  • 這樣,無需破解,只須要對比數據庫的MD5,黑客就得到了使用經常使用口令的用戶帳號。
  • 對於用戶來說,固然不要使用過於簡單的口令。可是,咱們可否在程序設計上對簡單口令增強保護呢?
  • 因爲經常使用口令的MD5值很容易被計算出來,因此,要確保存儲的用戶口令不是那些已經被計算出來的經常使用口令的MD5,這一方法經過對原始口令加一個複雜字符串來實現,俗稱「加鹽」:
    def calc_md5(password):
        return get_md5(password + 'the-Salt')
  • 通過Salt處理的MD5口令,只要Salt不被黑客知道,即便用戶輸入簡單口令,也很難經過MD5反推明文口令。
  • 可是若是有兩個用戶都使用了相同的簡單口令好比123456,在數據庫中,將存儲兩條相同的MD5值,這說明這兩個用戶的口令是同樣的。有沒有辦法讓使用相同口令的用戶存儲不一樣的MD5呢?
  • 若是假定用戶沒法修改登陸名,就能夠經過把登陸名做爲Salt的一部分來計算MD5,從而實現相同口令的用戶也存儲不一樣的MD5。

 

實例

    • 根據用戶輸入的登陸名和口令模擬用戶註冊,計算更安全的MD5:
      import hashlib, random
      
      def get_md5(s):
          return hashlib.md5(s.encode('utf-8')).hexdigest()
      
      class User(object):
          def __init__(self, username, password):
              self.username = username
              self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)])
              self.password = get_md5(password + self.salt)
      db = {
          'michael': User('michael', '123456'),
          'bob': User('bob', 'abc999'),
          'alice': User('alice', 'alice2008')
      }
      
      def login(username,password):
          ss=get_md5(password +db[username].salt)
          if db[username].password==str(ss):
              return True
          else:
              return False
      
      print(login('michael', '123456'))
      print(login('bob', 'abc999'))
      print(login('alice', 'alice2008'))
      print(login('michael', '1234567'))
      print(login('bob', '123456'))
      print(login('alice', 'Alice2008'))
 

小結

  • 摘要算法在不少地方都有普遍的應用。要注意摘要算法不是加密算法,不能用於加密(由於沒法經過摘要反推明文),只能用於防篡改,可是它的單向計算特性決定了能夠在不存儲明文口令的狀況下驗證用戶口令。

參考源碼

import hashlib

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

sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())

 

IO模塊

StringIO和BytesIO

StringIO

  • 不少時候,數據讀寫不必定是文件,也能夠在內存中讀寫。
  • StringIO顧名思義就是在內存中讀寫str。
  • 要把str寫入StringIO,咱們須要先建立一個StringIO,而後,像文件同樣寫入便可:
    >>> from io import StringIO
    >>> f = StringIO()
    >>> f.write('hello')
    5
    >>> f.write(' ')
    1
    >>> f.write('world!')
    6
    >>> print(f.getvalue())
    hello world!
  • getvalue()方法用於得到寫入後的str。
  • 要讀取StringIO,能夠用一個str初始化StringIO,而後,像讀文件同樣讀取:
    >>> from io import StringIO
    >>> f = StringIO('Hello!\nHi!\nGoodbye!')
    >>> while True:
    ...     s = f.readline()
    ...     if s == '':
    ...         break
    ...     print(s.strip())
    ...
    Hello!
    Hi!
    Goodbye!

 

BytesIO

  • StringIO操做的只能是str,若是要操做二進制數據,就須要使用BytesIO。
  • BytesIO實現了在內存中讀寫bytes,咱們建立一個BytesIO,而後寫入一些bytes:
    >>> from io import BytesIO
    >>> f = BytesIO()
    >>> f.write('中文'.encode('utf-8'))
    6
    >>> print(f.getvalue())
    b'\xe4\xb8\xad\xe6\x96\x87'
  • 請注意,寫入的不是str,而是通過UTF-8編碼的bytes。
  • 和StringIO相似,能夠用一個bytes初始化BytesIO,而後,像讀文件同樣讀取:
    >>> from io import BytesIO
    >>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
    >>> f.read()
    b'\xe4\xb8\xad\xe6\x96\x87'

 

小結

  • StringIO和BytesIO是在內存中操做str和bytes的方法,使得和讀寫文件具備一致的接口。

 

參考源碼

## do_stringio.py
from io import StringIO

# write to StringIO:
f = StringIO()
f.write('hello')
f.write(' ')
f.write('world!')
print(f.getvalue())

# read from StringIO:
f = StringIO('水面細風生,\n菱歌慢慢聲。\n客亭臨小市,\n燈火夜妝明。')
while True:
    s = f.readline()
    if s == '':
        break
    print(s.strip())

## do_bytesio.py
from io import BytesIO

# write to BytesIO:
f = BytesIO()
f.write(b'hello')
f.write(b' ')
f.write(b'world!')
print(f.getvalue())

# read from BytesIO:
data = '人閒桂花落,夜靜春山空。月出驚山鳥,時鳴春澗中。'.encode('utf-8')
f = BytesIO(data)
print(f.read())

 

JSON模塊

  • JSON (JavaScript Object Notation) 是一種輕量級的數據交換格式。它基於ECMAScript的一個子集。
  • Python3 中可使用 json 模塊來對 JSON 數據進行編解碼,它包含了兩個函數:
    • json.dumps(): 對數據進行編碼。
    • json.loads(): 對數據進行解碼。
  • 在json的編解碼過程當中,python 的原始類型與json類型會相互轉換,具體的轉化對照以下:

Python 編碼爲 JSON 類型轉換對應表:

image

JSON 解碼爲 Python 類型轉換對應表:

image

 

json.dumps 與 json.loads 實例

  • 如下實例演示了 Python 數據結構轉換爲JSON:
    import json
    
    # Python 字典類型轉換爲 JSON 對象
    data = {
        'no' : 1,
        'name' : 'Runoob',
        'url' : 'http://www.runoob.com'
    }
    
    json_str = json.dumps(data)
    print ("Python 原始數據:", repr(data))
    print ("JSON 對象:", json_str)
    
    ## 執行以上代碼輸出結果爲:
    Python 原始數據: {'url': 'http://www.runoob.com', 'no': 1, 'name': 'Runoob'}
    JSON 對象: {"url": "http://www.runoob.com", "no": 1, "name": "Runoob"}
  • 經過輸出的結果能夠看出,簡單類型經過編碼後跟其原始的repr()輸出結果很是類似。
  • 接着以上實例,咱們能夠將一個JSON編碼的字符串轉換回一個Python數據結構:
    import json
    
    # Python 字典類型轉換爲 JSON 對象
    data1 = {
        'no' : 1,
        'name' : 'Runoob',
        'url' : 'http://www.runoob.com'
    }
    
    json_str = json.dumps(data1)
    print ("Python 原始數據:", repr(data1))
    print ("JSON 對象:", json_str)
    
    # 將 JSON 對象轉換爲 Python 字典
    data2 = json.loads(json_str)
    print ("data2['name']: ", data2['name'])
    print ("data2['url']: ", data2['url'])
    
    ## 執行以上代碼輸出結果爲:
    Python 原始數據: {'name': 'Runoob', 'no': 1, 'url': 'http://www.runoob.com'}
    JSON 對象: {"name": "Runoob", "no": 1, "url": "http://www.runoob.com"}
    data2['name']:  Runoob
    data2['url']:  http://www.runoob.com
  • 若是你要處理的是文件而不是字符串,你可使用 json.dump()json.load() 來編碼和解碼JSON數據。例如:
    # 寫入 JSON 數據
    with open('data.json', 'w') as f:
        json.dump(data, f)
    
    # 讀取數據
    with open('data.json', 'r') as f:
        data = json.load(f)

 

老師實例

實例1:

import json

test = '[{"a": 1, "aa": 11, "aaa": 111}, {"b": 2, "bb": 22, "bbb": 222}, {"c":3}]'
test2 = '''{'aaa': 111, 'bbb': 222}'''
print(type(test))

# json.dumps()
newTest = json.loads(test)
print(type(newTest))
print(newTest[0]["a"])

xxx = json.dumps(newTest)
print(type(xxx))

yyy = str(newTest)
print(type(yyy))

print(type(json.loads(xxx)))
# print(type(json.loads(yyy)))


# print(test2)
# print(type(test2))
# result = json.loads(test2)
# print(result)

實例2:

import json

import requests

url = "http://qwd.jd.com/fcgi-bin/qwd_activity_list?g_tk=1231472791&env=3"
session = requests.session()
r = session.get(url)
result = json.loads(r.text)
print(result["errCode"])
print(result["msg"])

實例3:

'''python2 纔有一下的狀況'''
import json
a = dict(hello="你好")
print(a)
print(a["hello"])
print(str(a))
print(json.dumps(a, ensure_ascii=False))

實例4:

import codecs
import json

test = {"a": 1, "b": 2}
with codecs.open("1.txt", "w") as f:
    json.dump(test, f)

with codecs.open("1.txt", "r") as f:
    aa = json.load(f)
    print(aa)
    print(type(aa))
相關文章
相關標籤/搜索