【Rails】各種idをランダムな文字列に変換してPrimary keyとして使う

Railsでデータベースに値を登録すると、デフォルトでは各テーブルのid(integer型)が1から順番に割り当てられるようになっています。

しかし、このままだと(以下の例のように)URLのidパラメータに整数を代入しただけで、いろんなデータに簡単にアクセスできてしまいます。(データの推測が容易にできてしまいます)

例)http://localhost:3000/user/show/123

こうした理由から、idとしてランダムな文字列を割り当てたい場面もあるかと思います。

そこで、今回はユーザーidを例にとって、idをランダムな文字列に変換して、かつ一意な文字列(Primary key)として保存する方法についてまとめました。

http://localhost:3000/user/show/123
↓↓
http://localhost:3000/user/show/GwUzoWxCL9bZ8fdB
(こんな感じにしたい)

目次

ランダムな文字列を生成するモジュール「SecureRandom」

Ruby標準ライブラリには、任意の文字列からランダムな文字列を生成するモジュールSecureRandomが用意されています。

SecureRandomにはランダムな整数や文字列を生成するメソッドがいくつか含まれているので、そのうちのいくつかを紹介します。

スクロールできます
メソッド説明文字列サンプル
urlsafe_base64A–Z、a–z、0–9、”-“、”_”のいずれかの文字(64種類)からなる長さ22のランダムな文字列etwC73TNOBfNx_UxmGtYYw
base36a–z、0–9のいずれかの文字(36種類)からなる長さ16のランダムな文字列8yrcmoj9u4kg28f3
base58base64から英数字の区別しずらい文字(”O”と”0″、”I”と”1″など)や、”+”、”/”を取り除いた文字(58種類)からなる長さ16のランダムな文字列GwUzoWxCL9bZ8fdB
base64A–Z、a–z、0–9、”+”、”/”のいずれかの文字(64種類)からなる長さ22のランダムな文字列に加え、パディングのため末尾に==を加えた文字列Ta881i+NfzlPulj2dra50A==
uuida–f、0–9のいずれかの文字(16進数)に”-“を挟んで構成される、長さ128ビットのランダムなuuid文字列0457cabb-e82c-49d2-87f4-e0105e6ff4ae
hexa–f、0–9のいずれかの文字(16進数)からなる長さ32のランダムな文字列421468cc80319fd2c9530508c5b88486
SecureRandom のメソッドまとめ

urlsafe_base64

A–Z、a–z、0–9、”-“、”_”のいずれかの文字(64種類)からなる長さ22のランダムな文字列を返します。

ちなみに、urlsafe_というのは、英数以外に “-” と “_”のみが使われるため、URL名として(ファイル名としても) “safe” という意味です。

irb(main):001:0> SecureRandom.urlsafe_base64
=> "3Bqb9iP2pPH8QwF168KnvQ"
irb(main):002:0> SecureRandom.urlsafe_base64
=> "5Iu2LsDDY46Y00TqEDP3dQ"
irb(main):003:0> SecureRandom.urlsafe_base64
=> "siv0zniBrc54zK8QG4tQ8g"

urlsafe_base64(n)のように、引数nを指定することで文字列の長さを指定することもできます。

生成される文字列の長さは引数nの約4/3倍

irb(main):027:0> SecureRandom.urlsafe_base64(10)
=> "NxZDy1hWo9rQiw"
irb(main):028:0> SecureRandom.urlsafe_base64(20)
=> "D0jChUOIPrBLFYmcrchDLCFeXdA"
irb(main):029:0> SecureRandom.urlsafe_base64(30)
=> "pB3QXrYxeAXFC5SXtqxn0hgRcLja9cOQndQfkGwS"

今回、ユーザーidをランダムな文字列に変換するメソッドとしてurlsafe_base64を使おうと思います。

base36, base58, base64

それぞれ、base36base58base64 文字列を生成します。

  • base36… a–z、0–9のいずれかの文字(36種類)からなる長さ16のランダムな文字列
  • base58… base64から英数字の区別しずらい文字(”O”と”0″、”I”と”1″など)や、”+”、”/”を取り除いた文字(58種類)からなる長さ16のランダムな文字列
  • base64… A–Z、a–z、0–9、”+”、”/”のいずれかの文字(64種類)からなる長さ22のランダムな文字列に加え、パディングのため末尾に==を加えた文字列
irb(main):004:0> SecureRandom.base36
=> "ijjnrtkarqmb68sw"
irb(main):005:0> SecureRandom.base58
=> "x3bg7sxQCptSunWV"
irb(main):006:0> SecureRandom.base64
=> "8lAUztTPJmztoPBeYvVXoA=="

base36(n)のように、引数nを指定することで文字列の長さを指定することもできます。

生成される文字列の長さ = n

irb(main):014:0> SecureRandom.base36(10)
=> "wb4mb11t02"
irb(main):015:0> SecureRandom.base36(20)
=> "x0yeju8gzcskvznd4v72"
irb(main):016:0> SecureRandom.base36(30)
=> "w5sle7miyti650hkeonn9nck80n9ig"

数値(n)を指定しなかった場合、デフォルトでは16文字(n=16)のランダムな文字列として計算されます。

uuid

a–f、0–9のいずれかの文字(16進数)に”-“を挟んで構成される、長さ128ビットのランダムなuuid(乱数)を生成します。

irb(main):007:0> SecureRandom.uuid
=> "d412f6f9-508e-4727-9020-e50e507f07d5"
irb(main):008:0> SecureRandom.uuid
=> "556fd5ac-324e-4a27-8ec2-57d93eee843e"
irb(main):009:0> SecureRandom.uuid
=> "18385b6f-31e9-42ff-8c2d-30e5dde25d17"

uuidは、それぞれハイフン”-“の区切りごとに以下のような構成になっています。

uuid = time-low “-” time-mid “-” time-high-and-version “-” clock-seq-and-reserved clock-seq-low “-” node

これら時間的な要素を、それぞれhexというメソッドでハッシュ化することでランダムなuuidが生成されるようです。

詳しくは「RFC 4122」を参照。

hex

a–f、0–9のいずれかの文字(16進数)からなる長さ32のランダムな文字列を返します。

irb(main):035:0> SecureRandom.hex
=> "f60fab63c43de5627a26539805b842f1"
irb(main):036:0> SecureRandom.hex
=> "f266bddd3d9451e9b4690f864b65c424"
irb(main):037:0> SecureRandom.hex
=> "3739bd850600df31a6b925a8ca72689c"

hex(n)のように、数値nを指定することで文字列の長さを指定することもできます。

生成される文字列の長さは引数nの2倍

irb(main):038:0> SecureRandom.hex(5)
=> "fabce8b9e1"
irb(main):039:0> SecureRandom.hex(10)
=> "85849778dea1b285a49b"
irb(main):040:0> SecureRandom.hex(20)
=> "a5907a19cdeb7a58bf7ca066b17dd72f11a8e7d8"

ユーザーidをランダムな文字列に変換して保存する

サンプルとしてUserモデルを作成します。

$ rails g scaffold User name:string

生成されたマイグレーションファイル開き、idstring型にするよう記述します。

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users, id: :string do |t|
      t.string :name

      t.timestamps
    end
  end
end

こうすることで、idの型をデフォルトのintegerからstringに変更することができます。

変更を保存したら、マイグレーションを実行します。

$ rails db:migrate

続いては、Userモデルにランダムな文字列を生成するメソッドを追加します。

idをstring形式に変更したことで、現状ではcreate時に自動でidが付与されなくなります。

そのため、create時にランダムかつ一意である(ユニークな)文字列を生成して、ユーザーidに渡す必要があります。

class User < ApplicationRecord
  before_create :set_id


private

  # ランダムなユーザーIDを返す
  def set_id
    while self.id.blank? || User.find_by(id: self.id).present? do
      self.id = SecureRandom.urlsafe_base64(10)
    end
  end
end

ここで、

while self.id.blank? || User.find_by(id: self.id).present? do

は、idがまだセットされていない、もしくはすでに同じidのレコードが存在する場合にwhileループに入るようになっています。

これでユーザーidがランダムに割り当てられるようになりました。

コンソールで確認してみましょう。

irb(main):001:0> user = User.new(name: "sample")
irb(main):002:0> user.save
irb(main):003:0> user.name
=> "sample"
irb(main):004:0> user.id
=> "ubxWgn-QZOvPkw"

このように、ユーザーを新規登録した時にuser.idが期待通りの文字列になっていれば成功です。

以上です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次