#209 Introducing Devise
- Download:
- source codeProject Files in Zip (156 KB)
- mp4Full Size H.264 Video (20.9 MB)
- m4vSmaller H.264 Video (12.6 MB)
- webmFull Size VP8 Video (30.8 MB)
- ogvFull Size Theora Video (28.7 MB)
今までのエピソードでいくつかの認証の手法について解説してきましたが、今回もひとつ紹介します。今回紹介するのはdeviseという名前で、最近特に人気が出てきています。Deviseは、Rackベースの認証ツールであるWardenの上で、それを隠した状態で動作します。今回のエピソードを理解するためには、直接deviseを操作するので、Wardenに関する知識は必要ありません。
Deviseはフルスタックを使って認証を扱います。エピソード160 [動画を見る, 読む]で紹介したAuthlogicを触ったことがあれば、それがモデル層だけでなりたっていることをご存知でしょう。一方deviseはRails Engineとして動作して、コントローラとビューも持っています。Deviseはモジュラー方式で現在は11のモジュールから成り立っていて、それぞれが違った認証機能を提供します。例えばモジュールの一つのRememberableは保存したクッキーにユーザの認証情報を記憶し、別のモジュールのRecoverableはユーザのパスワードリセットを世話したりリセット作業の指示を送信します。このアプローチによって、アプリケーションで使いたい認証パーツを簡単に選べるようになっています。
アプリケーションに認証機能を追加する
アプリケーションでdeviseを動かすために何が必要かを見ていきましょう。このスクリーンショットは簡単なプロジェクト管理用アプリケーションです。Rails 3.0で書かれており、deviseを使ってUserモデルと認証機能を追加していきます。
DeviseはRails 3で動作しますが、正しいバージョンを設定するために従わなくてはいけないインストール手順がいくつかあります。Rails 3アプリケーション用には、最新バージョン(現在は1.1.rc0)をインストールします。deviseをRails 2.3のアプリケーションで使用したい場合はバージョン1.0.6が必要です。これは、バージョン1.1がRails 2に対して下位互換性を持っていないからです。
Rails 3アプリケーションでは、Gemfileにdeviseの参照情報を追加して、正しいバージョンを特定します。
gem 'devise', '1.1.rc0'
準備ができたらbundleコマンドを実行して、gemとその依存関係をインストールします。
bundle install
次はインストール用のジェネレータを実行します。
$ rails generate 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. Setup default url options for your specific environment. Here is an example of development environment: config.action_mailer.default_url_options = { :host => 'localhost:3000' } This is a required Rails configuration. In production is must be 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" ===============================================================================
このコマンドによっていくつかのファイルが生成されます。まず初期設定ファイルと、deviseが表示するすべてのメッセージを含んだlocaleファイルです。その下には、手作業で行う2つの設定作業が記述されています。一つ目にアプリケーションのメーラにhostオプションを設定します。二つ目にルート(root)のルート(route)設定を持たなくてはいけません。アプリケーションにはrootのルートがすでにあるのでそれについては作業は不要ですが、メーラの設定は必要です。これについては、上記の手順の表示から行をコピーして、開発(development)モードの環境ファイルのブロック内に挿入します。
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
この行は、localhost
にhost
オプションを追加しています。アプリケーションを本番(production)モードで稼働するときには、対応するproduction.rb
ファイルのドメイン名にこの値を設定します。
deviseユーザモデルを作成する
認証処理を行うためにUser
モデルが必要になりますが、deviseはそのためのジェネレータ(generator)を提供します。これを使うことは必須ではないですが、利用すればdeviseの設定で何ステップかを省略できます。
$ rails generate devise User invoke active_record create app/models/user.rb invoke test_unit create test/unit/user_test.rb create test/fixtures/users.yml inject app/models/user.rb create db/migrate/20100412200407_devise_create_users.rb route devise_for :users
このジェネレータはいくつか興味深いものを作成します。モデルファイル、マイグレーション、 devise_for
ルートです。これらを順に見ていきましょう。
生成されたモデルファイルの中身は以下のとおりです。
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :lockable, :timeoutable and :activatable # :confirmable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation end
User
モデルは通常のActiveRecordモデルに非常に似ていますが、devise
メソッドの呼び出しが含まれています。これによって認証マジックが実現します。deviseメソッドは、引数としてアプリケーションでサポートするモジュールのリストをとります。先に説明した:rememberable
と:recoverable
が含まれています。このリストから簡単にモジュールを追加や削除することで、deviseの認証機能をアプリケーションのニーズに従ってカスタマイズできます。その目的で、ユーザが登録したことをEメールで確認しなくてもいいように:confirmable
を削除しました。
User
クラスにもattr_accessible
があり、ユーザが画面から修正できる属性をリストアップします。もしモデルにカスタムカラムがある場合は、ここで追加できます。
次に、生成されたマイグレーションファイルを見てみましょう。
class DeviseCreateUsers < ActiveRecord::Migration def self.up create_table(:users) do |t| t.database_authenticatable :null => false # t.confirmable t.recoverable t.rememberable t.trackable # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both t.timestamps end add_index :users, :email, :unique => true # add_index :users, :confirmation_token, :unique => true add_index :users, :reset_password_token, :unique => true # add_index :users, :unlock_token, :unique => true end def self.down drop_table :users end end
このファイルは簡単に理解することができるでしょう。テーブルに各モジュール用に必要なカラムを作成する別のメソッド呼び出しが含まれています。confirmable
モジュールは使用しないため、対応するメソッドをコメントアウトしました。合わせて、データベースのテーブルにカラムが存在しないので、confirmation tokenに関連するインデックスを削除します。
マイグレーションファイルを利用するモジュールに合わせて修正したら、データベースマイグレーションを実行します。
rake db:migrate
最後に、routeルートファイルに追加されたdevise_for
ルートが設定されました。rake routes
を実行すると、このコードが生成するルートが表示されます。
new_user_session GET /users/sign_in {:controller=>"devise/sessions", :action=>"new"} user_session POST /users/sign_in {:controller=>"devise/sessions", :action=>"create"} destroy_user_session GET /users/sign_out {:controller=>"devise/sessions", :action=>"destroy"} POST /users/password(.:format) {:controller=>"devise/passwords", :action=>"create"} user_password PUT /users/password(.:format) {:controller=>"devise/passwords", :action=>"update"} new_user_password GET /users/password/new(.:format) {:controller=>"devise/passwords", :action=>"new"} edit_user_password GET /users/password/edit(.:format) {:controller=>"devise/passwords", :action=>"edit"} POST /users(.:format) {:controller=>"devise/registrations", :action=>"create"} PUT /users(.:format) {:controller=>"devise/registrations", :action=>"update"} user_registration DELETE /users(.:format) {:controller=>"devise/registrations", :action=>"destroy"} new_user_registration GET /users/sign_up(.:format) {:controller=>"devise/registrations", :action=>"new"} edit_user_registration GET /users/edit(.:format) {:controller=>"devise/registrations", :action=>"edit"}
これは少し読みにくいですが、いくつかの認証のためのルート(ログイン、ログアウト、パスワードの再設定、新規ユーザの登録、プロフィールの再設定)が含まれています。これらのルートを変更したい場合はすべてカスタマイズ可能です。
これらのルートを介して認証機能にアクセスできるようになりました。/users/sign_up
にアクセスすると、新規ユーザ登録用のフォームが表示されます。
フォームに記入して「登録(Sign up)」ボタンをクリックすると、登録が完了してログインします。ログイン状態のときには/users/sign_out
にアクセスすることでログアウトできます。再度ログインするために/users/sign_in
にアクセスして、登録時に指定したユーザ名とパスワードをフォームに入力するとエラーが表示されます。
これはRails 3.0 beta 2の問題により発生するもので、deviseに特有のものではありません。このエラーが発生しても、幸い修正は簡単です。/config/initializers/cookie_verification_secret.rb
のファイルに、署名されたcookieを検証する秘密鍵を設定するためのコード行があります。
# Be sure to restart your server when you modify this file. # Your secret key for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid!# Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. Rails.application.config.cookie_secret = '3b161f7668f938d1aeb73e1137964f8d5ebaf32b9173c2130ecb73b95b610702b77370640dce7e76700fb228f35f7865ab2a5ccd22d00563504a2ea9c3d8dffe'
この行を/config/application.rb
に移動し、Rails.application
セクションをこのファイルから削除するだけです。
require File.expand_path('../boot', __FILE__) require 'rails/all' Bundler.require(:default, Rails.env) if defined?(Bundler) module ProjectManage class Application < Rails::Application config.filter_parameters << :password config.cookie_secret = '3b161f7668f938d1aeb73e1137964f8d5ebaf32b9173c2130ecb73b95b610702b77370640dce7e76700fb228f35f7865ab2a5ccd22d00563504a2ea9c3d8dffe' end end
この修正を有効化するためにサーバを再起動する必要がありますが、これで正しくログインできるようになりました。
正常動作する認証機能が設定できたので、ここから改良を加えていきます。ページの上部に、ログイン中にはログアウト、ログアウト後はログインのためのリンクを表示して、現在の状態がわかるようになっていたら便利でしょう。
アプリケーションのレイアウトファイルにこの修正を加えればすべてのページにリンクを表示できます。フラッシュメッセージを表するコードの直前に次のコード行を追加します。
<div id="user_nav"> <% if user_signed_in?%> Signed in as <%= current_user.email %>. Not you? <%= link_to "Sign out", destroy_user_session_path %> <% else %> <%= link_to "Sign up", new_user_registration_path %> or <%= link_to "Sign in", new_user_session_path %> <% end %> </div>
コードにはif
/else
構文があり、サイトを見ているユーザがログイン中かどうかによって、異なったメッセージを表示できるようにしています。これを判断するために、deviseが提供するuser_signed_in?
メソッドを呼び出します。現在のユーザがログイン中の場合はtrue
を返します。現在ログイン中のユーザが存在する場合、ログインに使用したメールアドレスとログアウト用のリンクを表示します。Eメールアドレスを表示するためには、current_user
を呼び出して現在のユーザのUser
オブジェクトを取得してEメール属性を表示します。ログアウト用リンクの正しいパスを得るために、rake routesを実行して前に作成したルートのリストを見直すことができます。ここにリストされたルートの中にdestroy_user_session
があり、/users/sign_out
にマップされているので、destroy_user_session_path
を用いてリンク用の正しいURLを設定します。
同じようにnew_user_registration_path
とnew_user_session_path
を用いて登録とログイン用のリンクを設定します。これらが正しく設定されたらページを再度読み込みます。するとページの上部にユーザの情報が表示されます。
「ログアウト」のリンクをクリックすると、代わりに「登録」と「ログイン」のリンクが表示されます。
ここまでで示した通り、deviseを使えばかなり容易に完全な認証機能を設定することができます。少しの設定で新規ユーザに登録をさせる機能とユーザがログインとログアウトを行う機能を持つことができました。他にもまだ触れていない部品があります。例えば、パスワードのリセットページです。もしconfirmableモデルを利用する設定にしていたら、deviseがフォームと関連ロジックを自動的に作っていたはずです。
これらのフォームが自動的に生成されるのは便利ですが、アプリケーションの外観に合わせるためにカスタマイズする必要が出てくるでしょう。幸いdeviseを使えば簡単に行うことができるので、次回のエピソードで紹介します。