はじめに
2020/04/04
Rails6版はこちら
[Rails] deviseの使い方(rails6版)
Rails5で再検証版です。
以下のRails4のときのやつを現状(2016/11/12, 13)のバージョンで確認して、一部バージョン上がってうまく動かないところを修正したりした感じです。
[Rails] deviseの使い方(rails4版)
【前回】
rails : 4.2.0
ruby : 2.2.0
devise: 3.4.1
↓
【今回】
rails : 5.0.0.1
ruby : 2.3.1
devise: 4.2.0
あと、上の記事で最後に「全部まとめきれてないので、忘れないうちにちょっとずつ追加していこうと思います。」とか書いていながら全く追加することはなかったので、追加しようと思ってたものをちょっとだけ思い出して追加したバージョンです。
deviseとは
ユーザー登録して、送られてきたメールのリンクをクリックして本登録して、ログインして、パスワード忘れたら再設定して、何回もログインミスったらアカウントロックして…などといった認証系アプリに必要な機能を簡単に追加できる便利なgemです。
deviseの導入
1. gemのインストール
1. プロジェクトの作成
新しいプロジェクトを作ります。
$ rails new devise_rails5
$ cd devise_rails5
2. Gemfileの編集とインストール
以下ファイルにdevise
とomniauth-twitter
を追加します。
source 'https://rubygems.org'
(省略)...
# Devise
gem 'devise'
gem 'omniauth-twitter'
gemをインストール。
$ bundle install
2. deviseの設定
devise関連ファイルを追加。
すると以下のような英文が表示されます。1から4まで順番に見ていきます。
$ rails g devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Some setup you must do manually if you haven't yet:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===============================================================================
1. デフォルトURLの指定
英文の例に書いてあったconfig.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
を以下のファイルに追加しました。
config.action_mailer.default_url_options = { host: 'localhost:3000' }
でもOKです。
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
(省略)...
# mailer setting
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end
2. root_urlの指定
1番で指定した*http://localhost:3000/*にアクセスした際に表示されるページを指定します。
現状ページは1つも作っていないため、先に追加します。
Pagesコントローラーと、indexページとshowページを追加してみます。
$ rails g controller Pages index show
routes.rbに以下を指定します。
Rails.application.routes.draw do
root 'pages#index'
get 'pages/show'
(省略)...
end
3. flashメッセージの設定
ログインした時などに上の方に「ログインしました」みたいなメッセージが出るようにします。
以下のファイルの<body>
タグのすぐ下に指定されたタグを挿入します。
<!DOCTYPE html>
<html>
<head>
<title>DeviseRails5</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body>
</html>
4. DeviseのViewを生成
Deviseの導入で追加されるViewは、以下のコマンドを実行しなければデザインを変更できないので、デザインをカスタマイズするためにも実行します。
$ rails g devise:views
すると以下の様なファイルが生成されます。
app/views/devise/shared/_links.html.erb (リンク用パーシャル)
app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
app/views/devise/passwords/edit.html.erb (パスワード変更画面)
app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
app/views/devise/registrations/new.html.erb (ユーザー登録画面)
app/views/devise/sessions/new.html.erb (ログイン画面)
app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
app/views/devise/mailer/password_change.html.erb (メール用パスワード変更完了文)
app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)
3. Userモデルの設定
1. Userモデルの作成
以下を実行。
$ rails g devise User
マイグレーションファイルができます。
デフォルトではこんな感じになってます。
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
Userモデルはこんな感じになっています。
デフォルトではdatabase_authenticatable
、registerable
、recoverable
、rememberable
、trackable
、validatable
が使えるようになっています。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
2. deviseモジュール概要
各モジュールについて以下に紹介します。
機能 | 概要 |
---|---|
database_authenticatable | サインイン時にユーザーの正当性を検証するためにパスワードを暗号化してDBに登録します。認証方法としてはPOSTリクエストかHTTP Basic認証が使えます。 |
registerable | 登録処理を通してユーザーをサインアップします。また、ユーザーに自身のアカウントを編集したり削除することを許可します。 |
recoverable | パスワードをリセットし、それを通知します。 |
rememberable | 保存されたcookieから、ユーザーを記憶するためのトークンを生成・削除します。 |
trackable | サインイン回数や、サインイン時間、IPアドレスを記録します。 |
validatable | Emailやパスワードのバリデーションを提供します。独自に定義したバリデーションを追加することもできます。 |
confirmable | メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を提供します。また、サインイン中にアカウントが認証済みかどうかを検証します。 |
lockable | 一定回数サインインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。 |
timeoutable | 一定時間活動していないアカウントのセッションを破棄します。 |
omniauthable | intridea/omniauthをサポートします。TwitterやFacebookなどの認証を追加したい場合はこれを使用します。 |
3. Userモデルの編集
今回はデフォルトではないものも触ってみたいと思うので全部入れてみます。
Twitter認証を使うのでTwitterを指定しています。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable, :omniauthable, omniauth_providers: [:twitter]
end
4. マイグレーションファイルの編集
上に合わせて使用モジュールに対応する部分のコメントアウトを外します。今回は全部使うので全部外します。
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
t.string :unlock_token # Only if unlock strategy is :email or :both
t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
add_index :users, :confirmation_token, unique: true
add_index :users, :unlock_token, unique: true
end
end
5. omniauth-twitter用カラムの追加
ついでにomniauth-twitterで使うprovider
とuid
、それとTwitter認証の場合はアカウント名を保存しておきたいのでusername
もUserテーブルに追加します。
$ rails g migration add_columns_to_users provider uid username
以下のようなマイグレーションファイルができます。
class AddColumnsToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
add_column :users, :username, :string
end
end
ここまで出来たら以下を実行します。
$ rake db:migrate
##4. viewの編集
以下のファイルを編集して、ページ上部にメニューが出るようにします。
user_signed_in?
はdeviseのHelperメソッドです。
ログインしているかしてないかで上部のメニューの表示が変わるようになります。
current_user
で現在サインインしているユーザーの情報を取得できます。
<!DOCTYPE html>
<html>
<head>
<title>DeviseRails5</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<header>
<nav>
<% if user_signed_in? %>
<strong><%= link_to current_user.username, pages_show_path %></strong>
<%= link_to 'プロフィール変更', edit_user_registration_path %>
<%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to 'サインアップ', new_user_registration_path %>
<%= link_to 'ログイン', new_user_session_path %>
<% end %>
</nav>
</header>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body>
</html>
以下の2ページも修正。
indexの方がトップページ、showの方がログインしているユーザー用のページになる予定です。
<h1>ようこそ</h1>
<p>トップページです。</p>
<h1>こんにちは、<%= current_user.username %>さん</h1>
<p>ユーザー用ページです。</p>
サーバーを立ち上げて、「サインアップ」を押下すると、app/models/user.rb
でomniauthable
を設定しているのに何の設定もしていないので現状ではエラーになります。
omniauthable
を設定していない場合はサインアップページが表示されます。
Twitterで認証する
1. 設定
1. Twitter Developerの登録
Twitter Developerにアクセスし、Create New Appをクリックし(ツイッターにログインしてないとボタンが出ません)、情報を適当の入力にします。
作成したプロジェクトを開き、Settingsタブの下の方のAllow this application to be used to Sign in with Twitterにチェックが入っていることを確認します。
2. 設定ファイルの編集
Keys and Access Tokensタブを開き、**Consumer Key (API Key)とConsumer Secret (API Secret)**を以下の該当箇所にコピーして貼り付けます。
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
(省略)...
config.omniauth :twitter, "API Key", "API Secret"
end
2. 動作確認
サーバーを立ち上げてサインアップページの下の方にあるSign in with Twitterというリンクをクリックします。
すると以下の様な画面が開くので連携アプリを認証をクリックします。
3. コールバック用コントローラーの作成
Twitter認証後適切に画面が遷移するように以下を実行してコントローラーを作ります。
$ rails g controller omniauth_callbacks
作成したコントローラーの中身を以下のように修正します。
継承するのがDevise::OmniauthCallbacksControllerになっていることに注意です。
omniauth. authという環境変数に認証に関する情報が入っています。
その情報を使ってユーザーが登録されているかを検証し、登録してる場合はログイン、登録されてない場合は登録用ページに遷移します。
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def twitter
@user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))
if @user.persisted?
sign_in_and_redirect @user
else
session["devise.user_attributes"] = @user.attributes
redirect_to new_user_registration_url
end
end
end
ちなみにomniauth.authの中にはこんなものが入ってます。(一部の値は適当に変えてあります。)
{"provider"=>"twitter", "uid"=>"0123456789", "info"=>{"nickname"=>"manycicadas", "name"=>"芭蕉", "location"=>"関東", "image"=>"http://pbs.twimg.com/profile_images/483964583371997185/2ZqzhzKV_normal.png", "description"=>"JavaEE/Ruby(Rails)/HTML/CSS/JavaScript/Raspberry Pi などなどが好き。", "urls"=>{"Website"=>nil, "Twitter"=>"https://twitter.com/manycicadas"}}, "credentials"=>{"token"=>"0123456789-hQywfs78sQ9NnwpSkwiejf2Ij74sut7hKjEsF9", "secret"=>"sijIYUsiJslOhiwkYukshKKJG6skWbhbXCYji3sabla3O1"}}
Userモデルにself.from_omniauthとself.new_with_sessionを作ります。
self.from_omniauthではuidとproviderで検索してあったらそれを、無かったらレコードを作ります。
self.new_with_sessionについては、もしこのメソッドを追加しておかなければ、Twitter認証後サインアップページで登録を行っても、認証情報として取ってきたuidやproviderなどが登録されません。それらが登録されないのでTwitterで認証しても登録されてないユーザーとして毎回サインアップページに飛ばされます。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable, :omniauthable, omniauth_providers: [:twitter]
def self.from_omniauth(auth)
find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.username = auth["info"]["nickname"]
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"]) do |user|
user.attributes = params
end
else
super
end
end
end
以下ファイルを編集して、コールバック用のコントローラーとしてさっき作ったコントローラーが呼ばれるようにします。これを書かないとdevise側のコントローラーが呼ばれます。
Rails.application.routes.draw do
devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" }
root 'pages#index'
get 'pages/show'
(省略)...
end
これでTwitter認証ができるようになりました。
初回、Twitter認証を行うと、サインアップページに飛ばされ、そこでメールアドレスやパスワードを入力して登録するとユーザー情報が登録されます。
今回はcomfirmable機能を入れているので、登録したら確認メッセージを送ったとのメッセージが出て、そのままログインすることはできません。
この機能を入れてなかった場合、登録すると即ログインします。
アカウント登録確認メールを送る
1. comfirmable概要
多くの登録系サイトで採用されている、登録すると仮登録状態になり、届いたメールのリンクをクリックするとログイン可能になるという仕組みを追加できるのがcomfirmableです。
今回はGmailのアカウントを使って実際にメールが届くように設定します。
2. ログを見る
サインアップ画面からEmailやパスワードを入力して登録すると、ログに以下のような内容が出力されます。
現状何も設定してないので、送信元アドレスなどがデフォルトであることが分かります。
Devise::Mailer#confirmation_instructions: processed outbound mail in 715.7ms
Sent mail to test@example.com (51.1ms)
Date: Sat, 12 Nov 2016 22:26:18 +0900
From: please-change-me-at-config-initializers-devise@example.com
Reply-To: please-change-me-at-config-initializers-devise@example.com
To: test@example.com
Message-ID: <5827187aa0893_104ff3fcd39297f34991c@YukihonoiMac-8.local.mail>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Welcome test@example.com!</p>
<p>You can confirm your account email through the link below:</p>
<p><a href="http://localhost:3000/users/confirmation?confirmation_token=ZaPGWtxbTzo_3eJxvzm9">Confirm my account</a></p>
3. メールが実際に届くようにする
1. 設定ファイルの編集
今回はgmailを使うのでメールアドレスにはgmailを設定します。
Devise.setup do |config|
(省略)...
# mail setting
config.mailer_sender = "メールアドレス"
end
gmailの場合はGメールアドレスとGメールパスワードの部分を自分のアカウントのものに変更します。
Rails.application.configure do
# default url
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# mail setting
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:user_name => "Gメールアドレス",
:password => "Gメールパスワード",
:authentication => :plain,
:enable_starttls_auto => true
}
end
2. Gmailの設定
1. とりあえず試したい場合
デフォルトでは安全性の低いアプリケーションからGmailへのアクセスが制限されています。
よってその制限を解除することで動くようにできます。
ただ、安全性の低いアプリからのアクセスを許可するということはセキュリティ的にはいまいちなのであくまで動作確認ように利用することをおすすめします。
安全性の低いアプリがアカウントにアクセスするのを許可するの安全性の低いアプリを許可というリンクをクリックして、以下のように設定します。
2. 2段階認証がオンの場合
2段階認証をオンにしている場合、1の設定はできないはずなのでこちらをやってください。
また、1のセキュリティ的にイマイチな方をやりたくない方もこの際2段階認証をオンにしてこの設定をやってください。
アカウントへのアクセスとセキュリティ設定の管理を開きます。
2段階認証がオフの場合は2段階認証プロセスから2段階認証の設定をしてください。(2 段階認証プロセスを有効にする)
2段階認証設定を行っている場合はアプリパスワードを開きます。
端末を選択とアプリを選択で適切なものを選びます。
私は端末を選択ではMac
を、アプリを選択でその他(名前を入力)
を選択して登録しました。
生成されたパスワードを、1の設定の:password => "Gメールパスワード",
のGメールのパスワードの部分に設定します。
3. 届いたメールを確認
これでアプリ側からサインアップすると、以下の様なメールが届くようになります。Confirm my accountをクリックするログイン画面からログインが可能になります。
メールで送られる文章は以下のファイルを編集することで可能です。
app/views/devise/confirmation_instructions.html.erb
アカウントをロックする
1. lockable概要
アカウントの認証を一定回数間違うと、アカウントをロックするようにする機能です。
2. 設定
1. 設定ファイルの編集
以下ファイルを修正して、アカウントの認証を5回失敗します。
Devise.setup do |config|
(省略)...
# lock sessings
config.unlock_strategy = :email
config.maximum_attempts = 4
end
メールの中身はapp/views/devise/mailer/unlock_instructions.rb
ファイルを修正すれば変わります。
2. 設定値について
- lock_strategy(ロック方法)
属性 | 説明 |
---|---|
:failed_attempts | 失敗回数によってロック。 |
:none | ロックしない。 |
- unlock_strategy(ロック解除方法)
属性 | 説明 |
---|---|
:time | 指定時間でロックを解除する。 |
メールでロックを解除する。 | |
:both | :timeと:emailの両方。 |
:none | 解除させない。 |
:noneを指定した場合、ユーザーのレコードのlocked_at
カラムをnil
にアップデートしたらロックが解除できます。
- unlock_in(ロック解除時間)
2時間で解除するならconfig.unlock_in = 2.hours
といった具合に指定。
- unlock_keys
アカウントをロックまたは解除するときに使用するキーを定義するらしいです。
config.unlock_keys = [:username]
という感じで指定できます。
- maximum_attempts(失敗可能回数)
アカウントの認証を失敗して良い回数を指定します。
config.maximum_attempts = 4
と指定した場合、4回目までは失敗しても大丈夫ですが、5回目を失敗した時点でアカウントがロックされます。
セッションをタイムアウトする
1. timeoutable概要
一定時間活動がない場合にセッションをタイムアウトさせるのがtimeoutableです。
2. 設定
以下ファイルを修正するとタイムアウトまでの時間を指定できます。
以下の場合だと3分後にセッションがタイムアウトします。
デフォルトは30分だそうです。
Devise.setup do |config|
(省略)...
# timeout setting
config.timeout_in = 3.minutes
end
その他の設定
1. ログイン後のページを変更する
ログインすると、デフォルトではroot_urlに飛ばされます。
これをapp/views/pages/show.html.erb
になるように修正します。
after_sign_in_path_forメソッドを追加します。ここにログイン後に遷移したいページを指定します。
あとsign_in_requiredも追加します。showページはログインしているユーザーだけにアクセスさせ、ログインしてない場合はログインページに遷移させます。
class ApplicationController < ActionController::Base
(省略)...
def after_sign_in_path_for(resource)
pages_show_path
end
private
def sign_in_required
redirect_to new_user_session_url unless user_signed_in?
end
end
Pagesコントローラーにbefore_action
を追加します。
class PagesController < ApplicationController
before_action :sign_in_required, only: [:show]
(省略)...
end
これでログイン後showページに遷移するようになります。
2. サインアップする際に登録するパラメーターを増やす
ユーザーを登録するときにデフォルトではEmailとパスワードだけですが、
ユーザー名も登録させたい、などの場合があると思います。
まずはサインアップページにユーザー名を入力するエリアを追加します。
ユーザーのプロフィール変更ページ(views/devise/registrations/edit.html.erb
)にも同様に追加しときます。
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<!-- 省略 -->
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
<!-- 省略 -->
<% end %>
<%= render "devise/shared/links" %>
次にApplicationControllerに以下を追加します。
テンプレートを変えて、ユーザー名を入力するようにしただけでは実際に登録されないからです。
詳しくはstrong_parameters
について調べてください。
簡単に言えばよく分からんパラメーターは渡せないようになってるので渡せるようにします。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
# 省略
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
end
最後に
作ったプロジェクト全体は以下です。
cigalecigales/devise_rails5