ruby on rails實(shí)現(xiàn)登錄(記住密碼)

實(shí)現(xiàn)登錄功能
注冊(cè)模塊一般用users
登陸模塊一般用sessions

  1. 配置登錄模塊路由
get '/login', to: 'sessions#new'
post '/login', to:'sessions#create' 
delete '/logout', to: 'sessions#destroy’
  1. 準(zhǔn)備登錄表單頁(yè)面
  2. 編寫(xiě)控制器(sessions_controller.rb)
    控制器中的邏輯(sessions_controller.rb)
class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.find_by_email(params[:session][:email])
    if user && user.authenticate(params[:session][:password])
      sign_in(user)
      #判斷是否要記住密碼
      params[:session][:remember_me] == '1'? remember(user) : forget(user)
      redirect_to users_path
    else
      #flash.now 與flash不同,專門(mén)用于在重新渲染的(render)頁(yè)面中顯示閃現(xiàn)消息,會(huì)在下次請(qǐng)求時(shí)消失
      flush.now[:error]='賬號(hào)或密碼錯(cuò)誤'
      render 'new'
    end
  end
  
  def destroy
    sign_out
    redirect_to new_session_path
  end
end

4.編寫(xiě)用戶認(rèn)證需要的方法(sessions_helper.rb)

module SessionsHelper
#登錄操作
def sign_in(user)
   #這么做會(huì)在用戶的瀏覽器中創(chuàng)建一個(gè)臨時(shí) cookie,內(nèi)容是加密后的用戶 ID。瀏覽器關(guān)閉就會(huì)被清除。在后續(xù)的請(qǐng)求中,可以使用 session[:user_id] 取回這個(gè) ID
   session[:user_id] = user.id 
end

#記住用戶,持久性存儲(chǔ)登錄信息(存cookie)
#1.創(chuàng)建記憶令牌把未加密的存儲(chǔ)到cookie把記憶令牌加密更新到數(shù)據(jù)庫(kù)
#2.把用戶設(shè)置為當(dāng)前登錄用戶
def remember(user)
  remember_token = User.new_remember_token
  user.remember(remember_token)
  cookies.permanent[:remember_token] = remember_token #permanent自動(dòng)將過(guò)期時(shí)間設(shè)置為20年之后
  cookies.permanent.signed[:user_id] = user.id #signed設(shè)置存入瀏覽器前安全加密cookie中的用戶ID
  self.current_user = user
end

#忘記用戶,清空持久性登錄信息(清空cookie)
def forget(user)
  user.forget #清空用戶的記憶令牌
  cookies.delete(:remember_token)
  cookies.delete(:user_id)
end

#登錄成功存儲(chǔ)當(dāng)前登錄用戶信息
def current_user = (user)
  @current_user = user
end

#判斷是否登錄
#登錄用戶可能是從登錄頁(yè)面登錄進(jìn)來(lái),也有可能是記住密碼進(jìn)來(lái),所以如果@current_user沒(méi)值可以通過(guò)cookie獲取值
def sign_in?
 !current_user.nil?
end

#獲取當(dāng)前登錄用戶
def current_user
  if (user_id = session[:user_id])
    @current_user ||= User.find_by_id(user_id)
  else (user_id = cookies.signed(:user_id)) #解密cookie中的用戶ID
    user = User.find_by_id(user_id)
    #if user.remember_digest == User.encrypt(cookies[:remember_token])
    if user && user.authenticated?(cookies[:remember_token])
      sign_in user
      @current_user = user
    end
  end
end

#退出登錄
def sign_out
#忘記持久會(huì)話
  forget(current_user)
#忘記session
  session.delete(:user_id)
  @current_user = nil
end

end

5.編寫(xiě)model中的邏輯(user.model)

class User < ActiveRecord::Base
  #attr_accessor: remember_token
  #before_create :create_remember_token
  #生成記憶令牌(安全隨機(jī)數(shù)),返回A-Z a-z 0-9 -_    長(zhǎng)度為22的隨機(jī)字符串,每一位有64種可能
  def self.new_remember_token
    SecureRandom.urlsafe_base64
  end

  #加密算法
  #def encrypt(token)
    #Digest::SHA1.hexdigest(token.to_s)
  #end
  
  #返回指定字符串的哈希摘要(不可逆加密)
  def self.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                      BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  #更新users表記憶令牌密文
  def remember(remember_token)
    user.update_attribute(:remember_digest,User.digest(remember_token))
  end

  #如果指定的令牌和摘要匹配,返回 true否則返回false
  #remember_digest相當(dāng)于self.remember_digest
  #remember_token只是變量
  def authenticated?(remember_token)
    return false if remember_token.nil?
    BCrypt::Password.new(remember_digest).is_password?(remember_token) 
  end
    
  def forget
    self.update_attribute(:remember_token,nil)
  end
  private
  #創(chuàng)建記憶令牌
  #def create_remember_token
    #self.remember_digest = User.digest(User.new_remember_token)
  #end
end

關(guān)于為什么在記住密碼時(shí),已經(jīng)使用cookie.signed[:user_id] =user.id對(duì)cookie的用戶ID進(jìn)行加密了為什么還要使用記憶令牌的問(wèn)題

因?yàn)橐坏┇@取了加密的cookie攻擊者就可以冒充該用戶的身份進(jìn)行登錄,但是如果加了記憶令牌進(jìn)行多重驗(yàn)證,即使攻擊者獲取了用戶ID和記憶令牌進(jìn)行登錄,但是因?yàn)槊看蔚卿浂紩?huì)更改記憶令牌,退出會(huì)清空記憶令牌,所以冒充者最多只能維持登錄狀態(tài)到真正的用戶退出

authenticated?方法
在 User 模型中定義的 authenticated? 方法,比較摘要(加密令牌)和令牌。這個(gè)方法的作用類似于注冊(cè)模塊中 has_secure_password 提供的用來(lái)認(rèn)證用戶的 authenticate 方法

注:參考了很多資料,發(fā)現(xiàn)在實(shí)現(xiàn)存儲(chǔ)記憶令牌時(shí),都是選擇增加字段remember_digest存儲(chǔ)記憶令牌密文,并且定義一個(gè)虛擬屬性remember_token存儲(chǔ)生成的記憶令牌明文,但是我覺(jué)著沒(méi)必要新增一個(gè)虛擬屬性直接用變量代替了,所以在本例中只增加了一個(gè)字段remember_digest,(差異主要體現(xiàn)在helper模塊中的remember方法中,如果使用虛擬屬性還要在model中聲明attr_accessor: remember_token),如后續(xù)發(fā)現(xiàn)弊端會(huì)及時(shí)更新

參考:rails tutorial

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.用戶模型 2.注冊(cè) 3.登錄和退出 4.更新、顯示和刪除用戶 5.賬戶激活和密碼重設(shè) 6.用戶的微博 7.關(guān)注...
    Jayzen閱讀 732評(píng)論 0 0
  • 構(gòu)建用戶管理微服務(wù)翻譯自:https://springuni.com 構(gòu)建用戶管理微服務(wù)(一):定義領(lǐng)域模型和 R...
    極樂(lè)君閱讀 1,670評(píng)論 0 10
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • ——觀 《一八九四 .甲午大海戰(zhàn)》 平靜的、蔚藍(lán)的、一望無(wú)垠的大海,它是那樣安詳,那樣廣闊...
    Fwx煙雨傾城閱讀 90評(píng)論 5 3
  • 民23:19神非人,必不至說(shuō)謊;也非人子,必不至后悔。他說(shuō)話豈不照著行呢?他發(fā)言豈不要成就呢?
    寧寧2018閱讀 242評(píng)論 0 0

友情鏈接更多精彩內(nèi)容