只要不是一个实体的完成的业务就需要有身份ID来区分不同的实体,在实际的工程实现中,就是只有一个实体,实体内部也是要区分各个子模块,子模块也是需要ID来区分身份。正所谓“无名天地之始;有名万物之母”,也就是说ID是所有实体的必要的属性,至于ID本身是什么并不重要,重要是发挥区分不同实体的核心作用。
我们在做架构设计的时候为每个模块确定身份ID也是一个必然的过程,不过有些时候是隐含的,如通过域名,IP地址等这种现成的可以区分不同实体的标识来作为业务ID。这里我们可以得出ID的第一个核心特性——唯一性。
数字世界的ID说到底就是一串的数字,
1,只要保证身份ID在自己的作用域内不重复就可以,
2,ID的空间只要不小于作用域内实际的实体的数目;
只要这串数字同时满足以上两个特性就可以保证正常的业务运作。保证ID的唯一性其实就是保证数字的唯一性,我们可以求助于初等数学的一些知识。
最容易想到的就是使用一个等差数列,或者等比数列这种数学特性明显的方案,但是这种分配ID无法满足分布式的架构。如果要在分布式的架构中使用必须配合其他的手段,比如说为各个模块分配起始和结束的段,这样就从设计上限制的各个模块生成ID的最大空间,迟早会引发类似于电脑千年虫的问题。
使用等差数列还有信息泄露的问题。例如某电商网站,订单号的ID是全局递增的,也就是说第一个订单号是1,第二个订单号是2,以此类推。这种ID的设计方案是能很好的保证ID的唯一性属性,但是这个ID泄露了电商的订单数目,攻击者创建一个订单,这个订单号就是整个电商之前的所有订单数目+1的值;攻击者每天凌晨创建一个订单并且记录订单号X,午夜再创建一个订单并且记录订单号Y,那么Y-X的值就是电商一天的订单数。
另一种常用方案是使用时间戳作为ID,这种方案只能用在ID的分发必须在时间上是绝对离散的,并且分布式系统是定要准确校时,所以这是一种不容易保证唯一性的方案。
既然使用有规律的方式都有一些弊端,我们自然会想到使用现成的伪随机数数生成器的结果,其实这是一个非常危险的设计思路,因为通常代码库中的伪随机数数生成器是不会做重复检查的,同样也没有考虑到分布式场景的,如C语言标准库中的随机数生成器。这种思路其实是把功能交给了命运,它的出错的概率是高于上述的两种方案。
如我们上面讨论的情况一致,在分布式场景下生成一个合格的ID其实是所有系统都要面对的挑战,既然是共性问题就会有人去解决,我们使用Java的框架,C#的框架都提供了明确的生成唯一ID的API,开发可以通过这些API获取到UUID,然后使用这些UUID作用实体的身份ID。
对于身份ID是否应该表达一定的业务意义?这个问题有两派截然相反的论调。反对表达业务意义的一方的立场通常出自对安全的考虑,因为ID是一个公开的,防止攻击者通过ID识别出关键对象,然后针对性的进行攻击,如root,administrator就是被重点攻击的ID。支持表达业务意义的一方的立场通常出自易用性的考虑,同样因为ID是公开的,为了方便交流,既然已经是公开的为何不提供对方更便利的沟通服务呢?
从实际的实施经验来看,身份ID应该具有业务属性,不过不是无限制的提供数据,而是应该进行系统化的设计。从目前信息系统在不断互联互通的发展方向来讲,也是要求身份ID也具有自描述性。
那如何解决VIP的ID成为众矢之的呢?从安全业务的角度来说,这是ID对应的身份认证的问题,我们应该在认证设计上对抗VIP身份认证攻击的问题,而不是将此问题扩散到ID的设计上。
对于特殊场景,如军事,核心基础实施等自成体系或者外部交互需求不强的系统,安全性要求极强的领域,如果可以通过ID提高抗攻击的难度,也是有必要增加复杂度的。如我军对外都是使用代号来标识的。
综述所述,在追求互联互通的IoT系统中,实体ID的设计需要满足如下:
1,唯一性:作用域内不重复;
2,前瞻性:空间可以满足未来也发展的需求;
3,自描述性:在不影响隐私和机密性的情况下,方便互联互通的需要。
例如:
ID=UUID+功能描述+提供商签名,UUID确保了唯一性和足够大的空间,功能描述满足了自描述性,提供商签名满足了对端验证身份的要求。由于ID是一个高频使用的字段,过长的ID会增加通讯的带宽资源,这就要求架构师在多个视角中做权衡。