【ruby on rails】acts_as_authenticatedで認証

acts_as_authenticatedを使ってユーザ登録を実装してみる。

参考:
http://eringi.com/weblog/archives/2007/07/acts_as_authenticated.html
http://todoer.com/staff/hoti/2007/07/31/acts_as_authenticated%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e3%81%bf%e3%82%8b/

以下のプラグインをインスコ
$ ruby script/plugin discover
$ ruby script/plugin install acts_as_authenticated
discoverってのが何者なのかよく分かってない。

modelとcontrollerの作成
$ ./script/generate authenticated user account

ユーザテーブルの作成
$ rake db:migrate

これだけで、サインアップ、ログイン、ログアウトは可能
http://hogehoge/account/signup
http://hogehoge/account/login
http://hogehoge/account/logout

メールを使って本人性確認するには
authenticated_mailerコントロールの作成
$ ./script/generate authenticated_mailer user

config/environment.rbに以下を追加

Rails::Initializer.run do |config|
  config.active_record.observers = :user_observer
end

sendmailを利用する設定をconfig/environments/development.rbに追加

ActionMailer::Base.delivery_method = :sendmail

ユーザテーブルにアカウントのアクティベーションに関するカラムを追加
$ ./script/generate migration AddUserColumn

data/migrate/xxx_add_user_column.rbを以下のように修正

class AddUserColumn < ActiveRecord::Migration
  def self.up
    add_column :users, :activation_code, :string, :limit => 40
    add_column :users, :activated_at, :datetime
  end

  def self.down
    remove_column :users, :activation_code
    remove_column :users, :activated_at
  end
end

マイグレートしてDBに反映
$ rake db:migrate

Userモデルを下記に変更

require 'digest/sha1'
class User < ActiveRecord::Base
  before_create :make_activation_code

  # Virtual attribute for the unencrypted password
  attr_accessor :password

  validates_presence_of     :login, :email
  validates_presence_of     :password,                   :if => :password_required?
  validates_presence_of     :password_confirmation,      :if => :password_required?
  validates_length_of       :password, :within => 4..40, :if => :password_required?
  validates_confirmation_of :password,                   :if => :password_required?
  validates_length_of       :login,    :within => 3..40
  validates_length_of       :email,    :within => 3..100
  validates_uniqueness_of   :login, :email, :case_sensitive => false
  before_save :encrypt_password

  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  def self.authenticate(login, password)
    # u = find_by_login(login) # need to get the salt
    u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login]
    u && u.authenticated?(password) ? u : nil
  end

  # Activates the user in the database.
  def activate
    @activated = true
    update_attributes(:activated_at => Time.now.utc, :activation_code => nil)
  end

  # Returns true if the user has just been activated.
  def recently_activated?
    @activated
  end

  # Encrypts some data with the salt.
  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def authenticated?(password)
    crypted_password == encrypt(password)
  end

  def remember_token?
    remember_token_expires_at && Time.now.utc < remember_token_expires_at 
  end

  # These create and unset the fields required for remembering users between browser closes
  def remember_me
    self.remember_token_expires_at = 2.weeks.from_now.utc
    self.remember_token            = encrypt("#{email}--#{remember_token_expires_at}")
    save(false)
  end

  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end

  protected
    # before filter 
    def encrypt_password
      return if password.blank?
      self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
      self.crypted_password = encrypt(password)
    end
    
    def password_required?
      crypted_password.blank? || !password.blank?
    end

    # If you're going to use activation, uncomment this too
    def make_activation_code
      self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end

end

accountコントローラを修正

class AccountController < ApplicationController
  # Be sure to include AuthenticationSystem in Application Controller instead
  include AuthenticatedSystem
  # If you want "remember me" functionality, add this before_filter to Application Controller
  before_filter :login_from_cookie

  # say something nice, you goof!  something sweet.
  def index
    redirect_to(:action => 'signup') unless logged_in? || User.count > 0
  end

  def login
    return unless request.post?
    self.current_user = User.authenticate(params[:login], params[:password])
    if logged_in?
      if params[:remember_me] == "1"
        self.current_user.remember_me
        cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
      end
      redirect_back_or_default(:controller => '/account', :action => 'index')
      flash[:notice] = "Logged in successfully"
    end
  end

  def signup
    @user = User.new(params[:user])
    return unless request.post?
    @user.save!
# activateする前にログインさせない。
#    self.current_user = @user
#    redirect_back_or_default(:controller => '/account', :action => 'index')
    redirect_to(:action => 'signup_notification')
    
    flash[:notice] = "Thanks for signing up!"
  rescue ActiveRecord::RecordInvalid
    render :action => 'signup'
  end

  # 有効化
  def activate
    @user = User.find_by_activation_code(params[:id])
    if @user and @user.activate
      self.current_user = @user
#      redirect_back_or_default(:controller => '/account', :action => 'index')
      flash[:notice] = "Your account has been activated."
    end
  end
  
  def logout
    self.current_user.forget_me if logged_in?
    cookies.delete :auth_token
    reset_session
    flash[:notice] = "You have been logged out."
    redirect_back_or_default(:controller => '/account', :action => 'index')
  end
end

ログインが必要なコントローラにbefore_filterを追加

class xxxxController < ApplicationController
  include AuthenticatedSystem

  before_filter :login_required
end

以上で完了

タグ:[ruby on rails

このログへのコメント(0件)

コメント投稿フォーム

コメント

※半角英数字だけのコメントは投稿できません。

トラックバック

関連ログ