QA@IT

ワンタイムURLの生成

18892 PV

webでメール会員登録の仕組みを作りたいと考えています。
流れは以下のようなイメージです。

1.Web上でメールアドレス等の入力をして登録(仮登録)
2.有効期限付き(24時間以内にアクセスしないといけない等)のURLを作る
3.URLを書いたメールを、入力されたメールアドレスへ送信
4.メールを受け取り、URLへアクセス、本登録が完了

ここで分からない点は、

・登録したユーザごとに一意なURLを発行する方法
・URLに有効期限を設ける方法
・URLへ2回以上アクセスした時、それを判断する方法
・そもそもどんな言語で実装可能なのか

です。

webの経験が浅く感覚のようなものがつかめていないので、とんちんかんな質問だったらすみません。。。
有効期限や本登録済みかどうかは、
セッション?トークン?のようなもので管理されていると思うんですが、詳細が分かりません。

また、こういった仕組みはどんな言語が適しているのでしょうか。
思いついたのは、php/Java/asp.net辺りですが・・・作り易さとかあるんでしょうか。

よろしくお願いします。

回答

RubyとRailsでの回答ですみません。ただ、データの扱いとかは変わらないはずです。雰囲気でお願いします。

例えば、以下のようデータが有るとします。tokensテーブルにuuidとuser_id、expire_atというカラムがあると考えてください。

class User < ActiveRecord::Base
  has_many :tokens
end

class Token < ActiveRecord::Base
  # uuid: string, expire_at: datetime
end

1. 登録したユーザごとに一意なURLを発行する方法

ふつうにDBにデータを作ります。一意でランダムなものなので、RubyでいうところのSecureRandomなどちゃんとしたものを使ってデータを作って下さい。(Rails的に、user_idも勝手に入ります)

user.tokens.create!(uuid: SecureRandom.uuid)

で、このuuidをふくむURLを作ってあげればいいです。Railsのビューで言うと

/tokens/#{token.uuid}

など(Rails的にもっとキレイな書き方はありますが)。あとはそのURLへアクセスされた時にクエリパラメータ内のuuidをベースにtokensをひいて処理すればいいです。

2. URLに有効期限を設ける方法

生成時にexpired_atを指定しておいて、検索するときもそれを気にしながら検索すればいいです。

user.tokens.create!(uuid: SecureRandom.uuid, expired_at: 24.hours.since)

user.tokens.where(['expired_at < ?', Time.now]).find_by_uuid!(uuid)

3. URLへ2回以上アクセスした時、それを判断する方法

単純に、一回引いたあとにexpired_atをセットしてあげれば、以降はそのトークンは無効になります。

token = user.tokens.where(['expired_at < ?', Time.now]).find_by_uuid!(uuid)
token.update_attributes!(expired_at: Time.now)

# 2回目は引けない。
user.tokens.where(['expired_at < ?', Time.now]).find_by_uuid!(uuid)

2回以上のアクセスと有効期限切れを区別するのであれば、別カラムを用意して、使われたことを明記すると良いでしょう。

4. そもそもどんな言語で実装可能なのか

ということでいま言語生誕20周年&2.0リリースで盛り上がっているRubyを使った例で説明しました。
もちろんこの例の他にエラー処理やら何やら入りますが、基本的にはデータの持ち方の話になるので、実装言語はなんでもできます。

あとは要件次第ですかね。たとえば100req/secとかのリクエスト識別子を発行する仕組みなんかだとこの牧歌的な実装は使いづらいと思います。

編集 履歴 (3)
  • ちなみにRailsだとDeviseなどのメジャーなサインアップ・認証ライブラリでこのへんを自動生成できます。これから作るんならそういうアリモノをうまく使うのもいいかと思いました(それでも、仕組みを知っておくのは良いことですね!)

    他のメジャーなWebアプリフレームワークでも、このへんはあるんじゃないでしょうか。
    -
  • 回答ありがとうございます。私にとってRubyもRailsも未知の世界ですが、どんな言語でも実装できそうな感じでとても参考になります。
    重ねて質問になりますが、この方法の場合、仮登録のまま放置された場合はどんどんデータが増えていきますよね。その辺りは何か対策を考えた方が良いのでしょうか?
    -
  • どんどんデータが増えますが、たとえばuuidカラムやuser_idカラムに適切にインデックスが貼ってあれば、気になるようなパフォーマンス劣化はほぼないんじゃないかなあと思います。結局、一意であるuuidの完全一致での検索になるので。

    どうしても消したい場合は、expired_atが過去のものを消すようなcronなどを仕掛けておくといいんじゃないかなと思います。
    -
  • なるほど・・・おおまかなイメージが掴めてきました。これを機に、Rubyもちょっと勉強しようと思います!丁寧なご回答ありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。