13

特定の ActiveRecord モデル オブジェクトのインスタンスを作成したら、短い (6 ~ 8 文字) の一意の文字列を生成して、URL の識別子として使用する必要があります。 p/P541i4ErdL/、これを 404 にスクランブルしただけです) または Youtube のビデオ URL ( http://www.youtube.com/watch?v=oHg5SJYRHA0など)。

これを行う最善の方法は何ですか?一意になるまでランダムな文字列を繰り返し作成するのが最も簡単ですか? ユーザーが 1 文字を変更して URL をハッキングし (上記の 404 の Instagram リンクで行ったように)、最終的に新しいレコードに到達できないように、整数 ID をハッシュ/シャッフルする方法はありますか?

4

3 に答える 3

22

これは、plpgsql で既に実装されている衝突のない優れた方法です。

最初のステップ: PG wikiのpseudo_encrypt関数を検討してください。この関数は引数として 32 ビット整数を取り、人間の目にはランダムに見えますが、その引数に一意に対応する 32 ビット整数を返します (つまり、ハッシュではなく暗号化です)。関数内で式を変更することができます: [0..1] の範囲で結果を生成する、あなただけが知っ(((1366.0 * r1 + 150889) % 714025) / 714025.0)ている別の関数を使用します (定数を微調整するだけでおそらく十分です。以下の私の試みを参照してください) . より理論的な説明については、 Feistel サイファーに関するウィキペディアの記事を参照してください。

2 番目のステップ: 選択したアルファベットで出力番号をエンコードします。これは、すべての英数字を使用して base 62 で実行する関数です。

CREATE OR REPLACE FUNCTION stringify_bigint(n bigint) RETURNS text
    LANGUAGE plpgsql IMMUTABLE STRICT AS $$
DECLARE
 alphabet text:='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 base int:=length(alphabet); 
 _n bigint:=abs(n);
 output text:='';
BEGIN
 LOOP
   output := output || substr(alphabet, 1+(_n%base)::int, 1);
   _n := _n / base; 
   EXIT WHEN _n=0;
 END LOOP;
 RETURN output;
END $$

ここで、単調なシーケンスに対応する最初の 10 個の URL について得られるものを次に示します。

select stringify_bigint(pseudo_encrypt(i)) from generate_series(1,10) as i;
stringify_bigint
------------------
 tWJbwb
 eDUHNb
 0k3W4b
 w9dtmc
 wWoCi
 2hVQz
 PyOoR
 cjzW8
 ビゴクブ
 A5tDHb

結果はランダムに見え、出力空間全体で一意であることが保証されます (入力空間全体を負の整数で使用する場合、2^32 または約 40 億の値)。40 億の値が十分に広くない場合は、2 つの 32 ビットの結果を慎重に組み合わせて、出力の単一性を失わずに 64 ビットにすることができます。注意が必要な部分は、符号ビットを正しく処理し、オーバーフローを回避することです。

関数を変更して独自の独自の結果を生成する方法について: 関数本体で定数を 1366.0 から 1367.0 に変更し、上記のテストを再試行してみましょう。結果がどのように完全に異なるかを確認してください。

NprBxb
 SY38Ob
 urrF6b
 OjKVnc
 vdS7j
 UEFEB
 3zuaT
 0fjsab
 j7OYrb
 PYiwJb

更新: C 拡張機能をコンパイルできる人にとって、 の適切な代替品pseudo_encrypt()range_encrypt_element()からのpermuteseq extensionもので、次の利点があります。

  • 最大 64 ビットの任意の出力スペースで動作し、2 の累乗である必要はありません。

  • 推測不可能なシーケンスに秘密の 64 ビット キーを使用します。

  • それが重要な場合は、はるかに高速です。

于 2012-09-25T19:50:00.800 に答える
5

次のようなことができます。

random_attribute.rb

module RandomAttribute

  def generate_unique_random_base64(attribute, n)
    until random_is_unique?(attribute)
      self.send(:"#{attribute}=", random_base64(n))
    end
  end

  def generate_unique_random_hex(attribute, n)
    until random_is_unique?(attribute)
      self.send(:"#{attribute}=", SecureRandom.hex(n/2))
    end
  end

  private

  def random_is_unique?(attribute)
    val = self.send(:"#{attribute}")
    val && !self.class.send(:"find_by_#{attribute}", val)
  end

  def random_base64(n)
    val = base64_url
    val += base64_url while val.length < n
    val.slice(0..(n-1))
  end

  def base64_url
    SecureRandom.base64(60).downcase.gsub(/\W/, '')
  end
end
Raw

user.rb

class Post < ActiveRecord::Base

  include RandomAttribute
  before_validation :generate_key, on: :create

  private

  def generate_key
    generate_unique_random_hex(:key, 32)
  end
end
于 2012-09-25T16:32:23.343 に答える
0

IDをハッシュできます:

Digest::MD5.hexdigest('1')[0..9]
=> "c4ca4238a0"
Digest::MD5.hexdigest('2')[0..9]
=> "c81e728d9d"

しかし、誰かがあなたが何をしているのかを推測し、そのように繰り返すことはできます。おそらくコンテンツをハッシュする方が良いでしょう

于 2012-09-25T02:15:21.413 に答える