惟一標識符漫談

標識符在許多領域主要用於標記用途。能夠根據環境條件等因素隨機的生成一個ID,也可使用哈希算法或者消息摘要算法對對象生成一個惟一的固定長度的標記符。前者主要用於區分身份的標記,後者能夠用於比較文件數據的一致性和重複數據的檢測。node

三種標識符

UUID

uuid即通用惟一標識符(Universally Unique Identifier),是一種軟件構建的標準,目的是讓分佈式系統中的元素都能有惟一辨識信息。python

其由4個連字號(-)鏈接32個字節的字符串構成,總共36個字節長。如:UUID('c5c11029-8c99-400e-a7a5-d741f6aaf3e3')。算法

GUID

guid是UUID的一種實現,目前最普遍應用的UUID,是MS的全局惟一標識符(GUID)。數據庫

VS中經過Tools->Create GUID生成:瀏覽器

// {8403C5C0-7F0C-4933-BE85-0E2A90E888AB}
DEFINE_GUID(<<name>>, 
0x8403c5c0, 0x7f0c, 0x4933, 0xbe, 0x85, 0xe, 0x2a, 0x90, 0xe8, 0x88, 0xab);

ObjectID

objectid則是用於MongoDB的主鍵,12-byte的bson類型字符串:安全

An ObjectId is a 12-byte unique identifier consisting of:服務器

  • a 4-byte value representing the seconds since the Unix epoch,
  • a 3-byte machine identifier,
  • a 2-byte process id, and
  • a 3-byte counter, starting with a random value.
>>> from bson import objectid as objectid
>>> objectid.ObjectId()
ObjectId('5c6921b65846b70514ebf839')

上面是MongoDB中產生的若干條記錄網絡

這些隨機生成算法產生的ID重複的機率能夠小到忽略不計,一般能夠認爲是惟一的。dom

 消息摘要算法

上面是咱們主動生成一個全局惟一標識符,用於標記信息數據對象等。被標記對象與標記符之間沒有必然關係,二者之間一般經過key-value的形式關聯,經過key來索引獲得value。分佈式

那麼能不能用一個算法對給定的信息生成一個惟一的標識符?且知足下面的條件:

func(data_1) = identifier_1
func(data_2) = identifier_2
if data_1 == data_2 then identifier_1 == identifier_2
if data_1 != data_2 then identifier_1 != identifier_2

其實這就是消息摘要算法:

  • 結果一致性:相同的輸入應該產生相同的結果;
  • 結果惟一性:不一樣的輸入的結果應該不一樣;
  • 不可逆性:已知算法的生成過程和產生結果,但不能推斷出輸入值。

消息摘要算法的應用是很普遍的,其本質是對數據進行摘要計算以獲得一個固定長度的hash值,目前比較經常使用的有MD5和SHA1.

MD5

信息-摘要算法Message-Digest Algorithm 5簡稱MD5,是普遍使用的一種散列函數。

能將任意字符串加密成固定長度爲128bit的MD5值,且該過程不可逆,安全性較高。能對文件進行散列計算獲得一個"數字指紋」,用於校驗文件的一致性。

SHA1

安全哈希算法Secure Hash Algorithm簡稱SHA1 。SHA1基於MD5,它對長度小於264的輸入,產生長度爲160bit的hash值。
因爲加密後的數據長度更長,SHA1的運算速度要比MD5慢,可是更安全。

散列計算和上面介紹的隨機算法同樣都可能產生重複值,由於將任意的輸入轉換到一個固定格式和長度的元素構成的集合中,必然會出現多對一的映射關係。

可是實際應用中重複的機率很是小,能夠忽略不計。

所以實際使用時若是兩個輸入a和b產生相同的hash值,咱們便認爲a和b是相同的。這也是數字簽名和文件一致性校驗等應用的一個基礎。

動手實踐

因爲python有豐富的庫,並且操做較爲方便,下面經過python來簡單的瞭解實踐一下。

UUID的生成算法

python提供了RFC 4122標準中uuid 1, 3, 4, 和5版本的生成算法,分別對用uuid模塊中的uuid一、uuid三、uuid4和uuid5四個函數。

  • uuid1:該函數使用host ID和序列數以及當前的時間來生成UUID,因爲包含有網絡地址,所以該算法可能會暴露隱私;
  • uuid3:使用一個命名空間的UUID的MD5值和一個字符串來生成一個UUID;
  • uuid4:直接生成一個隨機的UUID;
  • uuid5:使用一個命名空間的UUID的sha-1值和一個字符串來生成一個UUID;

實際例子以下:

# uuid1(node=None, clock_seq=None)
>>> uuid.uuid1()
UUID('27751551-34cf-11e9-ac82-54bf64938e35')
# uuid3(namespace, name)
>>> uuid.uuid3(uuid.NAMESPACE_DNS, 'cnblogs/alpha_panda')
UUID('585ccea7-3560-3af5-a265-456e5944a39e')
# uuid4()
>>> uuid.uuid4()
UUID('72148b42-eec8-4109-80e8-b157134c94d7')
# uuid5(namespace, name)
>>> uuid.uuid5(uuid.NAMESPACE_DNS, 'cnblogs/alpha_panda')
UUID('a8fa1c8d-bff6-5df4-839b-7787b9094863')

經過str將其裝換成字符串,也可經過.bytes屬性獲得16字節的編碼。

>>> uobj = uuid.uuid4()
>>> uobj
UUID('d172a1ed-71bb-4cb5-ac00-4d0dd6041128')
>>> str(uobj)
'd172a1ed-71bb-4cb5-ac00-4d0dd6041128'
>>> uobj.bytes
'\xd1r\xa1\xedq\xbbL\xb5\xac\x00M\r\xd6\x04\x11('
>>> uuid.UUID(bytes=uobj.bytes)
UUID('d172a1ed-71bb-4cb5-ac00-4d0dd6041128')

ObjectID

python中能夠經過bson庫生成objectid,下面是咱們封裝的一個用於生成和處理objectid的類:

import bson.objectid as objectid

class ObjectIDMgr(object):
    @staticmethod
    def genid():
        return objectid.ObjectId()

    @staticmethod
    def id2str(obj_id):
        return str(obj_id)

    @staticmethod
    def str2id(id_str):
        return objectid.ObjectId(id_str)

    @staticmethod
    def id2bytes(obj_id):
        return obj_id.binary

    @staticmethod
    def bytes2id(id_bytes):
        return objectid.ObjectId(id_bytes)

hash算法

python中有內置函數hash和庫hashlib中提供接口可供計算hash值。下面來一一介紹一下。

1.內置hash函數

內置hash函數的函數原型爲:hash(object) -> integer

該函數主要用於程序運行時經過計算hash值來區分不一樣的對象,其散列的結果和對象的值以及id(內存中的地址)有關。

要想保證跨進程的結果一致性,可使用hashlib庫中提供的函數。

2.hashlib

hashlib中包括md5和sha1的實現,以md5爲例。

Python 2.x

import hashlib
m1 = hashlib.md5('cnblogs/alpha_panda')

m2 = hashlib.md5()
m2.update('cnblogs/')
m2.update('alpha_panda')
hd1, hd2 = m1.hexdigest(), m2.hexdigest()
print hd1, hd2, hd1 == hd2

結果:

fb9e9f0f84773f74587f2f05ae0e55d2 fb9e9f0f84773f74587f2f05ae0e55d2 True

Python 3.x

>>> import hashlib
>>> print(hashlib.md5('cnblogs/alpha_panda'.encode('utf-8')).hexdigest())
fb9e9f0f84773f74587f2f05ae0e55d2

hashlib.md5算法對同一字符串計算必能獲得相同的md5值,因爲該函數計算的md5值可重現,所以能夠進行保存和傳輸。

文件一致性校驗

對於文本文件和二進制文件都可以經過hashlib.md5進行計算。

def cal_file_md5(file_path):
    m = hashlib.md5()
    with open(file_path, 'rb') as fobj:
        for segment in iter(lambda : fobj.read(4096), b""):
            m.update(segment)
    return m.hexdigest()

 上面的函數能夠對較大的文件進行md5的值進行計算

明文轉密文

爲安全考慮,防止用戶的密碼泄露,通常公司都不會直接存儲用戶的密碼明文,而是將密碼轉換成密文存放到後臺數據庫中。

用戶登陸的時候,將密碼轉換成密文和數據庫中的存儲的密文進行比較以進行身份認證。

該過程要求不能有密文反推到出明文,並且明文和密文應該是一一對應的關係。消息摘要算法剛好能夠知足需求。

hash值可用做數據庫中用戶密碼的密文。下面是一個簡單的例子:

DB = {
    'zhangsan':'305e4f55ce823e111a46a9d500bcb86c',
    'lisi':'7c6a180b36896a0a8c02787eeafb0e4c',
}

def check_valid(user_name, pwd):
    cipher = hashlib.md5(pwd).hexdigest()
    valid_cipher = DB.get(user_name, None)
    return valid_cipher == cipher

def login_request(user_name, pwd):
    if check_valid(user_name, pwd):
        # do_login()
        return True
    else:
        return False

login_queue= (('zhangsan', 'password0'), ('zhangsan', 'hello'), ('lisi', 'password1'))
for user, pwd in login_queue:
    is_suc = login_request(user, pwd)
    print is_suc

即便服務器端DB中的用戶帳號信息泄露,可是因爲很難僞造一個明文使其md5值剛好等於密碼密文,所以有較高的安全性。

目前網絡上有傳言說md5已被破解,實際無從驗證。可是破解簡單密碼的暴力破解是能夠實現的。

預先計算一些常見的字符串的密文,而後將密文做爲key,明文做爲value存放到數據庫中,這樣可使用密文查詢獲得明文。

重複文件檢測與引用

使用消息摘要算法計算文件的md5值,能夠將改值做爲文件的ID。

好比某網盤上傳文件的秒傳功能,咱們將一個幾G大小的操做系統的鏡像文件上傳至網盤服務器,可能只須要幾秒鐘的時間。

客戶端或者瀏覽器先去讀文件,這個過程其實是計算待上傳文件的hash值,而後將改值上傳到服務器,查找改值對應的文件是否在網盤中;

若是已經存在,則直接將服務端網盤文件的引用計數加一,而後將文件添加到在當前用戶的文件列表中,便完成上傳。若是沒有,這須要將文件上傳至服務器。

這樣若是幾萬個用戶網盤中存儲一個相同的文件,網盤服務器只需存放一份文件,分別被幾萬個用戶引用。當刪除一個文件而致使其引用計數爲零時,即可真正刪除該文件。

此外,一些本地檢測重複文件的軟件一樣能夠分別計算硬盤上文件的md5值,經過md5值來判斷文件是否重複。

消息摘要算法的有很普遍的用途,限於篇幅就先介紹到這裏。

相關文章
相關標籤/搜索