コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.7 - Secure password -

# はじめに
第７回目となる今回は、モデルにセキュアなパスワードをもたらしてくれる`has_secure_password`メソッドの使い方を紹介していきます。
パスワードは他人に知られてはまずいものです。万が一、データを抜かれたり画面に表示されちゃったりしても一目でわからないようになっていることが望ましいですね。そんなことを実現してくれるメソッドが`has_secure_password`です。

# 前回のソースコード
前回のソースコードはこちらに格納してます。今回のだけやりたい場合はこちらからダウンロードしてください。

- [at946/rails_on_docker_handson at vol.06](https://github.com/at946/rails_on_docker_handson/tree/vol.06)

# `has_secure_password`
Railsでは、モデルにセキュアなパスワード属性を実装するメソッドとして、`has_secure_password`が用意されています。モデルに対して`has_secure_password`を適用することでモデルは以下の恩恵を受けることができます。

- 仮想属性として`password`と`password_confirmation`を利用可能
- `password`と`password_confirmation`の同一性について勝手に検証してくれる
- DBにはハッシュ化した`password_digest`を保存するようになる
- ハッシュ値でパスワード検証をする`authenticate`メソッドを利用可能

ハッシュ化についてちょっとお話ししておきます。ハッシュ化はある文字列を不可逆な別の文字列に変換してくれます。『不可逆』とは元に戻せないという意味です。
『暗号化』の場合は『復号化』することで元の文字列に変換しなおせるんです。これは『可逆』といいますね。

では、早速モデルに`has_secure_password`メソッドを適用していきます。

`has_secure_password`ではハッシュ化を行うために`bcrypt` gemを利用します。まずは、`bcrypt`のインストールからやっていきます。`Gemfile`の中身をみるとわかりますが、`bcrypt`は最初から`Gemfile`の中に書かれておりコメントアウトされているだけです。なのでコメントアウトをとって`docker-compose build`をするだけでOKです。

```diff:Gemfile
  ...
  # Use Active Model has_secure_password
- # gem 'bcrypt', '~> 3.1.7'
+ gem 'bcrypt', '~> 3.1.7'
  ...
```

```
$ docker-compose build
```
ビルド後はコンテナを起動してコンテナ内に入っておきます。

```
$ docker-compose up -d
$ docker-compose exec web ash
```
次に、`has_secure_password`メソッドを使うようになると`password`をハッシュ化した値を`password_digest`カラムにDB保存するようになります。マイグレーションファイルを作成しDBに`password_digest`カラムを追加しておきます。

```
# rails g migration add_password_digest_column_to_user password_digest:string
Running via Spring preloader in process 251
      invoke  active_record
      create    db/migrate/20200130075100_add_password_digest_column_to_user.rb

# rails db:migrate
== 20200130075100 AddPasswordDigestColumnToUser: migrating ====================
-- add_column(:users, :password_digest, :string)
   -> 0.0522s
== 20200130075100 AddPasswordDigestColumnToUser: migrated (0.0533s) ===========
```
そして、Userモデルにhas_secure_passwordメソッドを適用します。

```diff:app/models/user.rb
  class User < ApplicationRecord
    before_save { self.email = email.downcase }
+   has_secure_password

    validates :name,
      presence: true,
      length: { maximum: 50 }

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
    validates :email,
      presence: true,
      length: { maximum: 255 },
      format: { with: VALID_EMAIL_REGEX },
      uniqueness: { case_sensitive: false }
  end
```
これで完了です！簡単ですね！
では恩恵がちゃんと受けられているか確認してみましょう。

## `password`と`password_confirmation`
まずは`password`属性と`password_confirmation`属性の二つの仮想属性が利用できることを確認してみます。

```
# rails c
```
```pry
> user = User.new(name: "hanako", email: "hanako@sample.com", password: "password", password_confirmation: "password")
=> #<User:0x00005610de139120
 id: nil,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: nil,
 updated_at: nil,
 password_digest: [FILTERED]>
```
```pry
> user.password
=> "password"
```
```pry
> user.password_confirmation
=> "password"
```
```pry
> user.password_digest
=> "$2a$12$KWcqXmTL6sVj0ch127o0.eemXFmAgPRqU98D5CLqXNJ8.5F.10AiS"
```
DBカラム的に`password_digest`しかないはずですが、`password`、`password_confirmation`が使えていることがわかります。さらにその結果が`password_digest`として利用可能なのもわかりますね。

## `password`と`password_confirmation`の一致性確認
ここで一度`valid?`メソッドを使ってみましょう。これは今現在のモデルでvalidationを突破できるのかを確認できるメソッドです。先ほどまでは`save`でvalidationできるかを確認していましたが、実はvalidationを調べるためだけであればこれでOK。

```pry
> user.valid?
  User Exists? (6.5ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> true
```
今は`password`と`password_confirmation`がマッチしているのでvalidationは通っているようです。では、`password_confirmation`を別の文字列に変更した場合どうでしょうか？

```pry
> user.password_confirmation = "password1"
=> "password1"
```
```pry
> user.valid?
  User Exists? (2.3ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false
```
```pry
> user.errors.full_messages
=> ["Password confirmationとPasswordの入力が一致しません"]
```
validationでエラーになっていることがわかりましたね...あ、日本語化していない。
日本語化しましょう！

```diff:config/locales/ja.yml
  ja:
    activerecord:
      attributes:
        user:
          name: "お名前"
          email: "メールアドレス"
+         password: "パスワード"
+         password_confirmation: "確認用パスワード"
      errors:
  ...
```

```pry
> reload!
Reloading...
=> true
```
```pry
> user.valid?
  User Exists? (2.3ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false
```
```pry
> user.errors.full_messages
=> ["確認用パスワードとパスワードの入力が一致しません"]
```
`password`と`password_confirmation`が一致していない場合はエラーが起きていますね。
ただ、`password_confirmation`のようなものは必要なのか？という論争もあると思います。
「[登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK](https://uxmilk.jp/18568)」
例えばこちらの記事では、確認用パスワードを用意するのではなく、パスワードの欄のマスクを外せるようにした方がコンバージョンが上がると述べられていたりします。
`has_secure_password`では、`password_confirmation`が`nil`の場合、`password`と`password_confirmation`の一致を確認しません。

```pry
> user.password_confirmation = nil
=> nil
```
```pry
> user.valid?
  User Exists? (3.1ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> true
```
なので確認用パスワードを使う使わないによらず、`has_secure_password`メソッドは強力なのです。

## DBではハッシュ化された`password_digest`が使われる
最初にもお話しした通り、ハッシュ化は不可逆なデータ変換です。
`has_secure_password`では`password_digest`にハッシュ化された`password`を保存します。それを確認してみましょう！

実際にUserを作成してみましょう。

```pry
> user.save
  (0.4ms)  BEGIN
  User Exists? (2.1ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
  User Create (8.5ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "hanako"], ["email", "hanako@sample.com"], ["created_at", "2020-03-09 14:37:38.358397"], ["updated_at", "2020-03-09 14:37:38.358397"], ["password_digest", "$2a$12$KWcqXmTL6sVj0ch127o0.eemXFmAgPRqU98D5CLqXNJ8.5F.10AiS"]]
   (2.3ms)  COMMIT
=> true
```
```
> user = User.find(1)
  User Load (1.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<User:0x00005610e0b21ed8
 id: 1,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: Mon, 09 Mar 2020 14:37:38 JST +09:00,
 updated_at: Mon, 09 Mar 2020 14:37:38 JST +09:00,
 password_digest: [FILTERED]>
```
```pry
> user.password
=> nil
```
```pry
> user.password_digest
=> "$2a$12$KWcqXmTL6sVj0ch127o0.eemXFmAgPRqU98D5CLqXNJ8.5F.10AiS"
```
`user.password_digest`をみてもなんのことやらわかりませんね。ちょっと安心です。

## パスワード認証する`authenticate`メソッド
さて、ハッシュ化されてセキュアになったのはいいのですが、不可逆なのでこのままではパスワードで認証ができません。`has_secure_password`ではハッシュ化されたパスワードの認証をするための`authenticate`メソッドが用意されています。これは、`password_digest`の値と入力した`password`を同じハッシュ関数でハッシュ化した値の一致をチェックしてくれるものです。

```pry
> User.find_by(email: "hanako@sample.com").authenticate("a")
  User Load (3.9ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> false
```
```pry
> User.find_by(email: "hanako@sample.com").authenticate("password")
  User Load (2.5ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "hanako@sample.com"], ["LIMIT", 1]]
=> #<User:0x00005610de34c250
 id: 1,
 name: "hanako",
 email: "hanako@sample.com",
 created_at: Mon, 09 Mar 2020 14:37:38 JST +09:00,
 updated_at: Mon, 09 Mar 2020 14:37:38 JST +09:00,
 password_digest: [FILTERED]>
```
このように`find_by`と組み合わせて使えばメールアドレスとパスワードの2key認証を実装できます。`authenticate`は不一致の場合は`false`を、一致の場合はモデルオブジェクトを返却します。

## `password`にvalidationを設ける
仮想属性である`password`に対してもvalidationをつけることができます。
一方で、`has_secure_password`で作られた`password`にはデフォルトで`presence`のvalidationが設定されています。これに加えて6文字以上でないといけない文字数制限をつけてみましょう。
デフォルトで`presence`がついてはいるのですが、入力がない場合に`presence`と`length`の両方に引っかかってしまうので`presence`については適用しない方がわかりやすいでしょう。
`has_secure_password`に`validations: false`オプションをつけてデフォルトの`presence` validationを向こうにした後に、他の属性と同様に`length`のvalidationをつけてみましょう。

```diff:app/models/user.rb
  class User < ApplicationRecord
    before_save { self.email = email.downcase }
-   has_secure_password
+   has_secure_password validations: false

    validates :name,
      presence: true,
      length: { maximum: 50 }

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
    validates :email,
      presence: true,
      length: { maximum: 255 },
      format: { with: VALID_EMAIL_REGEX },
      uniqueness: { case_sensitive: false }
+
+   validates :password,
+     length: { minimum: 6 }
end
```
`password`のvalidationについて試してみましょう。

```pry
> reload!
Reloading...
=> true
```
```pry
> user = User.new(name: "john", email: "john@sample.com")
=> #<User:0x00005610deba8110
 id: nil,
 name: "john",
 email: "john@sample.com",
 created_at: nil,
 updated_at: nil,
 password_digest: nil>
```
```pry
> user.valid?
  User Exists? (4.1ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> false
```
```pry
> user.errors.full_messages
=> ["パスワードは6文字以上で入力してください"]
```
`password`に`nil`だとバリデーションにひっかかります。

```pry
> user.password = "a" * 5
=> "aaaaa"
```
```pry
> user.valid?
  User Exists? (4.6ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> false
```
```pry
> user.errors.full_messages
=> ["パスワードは6文字以上で入力してください"]
```
`password`が5文字以下でも同様にバリデーションに引っかかります。

```pry
> user.password = "a" * 6
=> "aaaaaa"
```
```pry
> user.valid?
  User Exists? (3.2ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> true
```
ということで、`password`の`length` validationが正しく挙動していることがわかりました。

# Userを作成
最後にこのバリデーションの中でちゃんとUserを作成できることを確認しておきましょう！

```pry
> User.create(name: "John Smith", email: "john@sample.com", password: "password")  
   (0.8ms)  BEGIN
  User Exists? (4.0ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
  User Create (1.1ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "John Smith"], ["email", "john@sample.com"], ["created_at", "2020-03-09 14:44:33.407908"], ["updated_at", "2020-03-09 14:44:33.407908"], ["password_digest", "$2a$12$FTRT7cRJ4c0k.H68k49yOOurmwSbGaRHwzOXLuP2LYyXD30sg6FDS"]]
   (1.9ms)  COMMIT
=> #<User:0x00005610e06db9a0
 id: 2,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 14:44:33 JST +09:00,
 updated_at: Mon, 09 Mar 2020 14:44:33 JST +09:00,
 password_digest: [FILTERED]>
```
validationにひっかからないUserであればちゃんと作成できることが確認できましたね！

# ユーザー情報を確認するViewを作ってみる
ここまででUserモデルがほぼ完成しました。この情報をUIで見れるようにViewを作ってみましょう！

Scaffoldを思い出してください。ユーザーの詳細情報を見るためのページは`/users/:id`のURLにアクセスして閲覧することができ、`show`アクションにルーティングされていました。

今回もそれにそってユーザーのページを作っていきます。

## ユーザー詳細ページを作成する
ユーザー詳細ページは、`/users/:id`のパスにアクセスした時に、その`id`のユーザーの詳細情報が表示されるページにしたいと思います。
この通りになるように、ファイルの編集や作成を行っていきます。いままで`rails g ~`コマンドでファイルを作成してきましたが、Railsの規則に則っていれば普通にファイルを作成しても動きます。

### Routing
まずは、`/users/:id`のルーティングを生成します。以前、Scaffoldの時に`resources`メソッドを使ってUsersコントローラーに対するルーティングを定義しました。`/users/:id`はその時の`show`アクションへのルーティングと同じです。
`resources`メソッドは`only`オプションをつけることで特定のアクションへのルーティングのみを定義してくれます。今回はこのオプションを使ってルーティングを定義します。

```diff:config/routes.rb
  Rails.application.routes.draw do
    root 'static_pages#home'
+
+   resources :users, only: [:show]
end
```
これでルーティングの設定は完了です。
今`rails console`を起動していると思うので`show-routes`コマンドでルートを確認してみましょう。

```pry
> show-routes
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                                 root GET    /                                                                                        static_pages#home
                                 user GET    /users/:id(.:format)                                                                     users#show
        rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                  action_mailbox/ingresses/mandrill/inbound_emails#create
        rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                  action_mailbox/ingresses/postmark/inbound_emails#create
           rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                     action_mailbox/ingresses/relay/inbound_emails#create
        rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                  action_mailbox/ingresses/sendgrid/inbound_emails#create
         rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                              action_mailbox/ingresses/mailgun/inbound_emails#create
       rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#index
                                      POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#create
    new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                             rails/conductor/action_mailbox/inbound_emails#new
   edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                        rails/conductor/action_mailbox/inbound_emails#edit
        rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#show
                                      PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#destroy
rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                      rails/conductor/action_mailbox/reroutes#create
                   rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
            rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
                   rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
            update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
                 rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create
```
色々と出てくると思いますが、とりあえず重要なのは最初の２つのルートです。今、`config/routes.rb`で定義しているルーティングが表示されていることがわかると思います。

同様のことを`rails routes`コマンドでも確認できます。

```
> quit
```
```
# rails routes
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                                 root GET    /                                                                                        static_pages#home
                                 user GET    /users/:id(.:format)                                                                     users#show
        rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                  action_mailbox/ingresses/mandrill/inbound_emails#create
        rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                  action_mailbox/ingresses/postmark/inbound_emails#create
           rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                     action_mailbox/ingresses/relay/inbound_emails#create
        rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                  action_mailbox/ingresses/sendgrid/inbound_emails#create
         rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                              action_mailbox/ingresses/mailgun/inbound_emails#create
       rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#index
                                      POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                 rails/conductor/action_mailbox/inbound_emails#create
    new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                             rails/conductor/action_mailbox/inbound_emails#new
   edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                        rails/conductor/action_mailbox/inbound_emails#edit
        rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#show
                                      PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#update
                                      DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                             rails/conductor/action_mailbox/inbound_emails#destroy
rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                      rails/conductor/action_mailbox/reroutes#create
                   rails_service_blob GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                               active_storage/blobs#show
            rails_blob_representation GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
                   rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                              active_storage/disk#show
            update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                      active_storage/disk#update
                 rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                           active_storage/direct_uploads#create
```

### Controller
次にUsersコントローラーを作成していきます。コントローラーは複数形の名称にするのがルールです。

```
# touch app/controllers/users_controller.rb
```

```rb:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end
```
`users`コントローラに`show`アクションがある、というところまでは今までの内容からわかりますね。
`show`アクションの中身をみてみます。

`params[:id]`は`/users/:id`のパスパラメータの`:id`を取得しています。例えば`/users/1`にアクセスした場合は`params[:id]=1`だし、`users/2`にアクセスした場合は`params[:id]=2`です。
`User.find()`はModel CRUDの回で話した通り、`id`でUserのモデルオブジェクトを取得するメソッドなので、パスの`id`のユーザーがこれで取得できるわけです。
あとはViewに引き渡すためにこれをインスタンス変数`@user`に代入しています。

ではこれを受け取るViewを作っていきます。

### View
まず、Viewファイルを格納するディレクトリを作成して、その中に`show.html.erb`ファイルを作ります。
UsersコントローラのためのViewなので`app/views/users/`ディレクトリ内にファイルを作成していきます。

```
# mkdir app/views/users
# touch app/views/users/show.html.erb
```

最初は情報が表示されていればOKとしましょう。特にUIは拘らない。

```erb:app/views/users/show.html.erb
<div class="container my-5">
  <%= @user.name %>
  <br>
  <%= @user.email %>
</div>
```
`name`と`email`を表示。

`http://localhost:3000/users/2`にアクセスしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/703a7210-49de-6067-3525-bf3c289dc486.png)
こんなページが表示されていれば成功です！
（`id`は実際にDBに格納されているデータ次第ですので、Rails consoleで`User.all`を打つなどして存在するUserの`id`を確認してみてくださいね。）

# 後片付け
次回に向けてデータを消しておきます。

```
# exit
```
```
$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset
```
DBコンテナが立ち上がった状態だと思うのでdownさせます。

```
$ docker-compose down
```

# まとめ
今回は、`has_secure_password`メソッドを使ってセキュアなパスワードが利用できるようになりました。
コード的にいえばたった1行`has_secure_password`を付け加えるだけでパスワードをハッシュ化してセキュアに扱うことができるようになる。これって強力なメソッドですよね。

さらにユーザーの情報を表示するページまで作ることができました。
次回はSign up（ユーザー登録）ページを作っていきましょう！

では、次回も乞うご期待！ここまでお読みいただきありがとうございました！

Next: [コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.8 - Sign up - - Qiita](https://qiita.com/at-946/items/e38d94b1f7236e8ed0fe)

# 本日のソースコード
- [at946/rails_on_docker_handson at vol.07](https://github.com/at946/rails_on_docker_handson/tree/vol.07)

# Reference
- [Ruby on Rails チュートリアル：実例を使って Rails を学ぼう](https://railstutorial.jp/)
- [登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK](https://uxmilk.jp/18568)
- [「ハッシュ」とは何なのか、必ず理解させます | ALIS](https://alis.to/yoshihiro/articles/3bNEvGdBW0RJ)

# Other Hands-on Links
- https://github.com/at946/rails_on_docker_handson/tree/master