26

我正在编写一个 Rails 应用程序,就其性质而言,它不能要求用户注册,因此不能使用身份验证作为保护记录的常用手段。(我知道,我知道...)这里的用户信息仅限于电子邮件地址。所以我确实需要一种方法来让我的模型 ID 不可预测,这样其他 ID 就不容易被猜到。(我知道我知道...)

我曾尝试使用像 uuidtools 这样的插件在创建记录时随机化 id,如下所示:

require 'uuidtools'
class Post < ActiveRecord::Base 
 def before_create() 
  self.id = OpenSSL::Digest.SHA1.hexdigest(UUID.timestamp_create()) 
 end 
end 

...起初看起来不错,但有趣的事情发生了。ActiveRecord 有时会尝试在 id 中插入 0 值,我会收到诸如“找不到 id=0 的帖子”等错误...

我已经没有想法了。任何人都可以帮忙吗?谢谢。

4

4 回答 4

21

在您的模型中,执行以下操作:

before_create :randomize_id

...

private
def randomize_id
  begin
    self.id = SecureRandom.random_number(1_000_000)
  end while Model.where(id: self.id).exists?
end
于 2012-12-03T10:03:20.883 回答
16

最好的方法是使用SecureRandom.uuid生成 V4 UUID(通用唯一标识符)。

它几乎是完全随机且唯一的(碰撞概率大约是数十万亿): https ://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29

这应该可以完成工作:

class Post < ActiveRecord::Base
 before_create :generate_random_id

 private 
 def generate_random_id
   self.id = SecureRandom.uuid
 end 
end

或者,如果您使用 Rails >= 4 和 PostgreSQL,您可以让它为您生成它们:

create_table :posts, id: :uuid do |t|
  ...
end
于 2016-01-08T14:52:31.080 回答
3

这里出错的是 self.id 需要一个 int 并且 OpenSSL::Digest.SHA1.hexdigest(UUID.timestamp_create()) 返回一个带有非数字字符的字符串,这将导致值 '0' 实际上是存储在数据库中

于 2009-07-12T12:32:10.990 回答
2

另一种方法是在记录创建期间在第二列中生成令牌或校验和或其他任何内容,并且在所有情况下,您的控制器查询对象,使用Model.find_by_id_and_token.

然后,您将始终生成包含并需要 id 和令牌的 URL。

于 2009-05-06T21:14:16.280 回答