LoginSignup
6
9

More than 5 years have passed since last update.

SinatraでDeviseの中身を実装・解説/ハッシュ解説

Last updated at Posted at 2018-06-29

前説

Railsで用いられるユーザー管理gemであるDeviseって便利だなあ。
環境構築、データベース接続設定はこちら

今回はSinatraで一から実装

環境

  • Sinatra
  • MySQL

すべきこと

  • ログイン認証
    • パスワードをひた隠し
    • ログイン・ログアウト
    • 新規アカウント作成
  • ユーザーに関するデータベース作成
  • アソシエーション

必要なページとHTTPメソッド

  • GET log_in
  • GET log_out
  • GET sign_up

  • POST session(log in)

  • DELETE session(log out)

  • POST new(sign up)

必要なカラム

  • id
  • name
  • email
  • password
  • password_confirmation
  • updated_date
  • created_date

Databse作成

mysql
mysql>CREATE [DATABASE_NAME];
mysql>USE [DATABASE_NAME];

mysql>CREATE TABLE [TABLE_NAME] (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  email VARCHAR(255) NOT NULL,
  url VARCHAR(255) NOT NULL,
  password VARCHAR(255) NOT NULL,
  password_confirmation VARCHAR(255) NOT NULL,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL,
  PRIMARY KEY(id)
);

main.rb

中身のプログラムを細かく書いていきます

main.rb
#メソッドのところだけ説明します!!
#sign_upページを参照
get '/sign_up' do
  #sessionにuser_idが保存されている -> ログインできてるやん
  session[:user_id] ||= nil
  if session[:user_id]
    redirect '/log_out'
  end

  erb :sign_up
end

#user作成を行う
post '/new' do
  #登録時のパスワード確認がうまくいきませんでした -> もう一回登録して、どうぞ
  if params[:password] != params[:confirm_password]
    redirect "/sign_up"
  end

  #Userクラスより新規データを追加、インスタンス作成
  user = User.new(email: params[:email], name: params[:name])
  #パスワードを暗号化
  user.encrypt_password(params[:password])
  #保存できた!
  if user.save!
    #ログイン完了 -> mainページへ
    session[:user_id] = user.id
    redirect "/"
  else
    redirect "/sign_up"
  end
end

#ログインページ
get '/log_in' do
  #sessionにuser_idが保存されている -> ログインできてるやん
  if session[:user_id]
    redirect '/log_out'
  end
  erb :log_in
end

#ログイン
post '/session' do
  #ログイン、できてるよー
  if session[:user_id]
    redirect "/"
  end

  #ユーザー認証
  user = User.authenticate(params[:email], params[:password])
  #oK! -> 現在のuserにuser.idが当てはまる
  if user
    session[:user_id] = user.id
    user_id = session[:user_id]
    redirect '/'
  #もう一度ログインして、どうぞ
  else
    redirect "/log_in"
  end
end


#ログアウトページの参照
get '/log_out' do
  unless session[:user_id]
    redirect '/log_in'
  end 

  erb :log_out
end

#ログアウト
delete '/session' do
  session[:user_id] = nil
  user_id = session[:user_id]
  redirect '/log_in'
end

忘れがちなところ(省いています・・・)

main.rb
#modelsディレクトリの中のuser.rbから呼び出し
require_relative 'models/user'

-----

#sessionを使いたいのならば、以下を書いてください
class Server < Sinatra::Base
  enable :sessions
  set :session_secret, "My session secret"
end

user.rb

user.rb
Bundler.require

class User <ActiveRecord::Base
  #データベースとの連携をmain.rbと同様に
  ActiveRecord::Base.configurations = YAML.load_file('database.yml')
  ActiveRecord::Base.establish_connection(:development)

  #name,email,passwordは必須かつ一意
  validates_presence_of :name
  validates_uniqueness_of :name
  validates_presence_of :email
  validates_uniqueness_of :email
  validates :password_hash, confirmation: true
  validates :password_hash, presence: true
  validates :password_salt, presence: true

 #passwordをハッシュ化
 def encrypt_password(password)
    if password.present?
      #詳細は次の章
      self.password_salt = BCrypt::Engine.generate_salt
      self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
    end 
  end


  #emailとpasswordで認証メソッド
  def self.authenticate(email, password)
    #現ログイン時userのemailを取得
    user = self.where(email: email).first
    #認証 (ハッシュ化したパスワードで認証 詳細は次の章)
    if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
      user
    else
      nil 
    end 
  end 

end

ハッシュ化認証

Papper(胡椒)とSalt(塩)というフレーズがありますが、あらかじめ打ち込んだ平文パスワードに設定されている文字列(=暗号パターン)を胡椒とすると、ランダムで生成されるハッシュを塩と例えています。平文を塩胡椒でまぶしてしまおうというわけです。
今回は塩でまぶしてやります。

つまり・・・

user.rb

#passwordの暗号化
 def encrypt_password(password)
    if password.present?
      #パスワードにおけるランダム文字列生成-saltと定義
      self.password_salt = BCrypt::Engine.generate_salt
       #ランダム生成した暗号,パスワードをハッシュとして
      self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
    end 
  end


#emailとpasswordで認証メソッド
  def self.authenticate(email, password)
    #現ログイン時userのemailを取得
    user = self.where(email: email).first

    #ログインしたいuserのemailを取得できるか
    #打ち込まれたpasswordがログインしたいユーザーのハッシュと等しいか調査
    if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
      user
    else
      nil 
    end 
  end 

最後にView

今回はSignupページだけ載せておきます。

sign_up.erb
<form action="/users" method="POST">
    <div>
    name: <input type="text" name="name" value="">
    </div>
    <div>
    email: <input type="text" name="email" value="">
    </div>
    <div>
    password: <input type="password" name="password" value="">
    </div>
    <div>
    password_confirm: <input type="password" name="confirm_password" value="">
    </div>
    <div>
    <input type="submit" value="登録">
    </div>
</form>

結果

スクリーンショット 2018-06-29 22.50.39.png

スクリーンショット 2018-06-29 22.51.38.png

参考文献

ハッシュ関連
- https://www.rubydoc.info/github/codahale/bcrypt-ruby/BCrypt%2FEngine.hash_secret
- https://qiita.com/kenjiszk/items/d38e398bc120cf4bbd1c
ヒントになりました
- https://dyama.org/2015/03/sinatra-mongoid%E3%81%A7%E7%B0%A1%E6%98%93%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86/

6
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9