ruby on rails爬坑(一):用戶賬號密碼管理

一,相關資料

慣例,先上文檔,BCrypt::Engine主要用於生成隨機字符串,hash值。

二,內容及思路

最近的項目需要引進用戶管理,主要功能有:
  • 註冊新用戶
  • 修改密碼
  • 登錄驗證

思路:
手稿,可能不容易理解,下面簡單說說

數據庫user表結構:
encrypted_password對應上圖中得hash_secret
整體流程:核心內容,看這個就夠了

數據庫裏存了encrypted_password,salt兩個關鍵的項。用戶註冊的時候,先隨機生成salt值,再根據用戶輸入的郵箱(或名字)與salt進行哈希運算,得到encrypted_password,存進數據庫裏。在用戶登錄的時候,根據用戶輸入的email(或名字)在數據庫中查詢,取得對應的user,將用戶輸入的password與數據庫裏的user.salt進行同樣的哈希運算,將得到的結果與數據庫中存儲的user.encrypted_password進行比較,如果相等,證明賬號密碼正確,允許登錄。成功登錄後,將數據庫中的user.name與user.salt進行哈希運算,得出user_token存成cookie[“user_token”],同時存儲cookie[“user_id”]。下次登錄時嘗試讀取該cookie,取得user_id,根據user_id獲取數據庫中對應的user,user.name與user.salt進行哈希運算,將得到的結果與cookie[“user_token”]比較,如果相同,則登錄成功。

三,用戶註冊

引用上文:用戶註冊的時候,先隨機生成salt值,再根據用戶輸入的郵箱(或名字)與salt進行哈希運算,得到encrypted_password,存進數據庫裏。 

不多說,看代碼

  def encrypt_password
    if password.present?
      #生成salt
      self.salt = BCrypt::Engine.generate_salt if self.salt.nil?
      #生成encrypted_password
      self.encrypted_password = BCrypt::Engine.hash_secret(password, self.salt)
    end
  end

關於BCrypt::Engine的具體用法,請查閱文檔。

四,修改密碼

與註冊用戶一樣,只是根據新的password與原來的salt重新生成encrypted_password。
    self.encrypted_password = BCrypt::Engine.hash_secret(new_password, self.salt)

五,登錄驗證

引用上文:在用戶登錄的時候,根據用戶輸入的email(或名字)在數據庫中查詢,取得對應的user,將用戶輸入的password與數據庫裏的user.salt進行同樣的哈希運算,將得到的結果與數據庫中存儲的user.encrypted_password進行比較,如果相等,證明賬號密碼正確,允許登錄。成功登錄後,將數據庫中的user.name與user.salt進行哈希運算,得出user_token存成cookie["user_token"],同時存儲cookie["user_id"]。下次登錄時嘗試讀取該cookie,取得user_id,根據user_id獲取數據庫中對應的user,user.name與user.salt進行哈希運算,將得到的結果與cookie["user_token"]比較,如果相同,則登錄成功。

情況一,首次登錄

登錄驗證:
  def authenticated?(password)
    #驗證密碼,返回boolen
    BCrypt::Engine.hash_secret(password, self.salt) == self.encrypted_password
  end
生成cookie:
  def gen_token
    #返回生成的user_token
    Digest::SHA1.hexdigest(self.email + self.salt)
  end

  #保存user_id
  cookies[:user] = { value:  user.id, expires: 20.years.from_now.utc }
  #保存user_token
  cookies[:user_token] = { value:  user.gen_token(), expires: 20.years.from_now.utc }

情況二,非首次,自動登錄

def valid_token?(token)
    gen_token() == token
end  
def gen_token
    Digest::SHA1.hexdigest(self.email + self.salt)
end
def login?
    if !cookies[:user].present? || !cookies[:user_token].present?
      return false
    else
      begin
        #從cookie中獲取user_id
        @current_user = User.find(cookies[:user])
      rescue
        @current_user = nil
        return false
      end
      #利用user_token進行登陸認證
      if !@current_user.valid_token?(cookies[:user_token])
        @current_user = nil
        return false
      end
    end
    return true
  end

六,rails

用戶驗證應該在程序開始的時候進行。在ApplicationController裏面進行用戶驗證技能滿足我們的需求。
class ApplicationController < ActionController::Base
  #設置爲before_action在程序運行前被調用
  before_action :require_login
  def require_login
    #根據是否登錄跳轉到不同頁面
    redirect_to :controller => 'login', :action => 'index' if !login?
  end

  #判斷以前是否登錄過
  def login?
    if !cookies[:user].present? || !cookies[:user_token].present?
      return false
    else
      begin
        @current_user = User.find(cookies[:user])
      rescue
        @current_user = nil
        return false
      end
      if !@current_user.valid_token?(cookies[:user_token])
        @current_user = nil
        return false
      end
    end
    return true
  end
end

七,一些思考

在cookie中爲什麼不存用戶的郵箱而存user_token?

從數據安全的角度想,即使是cookie也不應該以明文的方式存儲用戶的信息(郵箱)。
更新:其實上面的想法有點幼稚,因爲存郵箱根本不可行,在數據庫已有user表項的基礎上,郵箱與salt進行哈希運算得出的結果根本無法與其他的做比較,除非數據庫裏面又存了一個user_token用來作比較,這樣增加了數據庫的負擔,不是個好的選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章