RailsのRemember_meをipアドレスを使って実装

#初めに
Ruby on Railsチュートリアルでremember_meの実装があったけど普通に難しい…

それでほかにやり方ないかと思ってipアドレスを使ってremember_meを実装してみる

#実装

とりあえず前提としてuserモデルがあってそこにemailとpassword_digestカラムがある。
ipモデルがありそこにaddressとuser_idカラムがある。

sessions_controllerでログインを実装

gemに関してはbcryptを使っている


sessions_controllerはこんな感じになった

```ruby:sessions_controller.rb
class SessionsController < ApplicationController
  include SessionsHelper

  def new
  end

  def create
    user = User.find_by(email: params[:email])
    if user && user.authenticate(params[:password])
      login user 
      remember user 
      redirect_to users_path 
    else 
      render :new 
    end 
  end 

  def destroy
    logout 
    logout_ip
  end 
end
```

まずsessions_helperを使うのでインクルードしている。

login(user),remember(user)やlogout,logout_ipに関してはsessions_helperで書いてある。


それでsessions_helperはこんな感じ

```ruby:sessions_helper.rb
module SessionsHelper

    def login(user)
        session[:user_id] = user.id  
    end 

    def logout
        session[:user_id] = nil
    end 

    def logout_ip 
        ip = Ip.find_by(address: request.remote_ip)
        ip.destroy
    end 

    def remember(user)
        Ip.create(address: request.remote_ip, user_id: user.id)
    end 

    def remember_me
        unless @current_user
            address = request.remote_ip 
            ip = Ip.find_by(address: address)
            if ip 
                session[:user_id] = ip.user_id 
                @current_user = User.find session[:user_id]
            end
        end 
    end 


end

```

rememberメソッドではIpモデルを作成していてaddressにパソコンのipアドレスを入れている。

remember_meメソッドでは@current_userがなければIpモデルの中を検索してrequest.remote_ipをマッチするものがあったらログイン処理を行っている。

これでipアドレスを使って実装できた。


logoutではsession[:user_id]をnilにして、それだけではIpモデルが残っているのでログインしてしまうのでlogout_ipでIpのデータを削除している。

#ちなみに

remember_meを実装するなら多分Ruby on Rails tutorialのやり方のほうが良いんだと思う（知らないけど）

Ruby on Rails tutorialのやり方は


userモデルにremember_digestというカラムを作成してそこにSecureRandomで作ったトークンを入れてる

そんで

```ruby 
def authenticated?(remember_token)
  BCrypt::Password.new(remember_digest).is_password?(remember_token)
end 
```

っていうよくわからないメソッドで検証してるっぽい

models/user.rbの全体像はこんな感じになる

```ruby 

class User < ApplicationRecord
    has_secure_password

    attr_accessor :remember_token


    def User.digest(string)
        cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
        BCrypt::Password.create(string, cost: cost)
    end 

    def User.new_token
        SecureRandom.urlsafe_base64
    end

    def remember
      self.remember_token = User.new_token
      update_attribute(:remember_digest, User.digest(remember_token))
    end

    def authenticated?(remember_token)
      BCrypt::Password.new(remember_digest).is_password?(remember_token)
    end

    def forget 
        update_attribute(:remember_digest, nil)
    end 
end
```


正直全く読めない…

それでログイン処理を行うsessions_controllerは

```ruby 
class SessionsController < ApplicationController
  include SessionsHelper

  def new
  end

  def create
    user = User.find_by(email: params[:email])
    if user && user.authenticate(params[:password])
      login user 
      remember user 
      redirect_to users_path
    else 
      render :new 
    end 
  end 


  def destroy
    logout 
  end 

end
```

こうなっていてlogin,remember,logoutはsessions_helperに書かれている

sessions_helperはこんな感じ

```ruby

module SessionsHelper

    def login(user)
        session[:user_id] = user.id 
    end 

    def remember(user)
        user.remember 
        cookies.permanent.signed[:user_id] = user.id 
        cookies.permanent[:remember_token] = user.remember_token 
    end 

    def current_user
         if (user_id = session[:user_id])
            @current_user ||= User.find user_id 
         elsif (user_id = cookies.signed[:user_id])
            user = User.find user_id 
            if user && user.authenticated?(cookies[:remember_token])
                login user 
                @current_user = user 
            end 
        end 
    end 


    def forget(user)
        user.forget  
        cookies.delete(:user_id)
        cookies.delete(:remember_token)
    end 

    def logout
        forget(current_user)
        session[:user_id] = nil
        @current_user = nil
    end 

end
```

永続的に値を保存する際にcookiesっていうのをつかうっぽくて

cookies.permanentとすると20年間値を保持してくれるらしい

cookies.signedは署名つきのcookieらしい。いみはわからないけど安全なんだと思う。

remember_tokenはuser_idではないから直接見られても大丈夫ってことでsignedされてないのかな?



logoutメソッドでログアウト処理をしていてforgetでcookiesの削除とuserモデルのremember_digestを削除している。


メソッド同士が絡み合ってて個人的には読みづらいけどこうゆう書き方のほうがデバックしやすかったりして良いんだと思う。


おわり。