概要
本記事では、Railsのgem「devise」を用いてユーザーログイン機能を実装していきます。
ステップにそって作業を進めれば、実装出来ます。
##本記事でやること
- 基本的なサインイン・ログイン・ログアウト
- 確認メール付き認証
- emailとusernameのどちらでもログイン可能にする
#実装
##deviseの導入
下記をGemfileに記述してbundle install。
gem 'devise'
その後、以下のコマンドでdeviseの設定ファイルの生成。
$ bundle exec rails g devise:install
以下のようにメッセージが表示されるのではじめのステップではこちらを参考に作業を進めます。
===================================================================
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. If you are deploying on Heroku with Rails 3.2 only, you may want to set:
config.assets.initialize_on_precompile = false
On config/application.rb forcing your application to not access the DB
or load models when precompiling your assets.
5. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===================================================================
##各種設定
default_url_optionsの設定
下記を追記。
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
rootパスの設定
下記のようにrootパスを設定。ここは各々rootパスに設定したいものを設定。
root "home#index"
flashメッセージの設定
下記を追記。ひとまず、<body>
の直下でいいかと。
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
deviseのviewの生成
下記のコマンドでdeviseのviewの出力。
$ bundle exec rails g devise:views
##Model、migrationファイルの作成
以下のコマンドでuserモデル及びマイグレーションファイルが作成されます。
$ bundle exec rails g devise user
余談
ちなみに、今回はUserモデルで作業を進めますが、
$ bundle exec rails g devise member
のようにモデル名は変更出来ます。このときはdeviseのメソッド名もモデル名に合わせて変わる点には注意が必要です。
例えば、user_signed_in?
メソッドはmember_signed_in?
となります。
認証に必要なカラムを追加する
デフォルトでは以下のファイルの22行目あたりがコメントアウトされているので以下の項目のコメントアウトをすべてはずす。メール認証のためのカラムで今回は使用します。
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
そして、migrate。
$ bundle exec rake db:migrate
deviseの各種設定
ログアウトボタンを設置する
ログアウトボタンをとりあえず作っておきましょう。(ログインのテストをするときにないと困るので)
先ほどのフラッシュメッセージの直下に下記を記述しておきましょう。
deviseがログアウトの処理は準備してくれているので、それを使用するだけです。
パスについてはrake routes
で確認出来ます。
また、user_signed_in?
というユーザーがログインしているかどうかを判別するメソッドを使用することで、ログイン時のみログアウトボタンを出力します。
<% if user_signed_in? %>
<p><%= link_to "ログアウト", destroy_user_session_path, method: :delete %></p>
<% end %>
ログインしていないときにログイン画面にリダイレクトをかける
下記を追加しましょう。:authenticate_user!
によってログインしていないときログイン画面にリダイレクトをかけることが出来るようになります。ちなみに、ここではapplication_controller.rb内で使用しましたが、各コントロラー単位でも使用出来るので必要があれば書き換えて下さい。
before_action :authenticate_user!
メールに必要な設定を行う
メール認証のモジュールを読み込む
user.rbの4行目付近に:confirmable
を読み込む。
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
追記 2019.01.23
devise 4.5.0から :trackable
はデフォルトではなくなったようです。:trackable
を追加する必要はありません。
SMTPサーバーの設定
メールを実際に送信するためには、SMTPサーバーを設定しなければなりません。
ここでは、gmailを用いてメールの送信を行います。
以降でgmailを使用するのでメールアドレスとパスワードが必要になります
2段階認証をかけている場合は解除しなければ、使用できません。
config.action_mailer.smtp_settings = {
:enable_starttls_auto => true,
:address => "smtp.gmail.com",
:port => 587,
:domain => 'smtp.gmail.com',
:user_name => "xxxxxxx@gmail.com", #gmailアドレス
:password => "xxxxxxxxxxx", #gmailパスワード
:authentication => 'login',
}
github等にアップロードしてソースコードを公開される場合は注意が必要です。自身のgmail
のアドレスとパスワードを公開することになるので勝手に使用されるおそれがあります。
その場合は、gemの「config」を使うことをおすすめします。
下記の記事の中で使用法について記述してあるので参考までに。
rails 一度gitHubにあげたID・パスワード等を履歴から消し、環境ごとに管理する
ここまでの確認をする
ここまで出来れば、メール認証つきのログイン認証の出来上がりです。
実際にサインイン・ログイン・ログアウトの一連の処理を試してみてください。
まだ、ここまででは通常のemailでの認証です。
SMTPにエラーが発生するときは「安全性の低いアプリのアクセス」をonにしていない可能性があります。
メールが届くと思うのでそれに沿って設定して下さい。
一応、下記のURLから設定出来るのでこちらからでも設定変更可能です。
https://www.google.com/settings/security/lesssecureapps
それでは、次からemailとusernameのどちらでもログイン可能にしていきます。
userテーブルにusernameカラムを追加する
migrationファイルの作成。
$ bundle exec rails g migration AddUserNameToUsers username:string:uniq
その後、
$ bundle exec rake db:migrate
ストロングパラメーターの設定
deviseのコントローラーはgemの中にあるため、直接変更出来ません。そのため、新たにカラムを追加したときは別途ストロングパラメーターの設定をする必要があります。
今回は:usernameをカラムとして追加し、さらにこの後に:loginをフォームの中で使用していくのでこちらの設定が必要となります。
# 以下を追加
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :username, :email, :password, :remember_me) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password) }
end
追記 2017.2.27
Rails5の変更に伴いdeviseのバージョンがあがりストロングパラメータの追加の仕方が変わっている可能性があります。devise4をお使いの方は以下の記事を参照して下さい。
deviseでストロングパラメータを追加する (Rails5を受けての変更点)
ログイン用のパラメーターの設定
emailとusernameのどちらでもログイン出来るようにするためのパラメーター:loginを設定します。
loginパラメーターのアクセサを設定
カラムに存在しないフィールドをフォーム内で設定すると通常では利用することが出来ません。そのため、attr_accessor
を定義することで利用可能にします。
attr_accessor :login
ログイン認証用のキーをloginに設定
先ほど、confirmableを追記したところにさらに:authentication_keys => [:login]
を追加します。
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, :confirmable, :authentication_keys => [:login]
ログイン認証の条件をオーバーライド
以下を追記します。ここではdeviseのログイン認証のメソッドをオーバーライド(上書き)することで新たなログイン条件に設定しています。
ここでは、usernameとemailのどちらでもログイン出来るように条件を設定しています。
# 以下を追加
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["username = :value OR lower(email) = lower(:value)", { :value => login }]).first
else
where(conditions).first
end
end
追記 2019.01.23
@takotakot さんより
lower(email) = lower(:value)" のところ、パフォーマンスを考えると気になります。
- DB に保存するときに lower して保存する
- lower してあるカラムを追加する
- 何も与えないで比較させる
等工夫した方が良さそうです。full scan になってしまうので。
usernameのバリデーションをかける
usernameのバリデーションがない状態なので最低限のバリデーションはつけておきましょう。
下記を追加することでusernameがuniqueであることと、文字数が4から20文字であることを設定出来ます。(case_sensitive: :falssは大文字小文字の区別をしないということ)
こちらは各自お好みの設定をすればオーケーです。
# 以下を追加
validates :username,
uniqueness: { case_sensitive: :false },
length: { minimum: 4, maximum: 20 }
半角英数字のバリデーションをつけるのも良さそうですね。
format: { with: /\A[a-z0-9]+\z/, message: "ユーザー名は半角英数字です"}
追記 2019.01.25
@takotakot さんより
attr_accessor :login
を予期するということは、フォーム側で、name や email ではなく、login として情報送信を試みるはずなのですが、その部分の説明が抜け落ちています。
参考にされている記事の「Viewファイルの書き換え」というところに書いてあります。
もし、View周りでお困りの場合はこちらもご参照ください。Rails4.1でdeviseのログインIDをemailとusernameのどちらでも可能にする
参考
Rails4.1でdeviseのログインIDをemailとusernameのどちらでも可能にする
後半部分はこちらを参考にしています。