moxt

Just another Blog site

意識低いRuby on Rails再入門4

      2015/07/03

下記の内容を読んでテスト系の処理をすっ飛ばしたメモ。
http://railstutorial.jp/chapters/sign-in-sign-out?version=4.0#top

Userモデルを作ってログインしたりセッション管理したい。

Userモデルを作る

rails generateコマンドで作る
キーはname,email,password_digestにする。

マイグレーションファイルが生成されるので、マイグレーションする

Userモデルができた。

Userモデルにhas_secure_passwordという文を宣言すると、パスワードを平文ではなくハッシュ化された状態で管理することができる。らしい。

https://github.com/rails/rails/blob/869a90512f36b04914d73cbf58317d953caea7c5/activemodel/lib/active_model/secure_password.rb

何ができるモノなのか全く掴めないのでコードに書いてる説明を読んでみる。

This mechanism requires you to have a +password_digest+ attribute.

password_digestというキーを追加していた理由はこれだ。

Add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
gem ‘bcrypt’, ‘~> 3.1.7

bcryptというgemが必要らしい。
このgemを利用してpassword_digestを暗号化して保存してくれてるみたい。

Gemfileを見るとbcryptがコメントアウトされてるので、外してbundle installする。

インストールできたら実際にUserを作ってみる。(サンドボックスモードで)

まず、パスワード無しでユーザを作ってみる。

しっかりロールバックされている。

次にパスワードを入力してユーザを作ってみる。

作られたっぽい。

Userの新規登録画面を作るなり、コンソールから作るなりして自分用のUserを作る。

自分のアカウントでログインしたい

ログインとは何か

普段、我々は当たり前のように何かのウェブサービスにログインしていると思う。
ログインしているとは何なのだろうか。

ログインしていないと、いいねもできなければ、自分のプロフィールを編集したりすることもできない。

私は日本太郎です。と、各ページを閲覧するため、いいねするため、プロフィールを編集するためにサーバにたいして表明しなければいけない。

ブラウザ上でイケてるhtmlを見るためにhttp、httpsという通信ルールを使ってデータをやりとりしている。

で、このhttp,httpsっていうのは状態を持たない。
『前の通信してきた人が日本太郎さんだったから、この通信も日本太郎さんだね。じゃあ、プロフィール編集していいよ。』と文脈を読んでくれない。

じゃあ、どうすれば良いのか。

『毎回ログインさせる』という方法がある。

ページの遷移をするたび、いいねボタンを押すたび、プロフィール編集画面で『登録』ボタンを押すたびにログインさせるのだ。
そうすれば、日本太郎さんがいいねボタンを押した、プロフィール編集ページに遷移した、ということが保証できるはず。

しかし、面倒すぎる。

セッションの出番だ

なので、セッションという方法を使う。
通行手形のようなモノだ。
これをサーバへのリクエストに付加して通信を行う。
サーバはセッションを受け取り、それが有効であるか確認する。
有効であればリクエスト本来の処理を実行してレスポンスを返す。
無効なセッションであればエラーを表示するなり、ログイン画面に飛ばしたりする。

セッションの受け渡し方はいろいろある、リクエストのパラメータに含める、ヘッダーに追加する、Cookieに追加する、などなど。

Cookieで。

アカウントにセッションを持たせるようにする

セッションを受けたはいいが、そのセッションからUserを取得するにはどうすればいいのだろう。
Userにセッション用のカラムを追加する方法はどうだろう。

既存のテーブルにカラムを追加する場合もModelを作成したときに使ったmigrationファイルが活躍する。

新たにremember_tokenというカラムを追加してみる。

上のように

AddColumnToXXX 追加したいカラム名:型

と、書くと

このようにカラムを追加する文を勝手に追加したマイグレーションファイルが生成される。
railsの強力さを垣間見る。

マイグレーションファイルができたらマイグレーションしよう。

これでUserクラスにセッションの文字列を保持する新たなメンバーが追加された。
次にセッション管理用(ログイン・ログアウト)のControllerを作る。

セッション管理用の画面を作る

新しくControllerを作る必要があるので、rails g controllerを使う。

bundle exec rails g controller Sessions

Controllerができた。
ログインページを作る。
エンドポイントはnewが良さそう。

routes.rbにエンドポイントを明記しておくことも忘れないように。

resourcesというrailsが用意してるメソッドを使うとRESTfulなエンドポイントたちを自動で作ってくれる。
http://railsdoc.com/references/resources

onlyオプションをつけて不要なエンドポイントは作らないようにする。

Controllerにnewメソッドを追加したら、次のようなViewファイルをviews/sessions以下につくる

sessions/newにアクセスするとそれらしい画面が表示される。

ログインボタンを押すと、エラーが表示される。
Controllerにcreateがないからだ。
なのでcreateを追加する。

createではリクエストのパラメータからUserクラスを取得、その可否でログインの成功失敗を判断して適当なViewをレンダリングする。

与えられたパラメータからUserを取得できたら、remember_tokenとクライアントに返すCookieにセッション情報を格納する必要がある。

さて、肝心のセッション情報は誰が、どうやって作成するべきだろうか。
Userクラスに任せよう。

どうやって作るか、を考える前にセッション情報の性質とはどんなものか考える。

まず、セッション情報は重複してはいけない。

重複するということは1つのセッションに複数人のアカウントが紐付いている状態だ。
例として、プロフィール編集画面でセッションが必要な場合を考える。
セッションに対して複数人のアカウントが紐付いていると、誰のプロフィール情報を表示すればよいかシステム側で判断できない。これは困る。

なので、セッション情報は重複してはいけない。

フルスタック(笑)なRailsには便利な機能があって下記を呼ぶだけでbase64エンコードされたランダムな文字列が生成される。
SecureRandom.urlsafe_base64

Userのクラスメソッドでremember_token用の値を生成できるようにする。
インスタンスメソッドにすると、いちいちUserクラスをnewして生成してからメソッドを呼び出さないといけない。
また、new_remember_tokenメソッドは各Userインスタンスごとのメンバー変数に依存した処理ではない。
なので、クラスメソッドにする。

このメソッドをつかってログイン用の処理をControllerに書いてゆく。(本当はこういう処理をControllerに書いたら良くないんだけど。。w)

もう一度ログイン画面を開き、必要な情報を入力してログインしてみる。
ブラウザのデベロッパーツールなどでCookieを見てみると、remember_tokenというキーに謎の文字列が格納されていることが確認できるかと思う。

rails consoleで該当するUserの情報を見てみると、remember_tokenに謎の文字列が格納されている。

これでログイン処理ができたので、リクエストごとにセッションをつけて遷移できるようになった。
また「いいね」などのアクションを行う際にも、このセッションを見てユーザを一意に特定することができるためアクションごとにログインさせる必要もない。

ついでにログアウト機能も作る。

ログアウト=セッションを無効化する、と考えてよい。

SessionControllerにdestroyを追加し、ログアウト用の処理を追加する。(本当はこういう処理をControllerに書いたら良くないんだけど。。w)

このログアウト機能を呼ぶためのViewが必要になる。
ウェブアプリの中で常に表示させておきたいのか、特定の画面だけでログアウトできるようになってればいいのか、要件次第だと思う。
この辺は長くなりそうなので別に書く。

次はHelperクラスを利用してControllerに書かれていたログイン用の処理を切り出してControllerを見通しよくしてみたい。

 - プログラミング

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

  関連記事

ReactとFluxとReduxについて順を追って整理する

書き途中 Contents1 …

no image
Macでdocker系のコマンドが使えなくなったら確認すること

OSXではdockerは使えないため、別にVMを立ち上げ、そこでdockerを動かしてる。 macからdockerコマンドを使うためにboot2dockerというコマンドを使う。 …

no image
iOSアプリの設計ってどうやるの?

ViewControllerに処理を詰め込みすぎて保守が大変になるのはあるあるネタですよね。 じゃあ、ViewControllerでやることってなんだろうって問われると、まあ、こんなんだろうなってフワッとしてる。 …

no image
gitであまり使わないけど知らないと困るコマンド一覧

随時追加 originのURLを変更したい …

no image
意識低いRuby on Rails再入門6 ~ログイン必須のControllerを作りたい~

ログイン状態を取得するためのSessionsHelperを前に書いた 新規投稿画面を表示したり、実際に投稿するときにはログイン必須であることを保証したい。 …

no image
MecabをPythonから使いたい

ベイズ分類器を自分で作りたかった。 そのためには、文書を単語の集合に変換する必要がある。 …

コードを雑に読むアプローチでScrapyを入門する

Scrapyはスクレイピング用フレームワークなので、登場人物多すぎてよく分からない。 彼らの関係性や役割を理解を深めるために『Data …

no image
AndroidのHandlerって何?

Handlerは何?と、Handlerを直視するとHandlerの存在意義というかなんというか文脈を捉えることが難しい。 なので、まずはAndroidがシングルスレッドである、という所からスタートしてHandlerに向かってゆく。 …

no image
SwiftでOSのバージョンが8.0.0以下とそれより大きいヤツで処理を分岐させたい

前置き push通知のデバイストークン取得方法がiOS8から変わりました。 …

no image
RubyとSeleniumを使って自動で画像をダウンロードする

Contents1 はじめに2 …