Ruby on Rails
基礎3
- 前回、各ページのアクセス制限を行いました。
- 次は、悪意あるユーザーからの攻撃方法を知り、それに対抗するために、よりセキュリティを高める対策をやっていきます。
前回の確認
- 最初に行う対策は【パスワードの暗号化】です。
- 実は、現在はパスワードは暗号化されずに、データベースに保存されています。
- 通常はデータベースの中身が見られることはないとはいえ、何かのバグで表示してしまったり、データが流出してしまった場合でも、パスワードは読み取れないものにしておくべきです。
- そのため、パスワードを暗号化をします。
パスワードの暗号化
- Railsの場合、パスワードの暗号化は、非常にカンタンです。
- ほぼ自動で暗号化される gem(ジェム)が用意されていますので、それをインストールするだけです。
- ※gemとは、スマホのアプリのように、色々な機能をインストールして使える仕組みのことでしたね。
- インストールするのは bcrypt(ビー クリプト)というgemです。
- これは Gemfile.rb にすでに書き込まれているのですが、コメントの状態になっていますので、コメントを解除して、保存してください。
- たぶん41行目か、そのあたりにあると思います。※もし見つからなければ、自分で入力してください。
#を消す
- 後は shell から bundle install を実行し、インストールします。
パスワードの暗号化
- インストールが出来たら、一旦Railsを再起動しておいてください。
- その後、userモデル に、以下のメソッド has_secure_password を追加します。
- 次にフォームを少しイジります。
- user の「newファイル と editファイル」のフォームの中で password_digest カラムを指定していた部分(赤枠部分)を、 password に変更してください。※2つのファイルとも変更してください。
パスワードの暗号化
- 次に、userコントローラーの方でも、 password_digest カラムを指定していた部分(赤枠部分)を、 password に変更します。
- ※createメソッド と updateメソッド の2か所とも変更します。
- これでOKです。
- 「password」というカラムは作っていないのですが、このように指定しておくとデータベースに保存されるよりも前に、「 bcrypt 」が、password に入っている文字列を暗号化(ハッシュ化)して、さらに暗号化した後のデータ(ハッシュ値)を password_digest に入れてくれます。
パスワードの暗号化
フォーム
password_digest
aaaaaa
コントローラー
データベース
password_digest
aaaaaa
password_digest
aaaaaa
今まで
bcryptを使った場合
フォーム
password
aaaaaa
コントローラー
データベース
password
aaaaaa
bcrypt
$2a$12$TI0TM5S5XQAJ...
password_digest
$2a$12$TI0TM5S5XQAJ...
おらぁ!暗号化や!
結局入るカラムは変わらない
- これで暗号化されるはずなので、試しに新しいユーザーを作ってみてください。※名前とパスワードは別にしても良いです。
パスワードの暗号化
- 作成できたら、Rails c(レイルズ コンソール)からパスワードを確認してみます。
- shell で rails c を実行してください。次へ。
- 次に、User.all を実行すると、すべてのユーザーの情報が出てきますが、以下のようにパスワードが見れないようになっているかと思います。
パスワードの暗号化
- これは暗号化とは関係なく、セキュリティ上、表示させないようになっているだけです。
- ※もう少し古いバージョンのRailsだと表示されているかも知れません。
- 今回は暗号化がされているのか?を確認したいので、一旦このセキュリティを解除します。
- 「config/initializers/filter_parameter_logging.rb」というファイルを探して、開いてください。
- ここに先ほどのセキュリティ設定がありますので、以下のようにコメントアウトしてください。
- ※後で戻すので、消さないようにしましょう。
パスワードの暗号化
- 一応Railsを再起動しておいてください。
- その後、再度 User.all を実行してください。すると以下のようにパスワードが表示されると思います。
パスワードの暗号化
- 上のように謎の英数字が保存されていたらOKです。
- これが暗号化された状態です。(元々のパスワードは aaaaaa でしたね)
- 新しく追加したユーザー以外は、暗号化されていないままで、表示されていると思いますので、他のユーザーも暗号化しておきましょう。
- 次のページへ。
- すでに作成済みのユーザーのパスワードも簡単に暗号化できます。
- 普通にユーザー編集ページに開き、改めてパスワードを設定しなおせばOKです。
- それが終わったら、再度 User.all を実行し、全員が暗号化されたか確認しておきましょう。
- すべて確認できたら、先ほどのセキュリティ設定を元に戻します。
- 「config/initializers/filter_parameter_logging.rb」を以下の状態に戻してください。
パスワードの暗号化
- 念のため Rails を再起動しておいてください。
- ※configフォルダの中のファイルは、基本的に設定ファイルなので、変更したら再起動しておいた方が無難です。
- routes.rb ファイルのように、再起動しなくても更新されるものもありますが、再起動しないと更新されないものや、変な動作をするものもあります。
- その中でも特に initializersフォルダの中のファイルを変更後は、必ず再起動しておきましょう。
パスワードの暗号化
- これで暗号化は完了ですが、実はこのままだとログインができません。
- なぜなら今は「入力された値(暗号化されていない)」と「データベースの password_digest の値(暗号化されている)」を比較しているので、パスワードが一致するわけがありません。
- 暗号化した値を、元に戻すことを複合化(ふくごうか)と言いますが、bcrypt のような暗号化は「元に戻すことができません」
- この「元には戻せない、一定の長さの値」のことをハッシュ値と言います。
- 元に戻せないから、もしデータベースの情報を見られたとしても、パスワードがバレることがないって事ですね。
- 元に戻せないのに、どうやって認証するのか?というと「もっかい暗号化してみて、同じハッシュ値になるか」を確認すればいいのです。
- ハッシュ値は「元の値が同じであれば、変換されるハッシュ値も同じになる」という特徴もあります。
- それをやっていきましょう。次へ。
- ハッシュ値は「元の値が同じであれば、変換されるハッシュ値も同じになる」という特徴もあります。
- 変更するのはログイン処理をするところなので、sessions_controller.rb の createメソッドです。
- ここを以下のように変更しましょう。
パスワードの暗号化
- 「.authenticate」というメソッドは、bcrypt によって追加されたメソッドで「引数に与えた値を暗号化して、保存されている ハッシュ値と同じか?」を判定してくれます。
- ログイン画面に入力した値をハッシュ値にして、それが pass_word_digest に保存されているハッシュ値と、同じになれば true 違えば false になります。これでパスワードが正しいかを判定が出来るようになりました。
- もう少し暗号化の話をしておきます。※興味なければ飛ばしてOKです。
- ハッシュ値は「元の値が同じであれば、変換されるハッシュ値も同じになる」と言いましたが、という事は「abc のハッシュ値は $xyz なんだな」と一回覚えておけば、もしハッシュ化(暗号化)しておいたとも、そのハッシュ値を見れば、元のパスワードがわかってしまう、という事でもあります。
- これだと「ハッシュ値は元に戻せないから、もしデータベースのハッシュ値を見られても、パスワードはわからない」という安心感は薄れてしまいます。
- なので bcrypt はパスワードの値だけではなく「ソルト」と呼ばれるランダムな値を作り、それとパスワードの値を組み合わせてハッシュ値にしています。
- このハッシュ値はユーザーごとに作られるため、ハッシュ値だけで、パスワードを特定することは不可能になります。
- このように何重にもセキュリティ対策を施しています。
パスワードの暗号化
- ちなみに params などで使われるデータ型の「ハッシュ型」(辞書型とも呼ばれる)というのもありましたが、暗号化とは特に関係ありません。
- ハッシュ型は、キーをハッシュ値に変換して保存していることから、Rubyでは「ハッシュ型」と呼ばれますが、暗号化のためではなく、高速化するためにハッシュ化しています。
- (ハッシュテーブルという、普通の配列よりも挿入や削除が効率的に行えるデータ構造に収めるためにハッシュ化してます)
- それだとしても「ハッシュ型」という名前は紛らわしいので「辞書型」にしてほしいですが…
- さらにちょっとした「ヤバイサイトの見分け方」も紹介します。
- 先ほど説明した通り、パスワードはハッシュ値に変換されてデータベースに保存されますので、実は「パスワードの値は、どこにも存在しない」状態になっています。
- 何が言いたいかと言うと「パスワード忘れた…」となった場合「そうだ、サイトの管理者にパスワードを聞いてみよう!」ってことは出来ないのです。だって「パスワードの値」はどこにも保存されていないので、サイトの管理者だろうと確認することはできません。
- 試しに暗号化したユーザーの editページ を開いてみると、パスワードの部分は空欄になっているはずです。(暗号化する前は表示されてましたよね?)
- 逆に言うと編集画面でパスワードの値が表示されているサイトや、「あなたのパスワードは〇〇です」などと聞いたら答えられるサイトというのは「パスワードを暗号化せず保存している」ということです。
- これはかなり「ヤバイサイト」なので、気を付けてください。(絶対利用しない方が良いです)
- とはいえ、登録してからじゃないとわからないことなので「うさんくさいサイトには何も登録しない」ということと「登録する時は、サイトごとにパスワードを変える」のが重要と言われる理由です。
- もし、パスワードを悪サイトに登録してしまい、それが暗号化されていなければ、情報が漏れた時に危険ということ以上に「悪サイトの管理者からは、パスワードが丸見え」というのがヤバイですよね。
- 悪サイトの管理者は、手に入れたメールアドレスやパスワードを利用して、amazonや別のサイトで、ログインを試してみるでしょう。
- 他のサイトでも同じパスワードを使っていれば、これでアウトです。好き放題買い物されてしまいますね。
- なので一般的に「パスワードを暗号化してない=完全にヤバイサイト」という認定になります。自分のサイトがヤバイサイト認定にならないようにも暗号化は必須なのです。
パスワードの暗号化
- 暗号化自体は終わったのですが、パスワード関係で、もう少し改善ポイントがあるのでやっておきます。
- 今までの流れ通り、データベースにはハッシュ値しか保存されず、気楽に確認できるような情報ではないので、逆に「間違ったパスワードを登録してしまった場合」というのが結構大変なのです。
- という事で、なるべく間違ったパスワードを登録するのを防ぐ対策をしておきたいと思います。
- user の newファイルの パスワードの入力欄の下に以下の赤枠の部分を追加してください。
パスワードの登録間違いを防ぐ
- ユーザー登録ページを見ると、新しく入力欄が出てきていると思います。
- 次に userコントローラー を開いて、以下の赤線部分を追加してください。
パスワードの登録間違いを防ぐ
- この「 password_confirmation 」も、bcrypt で追加されたやつで、「 password 」の値と一致するか?を自動的に判定してくれて、一致する場合だけ、@user.save が実行されます。
- もし、一致しない場合は@user.save は実行されず、終わります。(エラーにはならない)
- 次へ。
- 実際に試してみましょう。
- わざと一致しないパスワードをいれて、登録してみます。
- すると、一見登録されたかのような表示になりますが、実際には登録されていないはずです。
パスワードの登録間違いを防ぐ
新規登録が完了しました!と表示されているが、
実際は追加されてない
- 実際に登録されなくなりましたが、「新規登録が完了しました!」と表示されるのは変なので直します。
- 「.save」は「保存が成功したら、true を返し、何かしらの理由で失敗したら false を返す」というメソッドです。
- なので、以下のように if文 で分岐します。
パスワードの登録間違いを防ぐ
- まずは試してみましょう。
- わざとパスワードを一致させなかったら、以下の画面になると思います。
パスワードの登録間違いを防ぐ
- とりあえず成功なのですが、名前などの入力欄が全部消えてしまってますよね?
- 今は名前とパスワードだけなので「また入力しなおしたらええか」って感じですが、もしこれが「住所、年齢、生年月日、電話番号、メールアドレス」などなど、沢山入力した後の場合だとどうでしょうか?「また最初から入力し直すのめんどくさすぎ!!!」って切れそうになりますよね?(実際にこういうサイトありますけど…)
- なので、わざわざ入力した内容は消さずに、「登録に失敗したよ」ってことだけを伝えた方が良いですよね。次へ。
- そのためには以下のようにします。
パスワードの登録間違いを防ぐ
- redirect_to だったところが「 render :new 」(レンダー :ニュー)に変わっていますね。
- これは「newファイルを読み込む」という意味で、再度 newページを表示させるのは redirect_to と同じなのですが、newメソッド は実行しません。(アクションメソッドは実行しない)
- ページだけを開きます。なので、入力したデータが消えることはなく、そのまま残ります。
- これでもう一度試してみます。次へ。
- 試してみると、以下のように、入力した値はそのままになっているはずです。
パスワードの登録間違いを防ぐ
- ただし、今度は「新規登録が失敗しました!」というフラッシュメッセージが表示されていません。
- 実は、アクションメソッドを実行しないと、フラッシュメッセージなどのパラメータの受け渡しも発生しないため、フラッシュメッセージは表示できません。次へ。
- こういう時は以下のようにします。
パスワードの登録間違いを防ぐ
- 「flash.now」は、アクションメソッドを経由せずに、直接 flash にデータを代入する書き方です。
- このようにすると、render でもフラッシュメッセージを表示させられます。
- redirect_to と render の使い分けは、かなり重要なポイントなので覚えておいてください。
- とりあえず、これで「パスワードの登録間違いを防ぐ」ことはできました。
- 同じことをユーザー編集画面にもやっておきましょう。やることはほぼ同じなので、自分でチャレンジしてください。
- ヒント①:user の editファイルと、userコントローラーの updateメソッド の変更が必要です。
- ヒント②:render するのは :newではなく、:edit になるので注意しましょう。
- こんな感じでOKですね。
パスワードの登録間違いを防ぐ
user の editファイル
userコントローラー
- 次は、今まであえて放置していたのですが「入力中のパスワードを表示させないようにする」という事をやっていきます。
- これは、簡単でフォームの「 .text_field 」を「 .password_field 」に変えるだけです。
入力中のパスワードを非表示にする
- 変更するのは「user の new と editファイル」と、「session の newファイル」です。
- それぞれ、パスワード入力欄が2つあるので、すべて変更しておきましょう。やってみてください。
入力中のパスワードを非表示にする
- 上記のようになればOKです。(ちなみにこのことを「マスク化」と言います)
- これで、パスワードを入力中に、誰かに見られたり、監視カメラなどに写ったりしても安心ですね。
- 次は、さらなるセキュリティ向上を狙って「データベースのセキュリティ」をやっていきます。
- 悪い人は色々考えるもんで、データベースにイタズラする方法は色々あって、その対策をしておかないと、大変なことになってしまいます。
- 例えば、本のタイトルなどに「ああああ…」という文字を、何百、何千文字も入力されてしまった場合を想像してください。
- 画面上がああああああああああああああああああで埋め尽くされてしまいますね。
バリデーション
- このように入力された内容は、ちゃんと検証してから保存しないといけません。
- これを「バリデーション」と言って、必ずしもセキュリティが目的というわけではないですが、データベースを使う上で、バリデーションは必須の項目なので、これを実装していきます。
- 基本的にバリデーションはモデルで処理しますので、まずは book の modelファイル(/app/models/book.rb) を開いてください。
- バリデーションは各カラムに対して検証を行いますので、それぞれのカラムに対して、どんな制限が必要か考えていきます。
- まず、book の name ですが、これは本のタイトルですね。
- 先ほども書いた通り、あまりにも長いタイトルは入力できないようにしたいです。
- つまり「 文字数制限 」をしたいので、以下のように書きます。
バリデーション
- 「 validates : カラム名」で、どのカラムへのバリデーションかを指定します。
- その横の「 length: { minimum: 最少文字数, maximum: 最大文字数}」が、文字数の指定で今回の場合だと「最低1文字、最大で100文字」に制限しています。(世界一長い本のタイトルは1300文字あるらしいですが、まぁ普通は100文字あれば十分かと思います)
- (最小文字数か、最大文字数どちらかだけの指定も出来ます)
- これで長すぎるタイトルや、空白のタイトルの場合は保存(.save)できないように出来ます。一応試しておきましょう。
- 下記のように、わざと空白の名前で登録しようとすると、以下のように「パッと見は登録されているように見えるけど、されていない」と言う状態になります。
バリデーション
- これは、少し前に「パスワードの登録間違いをなくす」でも、同じ現状が起きましたよね?
- そう、コントローラーに問題があります。
- createメソッド で、保存はされていなくても「
redirect_to root_path, notice: "本が登録されました!"
」が実行されてしまうことが原因でした。 - 次へ。
- なので、同じように「ちゃんと.save が出来なかった場合」を書いてあげれば良いですね。
バリデーション
- ただ、book の場合は、これだとエラーになってしまいます。次へ。
- なぜ book だとエラーになってしまうか?というと、「 set_stars 」というメソッドが実行されないからです。
- 忘れているかも知れませんが、このメソッドは「評価」をセレクトフィールドにする時に使うために用意したメソッドで、このメソッドが実行されないと、book のフォームは表示できません。
- この「 set_stars 」は、「 before_action 」で、自動的に実行されるようになっていますが、これも「 render 」だと実行されません。
- (redirect_to だと実行されるが、ファームに入力したデータが消えてしまいましたね)
- ということで、render の場合は、自分で set_star も実行させないといけません。
- つまり以下のように書きます。
バリデーション
- 忘れない内に、updateメソッド にも同じことをしておきましょう。
バリデーション
バリデーション
- 試してみると、ちゃんと「本の登録に失敗しました!」と表示されましたね?これでOKです。
- ただ、今度は別の問題が発生しているのですが、気づいたでしょうか?
- 実はレイアウトが少しおかしくなってしまいます。
通常のページ
登録失敗後のページ
謎の空白が出来ている!!
- まぁ言うて、これぐらいなら良いかなって感じで、とりあえず気づかなかったことにして先に進みましょう。
- 次は、「 date 」(読んだ日)に行きましょう。
- これは「空白はダメ」という制限をしてみましょう。つまりなにかしらの日付を指定しないといけない、という事です。
- これは以下のように書きます。(書く場所は name の時と同じく、book.rb です)
バリデーション
- 「 presence: true 」(プレゼンス)と書くことで、空白はダメという制限ができます。
- ちなみに、name には、この「 presence: true 」がありませんが、length で「最少でも1文字以上」と指定しているので、実質「空白はダメ」という意味も含まれています。
- これで試してみましょう。
- ※本のタイトルは何かしら入力した状態で、日付は入力せずに登録ボタンを押してください。
- それで登録されなければOKです。
- ちゃんとバリデーションは動いたかと思いますが、またもやレイアウトがおかしくなっています。「読んだ日」と「評価」の部分です。
バリデーション
「読んだ日」が横長になってしまい、2段に分かれている
通常のページ
登録失敗後のページ
- 流石にこれは、なんとかせねば!と思いますが、まぁとりあえずはバリデーションを先に進めます。(後で直します)
- ちゃんとバリデーションは動いたかと思いますが、またもやレイアウトがおかしくなっています。「読んだ日」と「評価」の部分です。
バリデーション
「読んだ日」が横長になってしまい、2段に分かれている
通常のページ
登録失敗後のページ
- 流石にこれは、どげんとせんといかん!と思いますが、まぁとりあえずはバリデーションを先に進めます。(後で直します)
- 次は「評価」ですね。
- これはセレクトフィールドを使っており、何も選ばなかった場合は「★5」になるようにしていますが、念のためバリデーションしておきましょう。
- これは「空白はダメ」という指定だけでいいでしょう。つまり date と同じです。
- さらに「感想」も同時にやっちゃいましょう。
- これは name と同じように「文字数の制限」をかけましょう。
- ただし「感想」は「入力しなくていい」ようにし、「最大文字数は2000」ぐらいにしておきたいです。
- では「評価」と「感想」のバリデーションを、自分でやってみてください。
バリデーション
- 以下の感じで良いですね。
バリデーション
- これで、バリデーション自体はOKです。
- 後は「なぞのレイアウト崩れ」を直してみましょう。次へ。
- 実は「バリデーションしたらフォームのレイアウトが崩れる」という現象は、Rails ではあるあるの現象で、原因は「Rails が勝手にタグ(要素)を追加するから」です。
- バリデーションエラーが出る前と、後で、HTMLがどう変化してるか比べるとわかりやすいです。
- 「読んだ日」と「評価」のHTMLを見てみましょう。
なぜレイアウトが崩れるのか?
バリデーションエラー前
バリデーションエラー後
ここにタグ(要素)が勝手に増えてる
- この「 field_with_errors 」という class が設定されたタグが追加されたことによって、レイアウトが崩れてしまうのです。
- 次へ。
- じゃあどうするか?というと、2つの考え方があります。
- 勝手に追加されないようにする
- 追加されるタグにCSSを設定する
- 今回は、CSSを排除して進めていますので、「勝手に追加されないようする」方向で進めます。
- 「 config/application.rb 」のファイルに「 config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag.html_safe } 」というコードを追加します。
- 追加する場所は 21行目 が良いとおもいます。
- ※画像はつぎのページのありますので、参考にしてください。
- ※ファイルを変更したら Rails を再起動してください。
なぜレイアウトが崩れるのか?
なぜレイアウトが崩れるのか?
ココに追加する
バリデーション
- Rails を再起動したら、ちゃんとレイアウトが直っていると思います。これでOKです。
バリデーション
- 次は、user のバリデーションに行きましょう。ここは2つだけですね。
- まず name ですが、これは「最低1文字以上、最大で32文字」という文字数制限と、「ユニークな名前じゃないとダメ」という制限をします。
- 「ユニークな名前」とは、「面白い名前」という意味ではなく、「かぶらない名前」という意味です。
- 「すでに登録している名前と、かぶってしまうとダメ」「唯一の名前じゃないとダメ」という意味です。
- なぜこれを制限するかと言うと、この name はパスワードとセットで「ログイン情報」として使いますよね?
- 例えば「キノコ」という名前が2つ登録されていたとしたら、ログインのときに「どっちのキノコなの?」ってなってしまいます。
- なので、ログインに使う情報は必ず「ユニーク(唯一、一意)」でないといけません。
- これは、以下のように書きます。(書く場所は、app/models/user.rb ですね)
- 「 uniqueness: true 」の部分が「ユニークでないとダメ」の指定です。
カンマで区切れば、複数のバリデーションを設定できます
バリデーション
- 最後はパスワードです。
- これはセキュリティ的に非常に重要なところです。
- 「ユーザーのパスワードやから、データベースやサーバとは関係ないんちゃう?」と思う人もいるかもしれませんが、ユーザーのセキュリティを考慮するのは、サイト管理者の役目です。
- 強制的にでも適当なパスワードをつけられないようにして、安全性を守る義務があります。
- では、どんなバリデーションが良いかと言うと、
- 最低8文字以上じゃないといけない
- パスワード入力欄と、確認用パスワード入力欄が一致しないといけない
- 大文字、小文字、数字が、それぞれ最低1つ以上含まれないといけない
- これが一般的なパスワードのバリデーションです。
- まず1の文字数制限は、以下のように書けば良いですね。まずは下のようにしてください。
バリデーション
- 次に、2「パスワード入力欄と、確認用パスワード入力欄が一致しないといけない」は、これは「 has_secure_password 」で暗号化をしている流れで、自動的にバリデーションをやってくれているので、何も書かなくてOKです。
- 最後の3「大文字、小文字、数字が、それぞれ最低1つ以上含まれないといけない」について説明しておきます。
- これは「パスワードが推測されないため」に重要な内容です。
- 「aaaaaaaa」や「12345678」みたいな適当なパスワードが作れなくなる、という事です。
- 最低でも「Aaaa1234」みたいに大文字、小文字、数字を組み合わせないといけないので、セキュリティが一気に高まります。
- これの設定には「正規表現」(せいきひょうげん)というものを使い、文字列の組み合わせを制限します。
- 「 format: { with: /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/ } 」と書きます。
バリデーション
- 一応、正規表現の部分の説明も簡単にしておきます。
- 「 /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/ 」の部分が正規表現の部分で、
- 「(?=.*[a-z])」が「a-z(小文字の英語)が、1つ以上ある」という意味で、
- 「(?=.*[A-Z])」が「A-Z(大文字の英語)が、1つ以上ある」という意味で、
- 「(?=.*\d)」が「数字が、1つ以上ある」という意味になります。
- よりセキュリティを高めたい場合は「記号も最低1つ以上含まれること」という条件を追加します。その場合は上の正規表現に「(?=.*[\p{S}])」を追加するので、「 /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\p{S}])/ 」になります。
- これでバリデーションはOKです。
- ※現時点で、ユーザー編集の際に、ちょっと問題がでます。
- パスワードだけを編集したい場合でも、ユーザー名を変えないと「※その名前は設定済です。別に名前にしてください。」と表示されてしまいます。
- つまり、新規登録のバリデーションが邪魔をして、編集の時にも「あ、同じ名前のユーザーいる!」と判定してしまうのです。(実際は自分自身の名前)
- これに関してはもう少し後で変更します。
エラーの詳細を表示
- バリデーションは出来ましたが、今のままだと「何が悪いのかわからない」状態になっています。
- 「パスワードは8文字以上で、大文字、小文字、数字を含めてください」や「感想は2000文字以内にしてください」みたいに、ちゃんと表示してあげるのが親切ですよね。
- ということで、具体的なエラーを表示する方法をやっていきます。
- 大きく分けて2つの方法があって、「最初から用意されているエラーメッセージを使う方法」と「一つずつメッセージを設定する方法」があります。
- 最初から用意されているものを利用するのが楽な気がするのですが、英語のメッセージしか用意されておらず、日本語化(ローカライズ)する手間を考えると、「一つずつメッセージを設定する方法」の方が楽で、自由にカスタマイズしやすいので、そちらで進めましょう。
- 一つずつメッセージを設定するのは、バリデーションの箇所でやります。
- 先ほどの流れで user の方からやっていきます。
- まず、name のバリデーション部分を、以下のようにしてみましょう。出来たら次のページへ。
エラーの詳細を表示
- 新しく追加した message: に、エラーの時に表示させたい文字列を指定します。
- length でのエラーの場合は、length: {} の方に指定し、uniqueness でのエラーに関しては uniqueness {} に設定します。
- uniqueness の true がなくなっていますが、デフォルトで true なので、メッセージを設定する場合は削除します。
- ※book で使っている presence も同じです。
- uniqueness の true がなくなっていますが、デフォルトで true なので、メッセージを設定する場合は削除します。
- このコードで、エラーメッセージが「 @user.errors 」というところに保存されます。
- この段階では保存されるだけなので、ビュー側で、これを取り出して表示するコードが必要です。
- メッセージの取り出し方は「 @user.errors[:name] 」というようにキーとしてカラム名を指定するだけです。
- ということで、後はビューに「エラーメッセージがあれば、表示する」というコードを追加してあげます。
- まずは user の newページ からやりましょう。newファイルの名前入力の場所の下に、以下のコードを追加してください。
「 .any? 」は「空白ではないか?」を判定するメソッドです。
空じゃない場合だけ、<span>要素が作られ、そこにnameのエラーメッセージの内、最初のものが表示されるようにしています。
エラーの詳細を表示
- 「 @user.errors[:name].first 」の「 .first 」は配列から、最初の要素(0番の要素)を取り出すメソッドです。
- もしnameカラム に関してのエラーメッセージが複数あった場合でも、1つだけしか表示されませんが特に問題ないため1つだけにしています。
- 複数すべて表示するとなると、each文 を書くことになり、コードがごちゃごちゃになるので、今回は1つだけ表示にしてます。
- では、試してみると以下のような表示になります。
length でのエラーメッセージ
uniqueness でのエラーメッセージ
- エラー内容が明確で、どこを、どう直せば良いか、わかりやすいですよね?
- (ここまでちゃんと表示してくれるサイトは、以外と少なかったりしますが…)
エラーの詳細を表示
- では、次は password に行きましょう。
- これも同じで、まずは user.rb でメッセージを設定します。※文字が小さくて見にくいですが…
- 次に、newページ にメッセージを表示できる場所を作りましょう。
- password と一緒に、password_confirmation の方も作ってしまいましょう。
- 次のページ。
エラーの詳細を表示
- ひとまずこんな感じです。
- これで表示してみると、謎の現象が起こります。次へ。
エラーの詳細を表示
- パスワードを空白で登録してみたい場合の表示を見てみます。
- こんなメッセージ設定してないぞ!?っとなってしまうのですが、これは「 has_secure_password 」(暗号化のやつ)が勝手にやってます。
- (このように Rails は便利な反面「なんか知らんうち勝手にやられて困る」ということが非常に多いです…)
- 「 has_secure_password 」は、自動でバリデーションをやってくれており、メッセージも勝手に設定しています。
- なので、まずは「バリデーションはこっちでやるから、キミはやらんとって」と教えてあげます。
- 「 has_secure_password 」の横に、以下のコードを追加してください。
エラーの詳細を表示
- これで勝手なバリデーションはなくなるので、ちゃんと自分で設定したメッセージが表示されます。
- ただ、またもや別の問題も出てきます。
- それは「password と password_confirmation が一致しているか?」というバリデーションもなくなってしまうのです。
- つまり、一致してなくても登録できてしまいます。
- これは「 has_secure_password 」君が勝手にやってくれていたのを「勝手にやるな」と止めさせたせいです。なので自分でやります。
- とはいえ、別に難しくはなく、password のバリデーションに、以下を追加するだけです。
- 小さくて見づらいですが、「, 」と「 confirmation: { message: "※パスワードが一致しません"} 」が追加されています。
- (カンマを忘れないように)
エラーの詳細を表示
- 今度こそOKですね!
- これで、newページにはエラーメッセージが表示されるようになりましたね。
- editページ にも同じように設定が必要なのですが、これは後回しにしますので、やらなくてOKです。
- ※一気にサクッとやる方法があるので。
エラーの詳細を表示
- 次は book のエラー表示に行きましょう。
- こっちは、特にひっかかるポイントはないので、チャレンジしてみましょう。
- name のエラーメッセージは「※本のタイトルは1文字以上、100文字以内で入力してください。」になること。
- date のエラーメッセージは「※読んだ日を選んでください」になること。
- star のエラーメッセージは「※評価を選んでください」になること。
- impression のエラーメッセージは「※感想は2000文字以内で入力してください。」になること。
- 上記のエラーメッセージが表示されるように、app/models/book.rb にメッセージを設定し、book のnewファイルに、設定したメッセージが表示されるようにしてください。
- ※editファイルの方は、やらなくて良いです。
- ヒント①:presence にメッセージを設定する時は、uniqueness のときと同じように、true は消して、メッセージを追加します。
試してみよう
- ここまでできたら、もう一つのオリジナルサイトも同じようにパスワードの暗号化や、バリデーション設定してください。
- バリデーションの内容は自分で考えて結構ですが、必ず1カラムに対して、最低1つはバリデーションの設定をしてください。
- 今回で、パスワードの暗号化と、バリデーションの設定も出来ました。
- 「Ruby on Rails基礎4」に進んでください。
お疲れ様です。
Ruby on Rails基礎3
By kinocode
Ruby on Rails基礎3
- 1,056