前説
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
- password
- password_confirmation
- updated_date
- created_date
Databse作成
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
中身のプログラムを細かく書いていきます
#メソッドのところだけ説明します!!
#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
忘れがちなところ(省いています・・・)
#modelsディレクトリの中のuser.rbから呼び出し
require_relative 'models/user'
-----
#sessionを使いたいのならば、以下を書いてください
class Server < Sinatra::Base
enable :sessions
set :session_secret, "My session secret"
end
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(塩)というフレーズがありますが、あらかじめ打ち込んだ平文パスワードに設定されている文字列(=暗号パターン)を胡椒とすると、ランダムで生成されるハッシュを塩と例えています。平文を塩胡椒でまぶしてしまおうというわけです。
今回は塩でまぶしてやります。
つまり・・・
#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ページだけ載せておきます。
<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>
結果
↓
参考文献
ハッシュ関連
- 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/