在Ruby中生成GUIDs

164

我有一个使用GUID非常容易解决的问题。

特别是在密码重置流程中,我想向用户的电子邮件发送一个GUID令牌,并要求他们使用该令牌重置密码。由于GUID是唯一的,这是相当安全的,并且避免了向用户发送密码(这是有风险的)。

我注意到有一个uuid gem @ rubyforge,但它看起来很旧,并且会向文件系统写入东西。

有人知道任何其他可以创建全局唯一标识符的gem吗?

我知道我可以回退到:

(0..16).to_a.map{ |a| rand(16).to_s(16) }.join 

但它似乎并不像一个真正的 GUID...


1
使用类似于那样的随机字符串并不是很合适;UUID 中的某些位指定了变体和版本。对于随机 UUID,您可能希望选择变体 2(RFC 4122)和版本 4,这种情况下必须将 6 个特定位设置为正确的值。 - jtpereyda
1
是的,@dafrazzman是正确的。随机拼凑出“类似UUID”的东西并不能保证唯一性。虽然没有UUID是真正保证的,但使用随机数构建一个UUID更容易发生冲突,可能不值得被称为“UUID”。一定要使用SecureRandom.uuid! - dooleyo
11个回答

354
自从 Ruby 1.9 版本开始,已经内置了 UUID 生成器。使用 SecureRandom.uuid 函数来生成 UUID。

例如:

require 'securerandom'
SecureRandom.uuid # => "96b0a57c-d9ae-453f-b56f-3b154eb10cda"

5
SecureRandom.uuid 生成随机 UUID,因此不能保证其唯一性。如果您只需要一个可能是唯一的随机字符串,则可以使用它。但是,如果您需要确保唯一性,则需要使用包括 MAC 地址、时间戳等信息的其他方法。 - Mike Dotterer
26
为了方便你,你需要添加'require 'securerandom''代码。 - Jesse Shieh
8
不能保证唯一性,但对于大多数实际用途而言,可以安全地假定它是唯一的。参见:https://dev59.com/aHA85IYBdhLWcg3wBO3h - Jesse Shieh
1
如果SecureRandom.uuid遵循文档中所说的RFC 4122,那么它是否具有时间戳字段?除了并发之外,这是否意味着唯一性? - Michael K Madison
1
@MikeDotterer 没有 UUID 生成器可以保证唯一性。它们使用不同的方法使冲突变得非常不可能,但从未完全排除。 - Mecki
显示剩余6条评论

41

如何在Ruby中创建小巧、独特的令牌

>> require 'digest'
=> []
>> Digest::SHA1.hexdigest("some-random-string")[8..16]
=> "2ebe5597f"

>> SecureRandom.base64(8).gsub("/","_").gsub(/=+$/,"")
=> "AEWQyovNFo0" 

>> rand(36**8).to_s(36)
=> "uur0cj2h"

23

你是否看过UUIDTools

UUIDTools旨在成为一个简单的库,用于生成各种类型的 UUID(或 GUID,如果你更喜欢这样称呼)。它尽可能符合 RFC 4122规范。


16

谷歌可以得到以下Ruby库

此外,在ruby-forum上他们说您可以安装宝石(在命令行上执行gem uuid来安装它),然后执行以下操作:

gem 'uuid'
puts UUID.new

在你的代码中查看一个新的 UUID。

(提示:我在谷歌上搜索了 guid ruby)


6
很奇怪...我也谷歌搜索了“guid ruby”,但只找到了这篇S.O.的帖子 :-P - Jason Whitehorn

6

Simone Carletti的回答进行了小更新:

SecureRandom.base64(8).gsub("/","_").gsub(/=+$/,"")

=> "AEWQyovNFo0" 

可以被替换为:

SecureRandom.urlsafe_base64(8)

5
为了创建一个合适的、mysql、varchar 32位的GUID,请使用以下代码:
SecureRandom.uuid.gsub('-','').upcase

1
或者 SecureRandom.hex.upcase - Daniel Antonio Nuñez Carhuayo

1
在深夜编程时,我想出了以下解决方案(基于 Simone的答案)来生成Rails中的唯一GUID。虽然我不为此感到自豪,但它确实运行得非常好。
while Order.find_by_guid(guid = rand(36**8).to_s(36).upcase).present?; end

2
希望你记得在那个晚上为你的GUID列建立索引。 - nurettin

1
我学到了一种很棒的技巧,来自JavaScript:
def uuid
    "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".gsub("x") do
        "0123456789ABCDEF"[rand(16)]
    end
end

尽管以更“Ruby的方式”,也可以这样做:

def uuid
    "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".gsub("x") do
        rand(16).to_s(16)
    end
end

你能提供一下 JavaScript 源代码的链接吗?我使用了这里的第一种方法,也就是 broofa 的旧回答。链接在这里:https://dev59.com/7HVD5IYBdhLWcg3wDXF3#21963136 - Cadoiz
1
相信源代码也是来自broofa。需要指出的是,上述内容不符合GUID v4标准,仅仅是UUID。但它应该具有GUID的相同性能和优点。获取符合v4标准的GUID需要稍微复杂一些的代码。 - Sancarn

1

对于运行PostgreSQL数据库的Rails,请执行以下两个步骤。

步骤1:生成一个脚手架/模型

rails g scaffold manager name:string --primary-key-type=uuid

步骤二:在迁移文件中添加一行

如果现在运行rails db:migrate,你会得到一个PG::UndefinedFunction: ERROR: function gen_random_uuid() does not exist的错误。

因此,在迁移文件中添加this

enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')

例子:

class CreateManagers < ActiveRecord::Migration[7.0]

  enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')

  def change
    create_table :managers, id: :uuid do |t|
      t.string :name
      t.timestamps
    end
  end
end

完成了!

哦,最后一件事,你只需要在一个迁移文件中添加enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')(例如,第一次使用uuid类型时)。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接