标识符在许多领域主要用于标记用途。能够根据环境条件等因素随机的生成一个ID,也可使用哈希算法或者消息摘要算法对对象生成一个惟一的固定长度的标记符。前者主要用于区分身份的标记,后者能够用于比较文件数据的一致性和重复数据的检测。node
uuid即通用惟一标识符(Universally Unique Identifier),是一种软件构建的标准,目的是让分布式系统中的元素都能有惟一辨识信息。python
其由4个连字号(-)链接32个字节的字符串构成,总共36个字节长。如:UUID('c5c11029-8c99-400e-a7a5-d741f6aaf3e3')。算法
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则是用于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.
信息-摘要算法Message-Digest Algorithm 5简称MD5,是普遍使用的一种散列函数。
能将任意字符串加密成固定长度为128bit的MD5值,且该过程不可逆,安全性较高。能对文件进行散列计算获得一个"数字指纹”,用于校验文件的一致性。
安全哈希算法Secure Hash Algorithm简称SHA1 。SHA1基于MD5,它对长度小于264的输入,产生长度为160bit的hash值。
因为加密后的数据长度更长,SHA1的运算速度要比MD5慢,可是更安全。
散列计算和上面介绍的随机算法同样都可能产生重复值,由于将任意的输入转换到一个固定格式和长度的元素构成的集合中,必然会出现多对一的映射关系。
可是实际应用中重复的几率很是小,能够忽略不计。
所以实际使用时若是两个输入a和b产生相同的hash值,咱们便认为a和b是相同的。这也是数字签名和文件一致性校验等应用的一个基础。
因为python有丰富的库,并且操做较为方便,下面经过python来简单的了解实践一下。
python提供了RFC 4122标准中uuid 1, 3, 4, 和5版本的生成算法,分别对用uuid模块中的uuid一、uuid三、uuid4和uuid5四个函数。
实际例子以下:
# 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')
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)
python中有内置函数hash和库hashlib中提供接口可供计算hash值。下面来一一介绍一下。
内置hash函数的函数原型为:hash(object) -> integer
该函数主要用于程序运行时经过计算hash值来区分不一样的对象,其散列的结果和对象的值以及id(内存中的地址)有关。
要想保证跨进程的结果一致性,可使用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值来判断文件是否重复。
消息摘要算法的有很普遍的用途,限于篇幅就先介绍到这里。