例如,赠送一张免费墨西哥卷饼的优惠券。用户A收到了一张免费墨西哥卷饼的优惠券,然后用户B也收到了一张免费墨西哥卷饼的优惠券。这2张优惠券应具有独特的优惠码。
最好的方法是如何生成这样的代码,以防被轻易伪造?我不希望用户在输入随机数字后能够成功兑换他人的优惠券。
我想,可以像一个背面有唯一编号的礼品卡一样考虑这个问题。
require 'securerandom'
SecureRandom.hex
# => "78c231af76a14ef9952406add6da5d42"
# Random, unguessable number as a base20 string
# .rjust(12, '0') covers for unlikely, but possible small numbers
# .reverse ensures we don't use first character (which may not take all values)
raw = SecureRandom.random_number( 2**80 ).to_s( 20 ).rjust(12, '0').reverse
# e.g. "3ecg4f2f3d2ei0236gi"
# Convert Ruby base 20 to better characters for user experience
long_code = raw.tr( '0123456789abcdefghij', '234679QWERTYUPADFGHX' )
# e.g. "6AUF7D4D6P4AH246QFH"
# Format the code for printing
short_code = long_code[0..3] + '-' + long_code[4..7] + '-' + long_code[8..11]
# e.g. "6AUF-7D4D-6P4A"
这个格式中有 20**12
个有效的代码,这意味着你可以发行十亿个自己的代码,并且用户仅有四百万分之一的概率猜对一个。在密码学领域,这是非常糟糕的(该代码对于快速本地攻击是不安全的),但对于提供免费墨西哥卷饼给注册用户的网页表单来说,在你注意到有人用脚本尝试四百万次后,这是可以接受的。
raw_string
最多有19个字符。当 random_number
方法返回 2
时会发生什么? - wuarmin2.44*10**-7
大约等于1/4000000。 - Neil Slater最近我写了一个名为coupon-code gem的东西,它做的事情与Algorithm::CouponCode CPAN模块相同。
优惠券代码不仅应该是唯一的,而且在保持安全性的同时易于阅读和输入。Neil的解释和解决方案非常好。这个gem提供了一种方便的方法来实现它,并附带了一个验证功能。
>> require 'coupon_code'
>> code = CouponCode.generate
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate(code)
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate('1K7Q-CTFM-LMTO') # Invalid code
=> nil
voucher_codes.generate({
length: 8,
count: 1000,
});
voucher_codes.generate({
pattern: "###-###-###",
count: 1000,
});
去尝试一些东西:
class Coupon < ActiveRecord::Base
before_save generate_token
validates_uniqueness_of :token
def generate_token
self.token = "#{current_user.id}#{SecureRandom.urlsafe_base64(3)}"
end
end
我曾经遇到过一个类似的用例,需要为系统中创建的每个对象(在这个问题中是优惠券)生成一个唯一/不重复的代码。我的要求如下:
我探索了几种生成密钥的方法,包括基于时间戳的方法,并发现大多数方法都会生成较长的代码。因此,我决定采用以下自己的逻辑。
在这种方法中,代码的字符数将由以下因素确定:
number of characters of ( count of objects in the system so far ) + 2
所以,当你开始时字符数量为3,当你达到10个对象时它将变成4,当你达到100个对象时它将变成5,对于1000个对象它将变成6,以此类推。这样系统将根据使用情况自动扩展。
这种方法比先生成代码然后检查代码是否已经存在于数据库中的情况更好。在那种情况下,你需要一直生成代码直到找到一个尚未生成的代码。
你可以使用随机数,并检查它是否之前未生成过,通过将所有有效代码存储在数据库中。
使用经过验证的生成器(http://en.wikipedia.org/wiki/List_of_pseudorandom_number_generators)生成随机数。
假设您每天发放333张优惠券,有效期为30天。所以您需要存储10000个数字,并确保一个伪造者无法偶然找到其中一个。
如果您的数字有10个有效数字(约32位,约8个十六进制数字),这种事件的概率为一百万分之一。当然您可以使用更多。