gem(device等)を使わないログイン機能の実装（rails）

#　記事投稿の背景

今週はrailsチュートリアルを行い、そこで実装したログイン機能についてまとめ&復習を行なっていきたので記事を執筆しました。

これまで簡単な投稿サイトをrailsで作った経験はあるのですが、その際はgemのdeviceを使ってログイン機能を実装していたのでいい経験になりました。

それでは行ってみましょう！！

##　ログインの使用について
ログインの使用についてはEmailとPasswordを用いる、
またサインインはName、Email、Passwordを登録できるとします。

# ①ユーザー登録機能の実装

##　（User）モデルの作成
ユーザーの情報をDBに登録するためにモデルを作って行きましょう。

```ruby:ターミナル
$rails g model User name:string email:string digest:string
```
このコマンドにより

| user       |       |
|:-----------------|------------------:|:------------------:|
| id            |             integer | 
| name           |            string |   
| Email         |             stirng| 
| created_at            |              datetime | 
| updated_at             |             datetime| 
| password_digest       |           string|   

上記のようなモデルが作成されているはずです。
ではmigrateしてDBに反映させましょう！！

```ruby:ターミナル
$rails db:migrate
```

パスワードを入れるカラムをpassword_digestという名前で登録してますが、これはの後に導入するパスワードのセキュリティ対策のためなのでパスワードを入れるカラム名は**必ずpassword_digestで登録をする必要があります**。

それではパスワードのセキュリティを強化するために最先端のハッシュ関数を導入するためにgemに 'bcrypt'を付け加えて**has_secure_password**を使えるようにしましょう。

```ruby:gem
source 'https://rubygems.org'

gem 'rails',          '5.1.6'
gem 'bcrypt',         '3.1.12'     ←追加記入
```

いつも通りbundle installを実行しましょう。

```ruby:ターミナル
$ bundle install
```
これでモデルの設定準備は完了です。


では実際にモデルの設定を行なって行きましょう。

```ruby:app/models/user.rb
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true, uniqueness: true, length: { maximum: 255 }
  has_secure_password
end
```

モデルの説明として
validatesに入れている設定として
・nameとemailが登録してなきゃイヤ！！
・『nameは５０文字以内』、『emailは２２５文字以内』にしなきゃダメだかんね！！（ツンデレ風）
と制限を加えております。

また、先ほどパスワードのセキュリティ強化で触れていたhas_secure_password
もここで宣言しており、'password', 'password_confirmation'がUserモデル内で使えるようになっています。（password == password_confirmationにならないといけないというvalidatesが設定されている）


ひとまずこれでモデルの作成は完了したので次はコントローラーの設定に行きましょう！！

~~他にも正規表現を用いたemailの設定やらcookieを用いたログイン状態の維持などあった気がしますがめんどくさいので省略(やる気があれば記事作りまs...)~~


## （Users）コントローラーの作成
次にコントローラーとユーザー登録画面のviewの生成を行いましょう（この場合newというviewとコントローラーのアクションが追加されます。）

```ruby:ターミナル
$ rails generate controller Users new
```

それではコントローラーの設定をして行きましょう。

```ruby:app/controllers/users_controller.rb
  class UsersController < ApplicationController

  def show
  	@user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      log_in @user
      redirect_to @user
    else
      render 'new'
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :password, 
     :password_confirmation)
  end
end
```

上記のコードを説明するとdef newでuser/new.htmlで入力されたユーザー情報がコントローラーに送られてきて、その情報をdef createに渡しております。
渡された情報がvalidatesで弾かれずにdef createで登録可能であればDBにユーザー情報をsaveしてユーザーのマイページに移動させる処理をしております。

またこの時private以下に設定しているストロングパラメータでname,email,password,password_confirmation以外のデータを受け付けない設定となっております。（セキュリティ対策？）


## (サインイン：ユーザー登録用)viewの作成

ここではusersコントローラーに登録情報を送るためのviewの設定をしていきたいと思います。

```ruby:app/views/users/new.html.erb
  <div>
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account" %>
    <% end %>
  </div>
```

コードとしては
form forでどのインスタンス変数に情報を送るのか（この場合は@user）を決定して、カラム名を宣言した入力ボックスを作成、submitのメソッドでコントローラーのdef createに情報を送る、最後はendでしめる。

submitする情報としては名前・メール・パスワード・パスワード確認となってます。

# ②ログイン機能の実装

##　（sessions）コントローラーの作成
ログイン、ログアウト用のコントローラーとviewの作成を行いましょう。

```ruby:ターミナル
$ rails g controller Sessions new
```

ここで生成されたsession/new.htmlをログイン用のページにします。
それでは次はroutesの設定を行いましょう。


## routesの設定
```ruby:config/routes.rb
Rails.application.routes.draw do
  get    '/signup',  to: 'users#new'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users
end
```

コードの説明として
1. get    '/---'    　login&signin用hmtlのurl設定
2. post   '/login',   to: 'sessions#create'　　　　　sessionsコントローラーにcreateのHTTPメソッドの実装
3. delete '/logout',  to: 'sessions#destroy'　　　　sessionsコントローラーにdeleteのHTTPメソッドの実装
4. resources :users    usersコントローラーにHTTPメソッドを全て実装

となっております。

それではlogin用のviewを作っていきましょう！！

##  （ログイン用）viewの作成
ログインに必要なemailとpasswordの情報を入力するviewの設定をしていきたいと思います。

```ruby:app/views/sessions/new.html.erb
<div>
    <%= form_for(:session, url: login_path) do |f| %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.submit "Log in" %>
    <% end %>
  </div>
```

サインイン用のviewと同じようにform forを用いて情報を送り、DBに登録があるemailとpasswordなのかを調べるられるようにしています。

##  (sessions)コントローラーの作成

```ruby:app/views/sessions/new.html.erb
class SessionsController < ApplicationController

  def new
  end

  def create
##送信されたメールアドレスを使って、データベースからユーザーを取り出しています##
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
#############################################################
      log_in user
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out
    redirect_to root_url
  end
end
```
コードの説明としてdef createでviewから送られてきたlogin情報（emailとpassword）をDB照合してあっていたらユーザーマイページにログイン、なければログイン画面に戻されるというメソッド。
この中で使用されているauthenticateメソッドは、モデルでhas_secure_passwordを宣言していると自動的に使用できるようになり、
入力されたパスワードを暗号化し、DBに登録されているpassword_digestと一致するか検証します。
またdef destroyではログアウトして初期ページに戻すというメソッドを実装しております。

これで基本的なログイン機能の実装は終わりです。

#  終わり

ここまでの内容はrailstutorialの５〜７章の内容をまとめたもので実際のWEBアプリではセキュリティー関連でさらにいろんな制限が必要になったり、ユーザーが使いやすいようにログイン機能の改善などする必要がかると思いますが基礎編ということで今週はここまでとしておきます。

来週にネタがなければログイン機能後編も上げれたらと思っております。~~後編作りたくない~~