更改用户表中的列时出现身份验证问题

发布于 2024-11-05 01:26:28 字数 3300 浏览 0 评论 0原文

我对 Rails 很陌生,我正在尝试解决以下身份验证问题:

用户发表评论或授予“赦免”(类似于评论),他会为此获得一些硬币。硬币是我的应用程序中的虚拟货币,也是用户表中的一列。

由于您的善意帮助,我已经能够在撰写评论或授予赦免后更新硬币价值。但是,当我写评论并退出后,我的登录名或密码就会更改(?)...我无法再使用此帐户登录。

这就是我的用户模型的样子:

require 'digest'
class User < ActiveRecord::Base

attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :twitter_url, :homepage_url, :coins

has_many :comments, :dependent => :destroy
has_many :absolutions, :dependent => :destroy

has_many :ratings
has_many :rated_sins, :through => :ratings, :source => :sins

email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
homepage_regex = /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix

validates :name,  :presence => true,
                  :length => { :maximum => 50 }
validates :email, :presence => true,
                  :format => { :with => email_regex },
                  :uniqueness => { :case_sensitive => false }
validates :twitter_url, :format => { :with => homepage_regex }
validates :homepage_url, :format => { :with => homepage_regex }
validates :password, :presence => true,
                     :confirmation => true,
                     :length => { :within => 6..40 }

before_save :encrypt_password

def has_password?(submitted_password)
  encrypted_password == encrypt(submitted_password)
end

def self.authenticate(email, submitted_password)
  user = find_by_email(email)
  return nil  if user.nil?
  return user if user.has_password?(submitted_password)
end

class << self
  def authenticate(email, submitted_password)
    user = find_by_email(email)
    (user && user.has_password?(submitted_password)) ? user : nil
  end

  def authenticate_with_salt(id, cookie_salt)
    user = find_by_id(id)
    (user && user.salt == cookie_salt) ? user : nil
  end
end

private

  def encrypt_password
    self.salt = make_salt if new_record?
    self.encrypted_password = encrypt(password)
  end

  def encrypt(string)
    secure_hash("#{salt}--#{string}")
  end

  def make_salt
    secure_hash("#{Time.now.utc}--#{password}")
  end

  def secure_hash(string)
    Digest::SHA2.hexdigest(string)
  end
end

这是我的评论控制器:

class CommentsController < ApplicationController

  before_filter :authenticate, :only => [:create, :destroy]

  def new
    @comment = Comment.new
  end

  def create
    @sin = Sin.find(params[:sin_id])
    @comment = current_user.comments.build(params[:comment])
    @comment.sin_id = @sin.id
    if @comment.save
      flash[:success] = "Comment created! Earned 20 coins."
      coins_new = current_user.coins.to_i + 20
      current_user.update_attribute(:coins, coins_new)
      redirect_to sin_path(@sin)
    else
      flash[:error] = "Comment should have 1 - 1000 chars."
      redirect_to sin_path(@sin)
    end
  end

  def destroy
  end



  private 

    def authenticate
      deny_access unless signed_in?
    end

end

我认为它与 before_save encrypt_password 方法有关,但这只是一个猜测。我非常感谢您的帮助和建议!

编辑: 天气变暖了......这与评论控制器中的以下行有关:

current_user.update_attribute(:coins, coins_new)

当他更新:硬币列时,似乎出了问题。如果您需要更多信息,请发表评论。感谢您的帮助!

I'm very new to rails and I'm trying to accomplish the following authentication issue:

User makes a comment or grants "absolution" (similar to comment) and he gets some coins for it. Coins is the virtual currency in my app and is also a column in the users table.

Because of your kind help, I was already capable to update the coins value after writing a comment or grant absolution. However, when I write a comment and log out after that, my login name or password gets changed(?)...I can't login anymore with this account.

This is how my User model looks like:

require 'digest'
class User < ActiveRecord::Base

attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :twitter_url, :homepage_url, :coins

has_many :comments, :dependent => :destroy
has_many :absolutions, :dependent => :destroy

has_many :ratings
has_many :rated_sins, :through => :ratings, :source => :sins

email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
homepage_regex = /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix

validates :name,  :presence => true,
                  :length => { :maximum => 50 }
validates :email, :presence => true,
                  :format => { :with => email_regex },
                  :uniqueness => { :case_sensitive => false }
validates :twitter_url, :format => { :with => homepage_regex }
validates :homepage_url, :format => { :with => homepage_regex }
validates :password, :presence => true,
                     :confirmation => true,
                     :length => { :within => 6..40 }

before_save :encrypt_password

def has_password?(submitted_password)
  encrypted_password == encrypt(submitted_password)
end

def self.authenticate(email, submitted_password)
  user = find_by_email(email)
  return nil  if user.nil?
  return user if user.has_password?(submitted_password)
end

class << self
  def authenticate(email, submitted_password)
    user = find_by_email(email)
    (user && user.has_password?(submitted_password)) ? user : nil
  end

  def authenticate_with_salt(id, cookie_salt)
    user = find_by_id(id)
    (user && user.salt == cookie_salt) ? user : nil
  end
end

private

  def encrypt_password
    self.salt = make_salt if new_record?
    self.encrypted_password = encrypt(password)
  end

  def encrypt(string)
    secure_hash("#{salt}--#{string}")
  end

  def make_salt
    secure_hash("#{Time.now.utc}--#{password}")
  end

  def secure_hash(string)
    Digest::SHA2.hexdigest(string)
  end
end

And this is my comments controller:

class CommentsController < ApplicationController

  before_filter :authenticate, :only => [:create, :destroy]

  def new
    @comment = Comment.new
  end

  def create
    @sin = Sin.find(params[:sin_id])
    @comment = current_user.comments.build(params[:comment])
    @comment.sin_id = @sin.id
    if @comment.save
      flash[:success] = "Comment created! Earned 20 coins."
      coins_new = current_user.coins.to_i + 20
      current_user.update_attribute(:coins, coins_new)
      redirect_to sin_path(@sin)
    else
      flash[:error] = "Comment should have 1 - 1000 chars."
      redirect_to sin_path(@sin)
    end
  end

  def destroy
  end



  private 

    def authenticate
      deny_access unless signed_in?
    end

end

I assume, that it has something to do with the before_save encrypt_password method, but its only a guess. I really appreciate your help and suggestions!

Edit:
It gets warmer...It has something to do with the following line in the Comments Controller:

current_user.update_attribute(:coins, coins_new)

When he updates the :coins column, something seems to go wrong. If you need further info, just drop a comment. Thanks for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

忆离笙 2024-11-12 01:26:28

您的问题是您正在“加密密码”方法中加密已经加密的密码。

因此,当用户是 new_record? 时,您将获取密码(假设它是“cat”),对其进行哈希处理,然后将其存储在数据库中。

因此,存储的是“cat”的加密哈希,我们称之为“dog”

然后,下次保存用户记录时,您将采用哈希密码(“dog”) #2 你的“加密密码”方法,并再次散列,我们会说生成“袋鼠”。

下次登录时,您的应用程序将对您在登录表单“cat”中输入的密码进行哈希处理,将其哈希为“dog”,并将其与数据库中的哈希版本“kangaroo”进行比较。哦,但是“dog”与“kangaroo”不匹配,所以登录失败。

因此更改:

def encrypt_password
  self.salt = make_salt if new_record?
  self.encrypted_password = encrypt(password)
end

为:

def encrypt_password
  self.salt = make_salt if new_record?
  self.password = decrypt(password) # decrypt it first to the plain text
  self.encrypted_password = encrypt(password) # then re-encrypt the plain text with the salt
end

或:

def encrypt_password
  self.salt = make_salt if new_record?
  if (password_has_changed?) # somehow you'll have to figure this out
    self.encrypted_password = encrypt(password)
end

Your problem is that you're encrypting the already encrypted password in your "encrypt_password" method.

So, when the user is a new_record?, you're taking the password (say it's "cat"), hashing it, and storing it in the database.

So, what gets stored is a cryptographic hash of "cat", which we'll say is "dog"

Then, the next time you're saving the user record, you're taking the hashed password ("dog") in line #2 of your "encrypt_password" method, and hashing it again, which we'll say generates "kangaroo".

Next time you log in, your app is hashing the password you enter into the login form "cat", hashing it to "dog", and comparing it to the hashed version in the database, "kangaroo". Oh, but "dog" doesn't match "kangaroo", so the login fails.

So change:

def encrypt_password
  self.salt = make_salt if new_record?
  self.encrypted_password = encrypt(password)
end

To either:

def encrypt_password
  self.salt = make_salt if new_record?
  self.password = decrypt(password) # decrypt it first to the plain text
  self.encrypted_password = encrypt(password) # then re-encrypt the plain text with the salt
end

Or:

def encrypt_password
  self.salt = make_salt if new_record?
  if (password_has_changed?) # somehow you'll have to figure this out
    self.encrypted_password = encrypt(password)
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文