railsでログイン機能実装

# はじめに
[前回](https://t.co/OhP19YVRAh?amp=1)に引き続いての投稿です！
今回は、ログイン機能の実装についてまとめます！！

# 目次
1. [Userモデルを作成](#Chapter1)
1. [gemを導入](#Chapter2)
1. [user.rbに追記](#Chapter3)
1. [ログイン機能実装](#Chapter4)
1. [ログイン情報を取得](#Chapter5)
1. [ログアウト機能実装](#Chapter6)
1. [ログインしてない場合の処理](#Chapter7)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# Userモデルを作成
まずUserモデルを作成します。

```
rails g model User name:string email:string password_digest: string
```
こちらを実行し、rails db:migrateしてください。
password_digestというのは、Rails標準機能のhas_secure_passwordを使用する際の命名規則となります。

<a id="#Chapter2"></a>
# gemを導入
先程作成したpassword_digestには、入力されたパスワードを不可逆にハッシュ化したものが入ります。passwordをそのままDBに保存すると、流出した際に大変危険ですので、このようにすることで漏洩対策となります。
そこで、ハッシュ関数を提供する「bcrypt」というgemを導入します。

```Gemfile
gem 'bcrypt', '~> 3.1.7'
```

gemfileに追記したら、bundle installします。

<a id="#Chapter3"></a>
# user.rbに追記
gem導入後、やることは一つだけ。以下を追記します。

```User.rb
has_secure_password
```

これでした準備が完了です！次に、controllerを実装していきます。

<a id="#Chapter4"></a>
# ログイン機能実装
ログイン機能は、session[:user_id]というセッションにユーザーのidを保持することで、実現します。
まず、sessions_controller.rbを、ログイン用のnewメソッドと共に作成します。

```
rails g controller Sessions new 
```

このnewアクションがログイン画面への遷移となります。今回は、以下のようなフォームにしました。

```
h1 ログイン

= form_with scope: :session, local: true do |f|
  .form-group
    = f.label :email, 'メールアドレス'
    = f.text_field :email, class: 'form-control', id: 'session_email'
  .form-group
    = f.label :password, 'パスワード'
    = f.password_field :password, class: 'form-control', id: 'session_password'
  = f.submit 'ログインする', class: 'btn btn-primary'


```

送られてくるパラメータをもとに、ログイン処理を行います。
まずは、routesの編集です。

```config/routes.rb
  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create' //こちらを追加
```

```controllers/sessions_controller.rb

  def create
    // users テーブルから、一致するメールアドレスが存在するか検索します。
    user = User.find_by(email: session_params[:email])
    // メールアドレスが存在した場合は、パスワードの認証をauthenticateメソッドで行います。
    if user&.authenticate(session_params[:password])
      // 認証に成功したら、sessionにuserのidを保持します。
      session[:user_id] = user.id
      redirect_to root_url, notice: 'ログインしました。'
    else
      render :new
    end
  end

  private
  def session_params
    params.require(:session).permit(:email, :password)
  end
```

<a id="#Chapter5"></a>
# ログイン情報を取得
以下のコードで、ユーザの情報を取得できます。

```
User.find_by(id: session[:user_id])
```

しかし、毎回この記述をするのは面倒なので、application.rbにcurrent_userという変数を定義し、どのcontrollerからでも利用できるようにします。また、helper_methodを指定し、viewでも使えるように書きます！

```controllers/application.rb
  helper_method :current_user

  private
  def current_user
    // session[:user_id]に値があれば（ログインしていれば）、@current_userにログインユーザーの情報を格納する
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
  end
```

<a id="#Chapter6"></a>
# ログアウト機能実装
ログアウト機能は、session[:user_id]を削除することで実現します。

```routes.rb
delete 'logout', to: 'session#destroy'
```

```session_controller.rb
  def destroy
    reset_session
    redirect_to root_url, notice: 'ログアウトしました'
  end
```

viewは以下のようになります

```application.html.slim
- if current_user
  li.nav-item= link_to 'ログアウト', logout_path, method: :delete, class: 'nav-link'
- else
  li.nav-item= link_to 'ログイン', login_path, class: 'nav-link'

```

<a id="#Chapter7"></a>
# ログインしてない場合の処理
ログインしなければすべての機能を使えなくする場合は、application_controllerにメソッドを追加します。

```application.rb
before_action :login_required

  private
  def login_required
    // current_userがnil(ログインしていない)とき、ログイン画面にredirectさせる
    redirect_to login_url unless current_user
  end
```

上記の記述の場合、ログイン画面もログインしないと見られないというカオスな状況になります。
そこで、session_controllerだけはこの制約を解除します。

```session_controller.rb
  skip_before_action :login_required
```

<a id="#reference"></a>
# 参考文献
- [現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4-ebook/dp/B07JHQ9B5T)