惟一标识符漫谈

标识符在许多领域主要用于标记用途。能够根据环境条件等因素随机的生成一个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值来判断文件是否重复。

消息摘要算法的有很普遍的用途,限于篇幅就先介绍到这里。

相关文章
相关标签/搜索