生成唯一且安全的标识符以供“有时离线” Web应用程序使用的策略
我有一个基于Web的项目,允许用户联机和脱机工作,我正在寻找一种为客户端记录生成唯一ID的方法。我想要一种方法,该方法在用户脱机时(即无法与服务器对话)有效,并且保证是唯一的并且是安全的。通过“安全”,我特别担心客户端(恶意或以其他方式)提交重复的ID,从而严重破坏数据完整性。 我一直在做一些谷歌搜索,希望这已经解决了。我还没有找到任何非常确定的东西,尤其是在生产系统中使用的方法方面。我发现了一些系统的示例,在这些系统中,用户将仅访问他们创建的数据(例如,在多个设备上访问的Todo列表,但仅由创建它的用户访问)。不幸的是,我需要一些更复杂的东西。我确实在这里找到了一些非常好的想法,这些想法与我一直认为事情可能有效的方式是一致的。 以下是我建议的解决方案。 一些要求 ID应该是全局唯一的(或至少在系统中唯一) 在客户端上生成(即通过浏览器中的javascript) 安全(如上所述和其他方面) 数据可以由多个用户查看/编辑,包括未编写数据的用户 不会对后端数据库(例如MongoDB或CouchDB)造成严重的性能问题 拟议的解决方案 当用户创建帐户时,将为他们提供一个uuid,该uuid由服务器生成,并且在系统内是唯一的。此ID不得与用户身份验证令牌相同。我们将此ID称为用户的“ ID令牌”。 当用户创建新记录时,他们会在javascript中生成新的uuid(如果可用,则使用window.crypto生成。请参见此处的示例)。该ID与用户创建帐户时收到的“ ID令牌”并置。现在,此新的复合ID(服务器端ID令牌+客户端uuid)是记录的唯一标识符。当用户在线并将此新记录提交到后端服务器时,该服务器将: 将其标识为“插入”操作(即不是更新或删除) 验证组合键的两个部分都是有效的uuid 验证所提供的复合ID的“ ID令牌”部分对于当前用户是否正确(即,它与服务器在创建用户时分配给该用户的ID令牌相匹配) 如果一切copasetic,插入数据到数据库(小心做插入,而不是一个“更新插入”,这样如果ID 确实已经存在,它不会更新错误现有记录) 查询,更新和删除不需要任何特殊逻辑。他们将简单地以与传统应用程序相同的方式将id用于记录。 这种方法的优点是什么? 客户端代码可以在脱机时创建新数据,并立即知道该记录的ID。我考虑了替代方法,即在客户端上生成一个临时ID,然后在系统联机时将其替换为“最终” ID。但是,这感觉很脆弱。特别是当您开始考虑使用外键创建子数据时,也需要对其进行更新。更不用说处理ID更改后会更改的网址。 通过将ID组合为客户端生成的值和服务器生成的值,每个用户都可以在沙箱中有效地创建ID。这旨在限制恶意/流氓客户端可能造成的损害。同样,任何id冲突都是基于每个用户的,而不是整个系统的全局冲突。 由于用户ID令牌与他们的帐户相关联,因此ID仅可以由经过身份验证的客户端(即用户成功登录的地方)在用户沙箱中生成。这旨在防止恶意客户端为用户创建错误的ID。当然,如果用户的身份验证令牌被恶意客户端窃取,他们可能会做坏事。但是,一旦身份验证令牌被盗,该帐户便会受到威胁。如果确实发生了这种情况,那么所造成的损害将仅限于被盗用的帐户(而不是整个系统)。 顾虑 这是我对这种方法的一些担忧 这样会为大型应用程序生成足够唯一的ID吗?有没有理由认为这会导致id冲突?javascript可以生成足够随机的uuid使其正常工作吗?看起来window.crypto 相当广泛,该项目已经需要相当现代的浏览器。(此问题现在有一个单独的SO问题) 我是否缺少任何漏洞,这些漏洞可能允许恶意用户破坏系统? 查询由2个uuid组成的组合键时,是否有理由担心数据库性能。应该如何存储此ID以获得最佳性能?两个单独的字段还是一个对象字段?Mongo和Couch会有不同的“最佳”方法吗?我知道在执行插入操作时,使用非顺序主键会导致明显的性能问题。为主键自动生成一个值并将该ID存储为单独的字段会更聪明吗?(此问题现在有一个单独的SO问题) 使用这种策略,可以很容易地确定一组特定记录是由同一用户创建的(因为它们都共享相同的公共可见id令牌)。虽然我看不到有任何立即出现的问题,但最好不要泄露比内部需求更多的信息。另一种可能性是对复合键进行哈希处理,但这似乎比其价值更大。 如果用户发生ID冲突,则没有简单的恢复方法。我想客户端可以生成一个新的id,但这似乎确实是不应该发生的。我打算解决这个问题。 只有经过身份验证的用户才能查看和/或编辑数据。这是我的系统可接受的限制。 结论 以上是合理的计划吗?我意识到其中一些归结为基于对相关应用程序的更全面了解的判断调用。