1

アップデート

この投稿の最後に記載されている質問をさらに明確にしました。

問題の概要:

トークン認証を含む電子メールで送信された URL を介して、Devise で遅延 (別名ソフト サインアップ) 登録を実装しようとしています。私のサイトでは、User has_many :payments、およびPayment belongs_to :user. がUser新しい を作成すると、新しい未登録ユーザー (「ゲスト」と呼びます) を表すPayment属性が含まれます。:emailActionMailer を使用して、この新しいゲストにメールを送信します。

送信されるこの電子メールには、トークン認証を含む URL (例: ) を含めたいと考えていますhttp://localhost/index?auth_token=TOKENVALUE。これにより、ゲストは、認証が必要なビューを表示および編集でき、(ゲストの に基づいて:email) 特別にカスタマイズされます。ゲストは、として登録することもUserできます。私はすでにメール アドレスを持っているので、パスワードを入力するだけで済みます。

これまでの進捗状況:

  • Devise を使用して誰かがサイトに登録できる機能を実装し、関連付けられたビュー、モデル、およびコントローラーを作成して、Paymentモデルに変更を加えました。
  • 私は ActionMailer をセットアップし、それを使用して電子メールを送信していますが、上記のユースケースを考えると、その方法がわからないため、まだトークン認証をセットアップしていません

関連リソース:

/app/models/payment.rb

class Payment < ActiveRecord::Base
  attr_accessible :amount, :description, :email, :frequency, :paid, :user_id
  belongs_to :user

  validates :email, :presence => true, :format => { :with => /.+@.+\..+/i }
  validates :amount, :presence => true
  validates :description, :presence => true
end

/app/models/user.rb

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable

  # I will also need to add ":token_authenticatable"

  attr_accessible :email, :password, :password_confirmation, :remember_me
  has_many :payments
end

質問:

  1. テーブルで作成された新しいものがテーブルに自動的に追加されるように、テーブルを作成されたUser新しいものと同期させるにはどうすればよいですか?Payments:emailPaymentUser:email
  2. 質問 1 に続いてUser、ユーザーはまだサイトに登録していないため、新しく作成されたものにはトークンが含まれている必要がありますが、パスワードは含まれていません。User空白のパスワード、ランダムに生成された文字列、またはその他を使用して新しいパスワードを作成する必要がありますか? いずれの場合も、トークン認証された URL から登録ページにアクセスして、他のユーザーが自分のメール ユーザー名で登録できないようにする必要があると思います (遅延登録など)。
  3. 質問 2 に続いて、ユーザーのタイプによって一部のビューが変わるため、ゲストと通常のユーザーを区別する必要があると思います。2 つのユーザー タイプを区別するために 0 または 1 を持つ列を追加する以外に、推奨される方法はありますか?

含まれている多くの機能を使用しているため、可能であればDeviseを使用することを好みます。私は RoR を初めて使用するので、アドバイスをいただければ幸いです。

編集:これは、他の誰かに役立つ場合に備えて、支払いコントローラーで上記の質問#1に対処するために使用したコードです

def create
    @payment = current_user.payments.build(params[:payment])

    #Here is the code I added to keep User :email in sync with Payment :email, without token authentication implemented yet
    unless User.find_by_email(params[:payment][:email].downcase)  
      u = User.new({:email => params[:payment][:email].downcase, :password => nil, :password_confirmation => nil })
      u.skip_confirmation!
      u.save(:validate => false)  #skip validation
    end

    respond_to do |format|
      if @payment.save
        format.html { redirect_to payments_url, :flash => { notice: 'Payment was successfully created.' } }
        format.json { render json: @payment, status: :created, location: @payment }
      else
        format.html { render action: "new" }
        format.json { render json: @payment.errors, status: :unprocessable_entity }
      end
    end
  end
4

1 に答える 1

2

Devise のようなフレームワークは、それらをうまく使用する方法を知っていて、フレームワークが設計された目的の範囲内でやりたいことが収まる場合に最適です。しかし、私の経験では、内部でどのように機能するかを完全に理解せずにモンキーパッチを適用しようとすると、設計されていないことを実行するようにそれらを「曲げ」ようとすると、混乱するだけです。そこに行ったことがある。多くの場合、「機能するまでハッキングする」だけに還元されます...これは、「機能するように見えるまでハッキングすることを意味します。ただし、後でしか表示されない多数の微妙なバグは別として」。

このような場合、私は単に「独自の」ログインおよび認証コードを作成する傾向があり、おそらく Devise のソース コードを見てアイデアを探します。

(実際、私は今日、新しいRailsアプリでまさにそれを行いました... 私のプロジェクトが実際に必要とする200行のコードを書くのに、何度も何度もDeviseのソースをチェックしてアイデアを探し、数時間かかりました.Devise自体、比較すると、約 2500 行あります。)

コードの「全体的な戦略と構造」についてのアイデアを求めました。独自のカスタム ログイン/認証コードを実装する場合は、おそらく次のようになります。

  1. sessionユーザーがログインしているかどうか、およびどのゲスト/ユーザーとしてログインしているかを識別するために、いくつかの情報をハッシュに保存します。セッションが Cookie ではなく DB に保存されていることを確認してください。
  2. このログイン情報へのアクセスをカプセル化するには、いくつかのメソッドを に追加ApplicationController(または にModule混合) します。ApplicationController
  3. ログインbefore_filterが必要なすべてのページに を配置し、ユーザーがログインしていない場合にリダイレクトします。
  4. パスワードまたはトークンでユーザーを認証し、ログイン/ログアウトできるコントローラーを追加します。
  5. パスワードの保存/認証には、bcrypt-rubygem を使用します。password_hashUser モデルでは、文字列フィールドと、おそらく次のようないくつかのメソッドが必要になります。

    require 'bcrypt'
    def password
      # BCrypt handles generation and storage of salts
      @password ||= ::BCrypt::Password.new(password_hash)
    end
    def password=(password)
      @password = ::BCrypt::Password.create(password)
      self.password_hash = @password
    end
    def authenticate_by_password(password)
      self.password == password
    end
    
  6. トークンベースの認証の場合、login_token文字列フィールドをユーザー モデル (またはゲストなど) に追加します。ユニークなフィールドにします。次のようなものを使用して一意のトークンを生成できます (Devise から借用し、少し変更しました)。

    require 'securerandom'
    def generate_login_token
      loop do
        token = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
        unless User.where(:login_token => token).exists?
          self.login_token = token
          self.save!
          return
        end
      end
    end
    
  7. login次に、ユーザーが set でアクションをヒットしたらparams[:token]、次のようにします。

    if user = User.find_by_login_token(params[:token])
      # if the token is supposed to be "one time only", null out the attribute
      # log user in
    else
      # login token was invalid
    end
    

login_tokenフィールドに DB インデックスを配置してください。

他に何かありますか?

于 2013-06-01T19:36:00.787 に答える