前 基礎Ruby on Rails Chapter7 バリデーションと国際化
次 基礎Ruby on Rails Chapter8 アクション・コールバック/マイアカウントページの作成
単数リソース
単数リソースとは
- member等と比較して、単数リソースとは1つしか存在しないもの。
- 例えば「セッション」「自分のアカウント」「自分のパスワード」など、自分自身の情報。
単数リソースのルーティング
- ルーティングを設定するには、routes.rbの中で、resourcesメソッドではなく、単数形のresourceを使う。
resource :account
- 集合である、indexアクションはない。
- idパラメータもない。
- membersテーブルの自分自身のレコードのみを使う。
|アクション|パス|HTTPメソッド|パスを示すシンボル|パスを返すメソッド|
|:--|:--|:--|:--|:--|:--|
|show|/account|GET|:account|account_path|
|new|/account/new|GET|:new_account|new_account_path|
|edit|/account/edit|GET|:edit_account|edit_account_path|
|create|/account|POST|:account|account_path|
|update|/account|PATCH|:account|account_path|
|destroy|/account|DELETE|:account|account_path|
セッションを使ったログイン機能
セッションとは
Railsのセッション
- Railsはセッションデータを複合化してクッキーに保存するが、暗号化はしないので、ユーザーがデータを解読できる。
- ただし、改ざんした場合はエラーになる。
セッションデータへのアクセス
- 「session[:データ名]=値」でセッションに値を入れる。
- 「session[:データ名]」でセッションから値を取り出す。
- 「session.delete(:データ名)」でセッションのデータを消す。
- 「cookie[:データ名]=値」でクッキーに値を入れる。
- 「cookie[:データ名]」でクッキーから値を取り出す。
- cookieの有効期間の設定
cookies[:name] = { value: "sato", expires: 30.days.from_now }
- セッションデータの保存期間
- 自動ログインには、cookieの代わりにcookie.signedを使う。
cookies.signed[:member_id] = member.id
パスワードの保存
ハッシュ値とbcrypt
- パスワードをそのまま保存するのではなく、ハッシュ値を保存する。通常Ruby on Railsでは、bcryptを使う。
- Gemパッケージ、bcrypt-rubyを組み込む必要がある。以下のようにコメントを外す。
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
- bundle installを実行する。
$ bundle install
ハッシュ値を保存するカラムの追加
- パスワードのハッシュ値を保存するカラムを追加する。
- マイグレーションスクリプトを作成する。
$ bin/rails g migration AlterMembers1
- マイグレーションスクリプトに以下のように、テーブル名、追加するカラム名、型名を追加する。
class AlterMembers1 < ActiveRecord::Migration[5.2]
def change
add_column :members, :password_digest, :string
end
end
- マイグレーションを実行する。
$ bin/rails db:migrate
クラスメソッドhas_secure_password
- モデルに、クラスメソッドhas_secure_passwordを追加する。
class Member < ApplicationRecord
has_secure_password
-
これにより、Memberクラスにpasswordおよびpassword_confirmationという名前の2つの属性が定義される。
-
password属性に対する次ようなバリデーションが設定される。
- レコードの挿入時には、パスワードが空ではいけない。
- パスワードの長さは72文字以下。
- パスワードと確認用パスワードは一致すること。
-
これらのバリデーションを無効にした場合は、以下のようにvalidationオプションをfalseに設定する。
has_secure_password validation: false
- 開発用のシードデータを修正する。
(省略)
password: "asagao!",
password_confirmation: "asagao!"
)
end
- データベースをリセットする。
$ bin/rails db:rebuild
ユーザーの認証
authenticateメソッド
-
member = Member.first
で1行取得する。 -
member.authenticate("detarame")
で、パスワードが正しいか調べる。間違っているので、false。 -
member.authenticate("asagao!")
は正しいので、1行返ってくる。
irb(main):003:0> member = Member.first
Member Load (0.3ms) SELECT "members".* FROM "members" ORDER BY "members"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Member id: 1, number: 10, name: "Taro", full_name: "佐藤 太郎", email: "Taro@example.com", birthday: "1981-12-01", sex: 1, administrator: true, created_at: "2018-09-27 13:16:20", updated_at: "2018-09-27 13:16:20", password_digest: "$2a$10$ShhumBcGJozj0uAYD7WU1.1Ul0bj1YS3l6rLuwLygJV...">
irb(main):004:0> member.authenticate("detarame")
=> false
irb(main):005:0> member.authenticate("asagao!")
=> #<Member id: 1, number: 10, name: "Taro", full_name: "佐藤 太郎", email: "Taro@example.com", birthday: "1981-12-01", sex: 1, administrator: true, created_at: "2018-09-27 13:16:20", updated_at: "2018-09-27 13:16:20", password_digest: "$2a$10$ShhumBcGJozj0uAYD7WU1.1Ul0bj1YS3l6rLuwLygJV...">
irb(main):006:0>
SessionsControllerの作成
- Sessionsコントローラに、create、destroyアクションを追加。
(省略)
resource :session, only: [:create, :destroy]
end
- sessions_controller.rbの作成。
$ bin/rails g controller sessions
- SessionsControllerの実装
class SessionsController < ApplicationController
def create
member = Member.find_by(name: params[:name])
if member&.authenticate(params[:password])
session[:member_id] = member.id
else
flash.alert = "名前とパスワードが一致しません"
end
redirect_to :root
end
def destroy
session.delete(:member_id)
redirect_to :root
end
end
current_memberメソッドの定義
- 現在ログインしている情報を取得するメソッドを作成。
- helper_methodで登録することにより、テンプレートの中で使うことができる。
class ApplicationController < ActionController::Base
private def current_member
Member.find_by(id: session[:member_id]) if session[:member_id]
end
helper_method :current_member
end
ログインフォームの実装
- 上部に、
flash.alert
の表示欄を追加。 - 次に、全体をform_tagで囲むように修正。
- ユーザー名、パスワードにname属性を追加。
<h2>ログイン</h2>
<% if flash.alert %><p class="alert"><%= flash.alert %></p><% end %>
<%= form_tag :session, id: "login-form" do %>
<div>
<label>ユーザー名:</label>
<input type="text" name="name">
</div>
<div>
<label>パスワード:</label>
<input type="password" name="password">
</div>
<div>
<input type="submit" value="ログイン">
</div>
</form>
<% end %>
- ログインしている場合は、サイドバー上部にログインフォームを表示しないよう
unless current_member
を追加する。
<%= render "shared/login_form" unless current_member %>
<h2>最新ニュース</h2>
(省略)
- ログイン成功後
- ログイン失敗