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_base64 | A–Z、a–z、0–9、”-“、”_”のいずれかの文字(64種類)からなる長さ22のランダムな文字列 | etwC73TNOBfNx_UxmGtYYw |
base36 | a–z、0–9のいずれかの文字(36種類)からなる長さ16のランダムな文字列 | 8yrcmoj9u4kg28f3 |
base58 | base64から英数字の区別しずらい文字(”O”と”0″、”I”と”1″など)や、”+”、”/”を取り除いた文字(58種類)からなる長さ16のランダムな文字列 | GwUzoWxCL9bZ8fdB |
base64 | A–Z、a–z、0–9、”+”、”/”のいずれかの文字(64種類)からなる長さ22のランダムな文字列に加え、パディングのため末尾に== を加えた文字列 | Ta881i+NfzlPulj2dra50A== |
uuid | a–f、0–9のいずれかの文字(16進数)に”-“を挟んで構成される、長さ128ビットのランダムなuuid文字列 | 0457cabb-e82c-49d2-87f4-e0105e6ff4ae |
hex | a–f、0–9のいずれかの文字(16進数)からなる長さ32のランダムな文字列 | 421468cc80319fd2c9530508c5b88486 |
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
それぞれ、base36
、base58
、base64
文字列を生成します。
- 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
生成されたマイグレーションファイル開き、id
をstring
型にするよう記述します。
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
が期待通りの文字列になっていれば成功です。
以上です。
コメント